如果我们要将一个控件转换成视图类,我们一般会想到CCtrlView,用它实现的控件视图一般添加一个GetXXXCtrl函数,函数的作用是返回视图中控件的引用,如果在MFC程序中跟踪它的调用我们会发现它的实现是这样的(以CEdit控件为例) _AFXEXT_INLINE CEdit& CEditView::GetEditCtrl() const 这个转换让人觉得很疑惑,因为CEdit和CEditView是来自两条不同继承链上的类,一般来说这样的转换是要出问题的,但是为什么可以进行这种转换?MSDN上有一篇文章对这种转换做了比较深入的解释,下面把这篇文章翻出来给大家看看。 问题: Mateo Anderson(回答者) 回答: CTreeView::CTreeView() : 在这个例子中,WC_TREEVIEW(在commctrl.h中定义)是树型控件类的类名,也就是“SysTreeView32”。CCtrlView会将此类名保存在一个数据成员中稍后使用: CCtrlView::CCtrlView(LPCTSTR lpszClass, 下一个起作用的函数是PreCreateWindows,这是一个CTreeCtrl从CCtrlView继承来的函数。CCtrlView::PreCreateWindow在窗口刚好创建完成之前使用m_strClass在CREATESTRUCT种设置类名。 // CCtrlView uses stored class name 现在创建的窗口就是使用所期望的类创建的了-在这个例子中就是SysTreeView32。到目前为止,一切都很好。但是如果CTreeCtrl派生于CCtrlView,而CCtrlView又派生于CView,那么它为什么能又派生于CTreeCtrl?难道是MFC类封装了树型控件?CTreeView和CTreeCtrl是完全独立的,有着不同的继承链。CTreeCtrl直接派生于CWnd,而CTreeView派生于CCtrlView/CView。这就是技巧发生作用的地方了。要让树型视图像树型控件一样操控,CCtreeView提供了特殊的函数GetTreeCtrl来获得树型控件。 CTreeCtrl& CTreeView::GetTreeCtrl() const GetTreeCtrl只是简单的将CtreeView转换为CTreeCtrl。但是等等-怎么会发生这么诡异的事?这两个类完全不同,有着不同的数据成员和虚函数表-你根本无法将一个转换成另一个同时还指望着能够正常工作!答案就是:CTreeCtrl没有虚函数也没有数据成员。你可以把它称为一个纯包装类。CTreeCtrl不对它的基类CWnd添加任何东西(不添加数据成员也不添加虚函数),所有添加的内容只是一些包装函数,一些将消息发送给内在HWND的有形函数。 HTREEITEM CTreeCtrl::InsertItem(...) InsertItem访问的唯一数据成员是m_hWnd,所有的CWnd派生类都有该成员。InsertItem和所有其它的包装函数只是将它们的参数传递给了内在的HWND,将C++风格的成员函数转换成Windows风格的SendMessage调用。对象本身(“this”指针)可以是任何CWnd派生类的实例,只要m_hWnd处于正确的位置(也即是类的第一个数据成员),并且HWND(实际上就是如此)是一个树型控件的句柄。相同的原因发生在下面的写法中: 即便在这里GetDlgItem返回的是一个指向CWnd的指针,不是CEdit。但是这样是允许的,因为CEdit也是一个包装类,而且对于它从CWnd继承来的内容没有添加额外的数据和虚函数。所以注释中“CCtrlView几乎允许将任何控件转换为视图”的说法中所说的“几乎任何”意思是特指对CWnd没有添加数据成员和虚函数的这些控件,也就是我所说的“纯包装类。”如果你的控件类有它自己的数据或虚函数,你无法使用CCtrlView,因为在CCtrlView/CView中不存在这些额外的数据成员和虚函数。 举例来说,CView中的第一个虚函数是CView::IsSelected。如果你的控件类有其它虚函数,在你把CCtrlView类转换成你的CFooCtrl类调用那个虚函数的时候就肯定会爆发问题了,因为这个函数根本不存在。类似的,CView中的第一个数据成员是m_pDocument。如果你的控件类需要一些其它的数据成员,那你在访问它们的时候就要倒霉了,因为对象事实上调用的是CCtrlView,而不是CFooCtrl。多么糟糕,多么令人沮丧。
|
|