來源: http://blog.csdn.net/xbwee/article/details/3592050
作者: xbwee
內容:
WTL中支持 MDI 的接口类是 CMDIWindow ,继承自 ATL::CWindow ,因此两个特殊的数据成员是
HWND m_hWndClient 继承自Cwindow 当前窗口客户区窗口句柄。
HWND m_hWndMDIClient MDI客户区窗口句柄,窗口类别为“MDICLIENT”。
它们的关系比较容易混淆···
下面我们来区分一下:
凡是谈到Windows程序设计必先从P先生的《Windows Programming》谈起。P先生提到,MDI组成:主窗口(应用程序框架窗口)、MDI客户窗口(据窗口类别“MDICLIENT”建立)、子窗口(文件窗口)。依次为父子关系。windows操作系统对 MDI 的支持包括一个窗口类别(“MDICLIENT”)、五个函数、两个数据结构和12个消息。
五个函数主要用于对窗口过程的处理:DefFrameProc,DefMDIChildProc,TranslateMDISysAccel,ArrangeIconicWindows,这些函数的作用正如它们的名称一样简单明了。第五个MDI函数是CreateMDIWindow,它使得子窗口可以在单独的线程中被建立。这些函数由主窗口调用。
其中12个消息尤其重要。命名为“WM_MDIXXX” 都以“WM_MDI”开头,它们是:
创建、销毁子窗口 WM_MDICREATE、WM_MDIDESTROY
最大化、还原子窗口 WM_MDIMAXIMIZE、WM_MDIRESTORE
层叠、并列子窗口 WM_MDICASCADE、WM_MDITILE
遍历子窗口 WM_MDIGETACTIVE、WM_MDINEXT
设置、刷新MDI菜单 WM_MDISETMENU、WM_REFRESHMENU
排列最小化子窗口图标 WM_MDIICONARRANGE
激活子窗口 WM_MDIACTIVATE
主窗口就是依靠给 MDI 客户窗口发送这12个消息来对文件子窗口进行管理的。具体 MDI 客户窗口如何响应这些消息,那是操作系统的事。一旦主窗口调用创建了窗口类别为“MDICLIENT”的 MDI 客户窗口,拥有其窗口句柄,那么主窗口就可以向其发送12个消息中的相应消息来获得对文件子窗口的控制。这些控制由 MDI 客户窗口负责实施。仅此而已···
现在回到 WTL,在框架窗口定义头文件 atlframe.h 中有
class CMDIWindow : public ATL::Cwindow
{
public:
// Data members
HWND m_hWndMDIClient; //MDI客户窗口句柄,负责管理文件子窗口。调用CMDIFrameWindowImpl::
//CreateMDIClient()创建。
HMENU m_hMenu; //MDI子窗口菜单。
······
成员函数,封装了MDI的后11个消息操作,并提供3个额外的操作支持。
}
主窗口和子窗口都继承自 CMDIWindow 因此拥有 m_hWndMDIClient成员,主窗口的成员函数 CreateMDIClient() 中创建了窗口类为“MDICLIENT”的 MDI 客户窗口并使得 m_hWndMDIClient 保存了 MDI 客户窗口句柄。此成员函数在主框架窗口 WM_CREATE 消息映射函数中调用。子窗口成员函数Create()/CreateEx() 中把其父窗口句柄保存在 m_hWndMDIClient 中。在主窗口对 ID_FILE_NEW 和 ID_FILE_OPEN 的命令消息映射处理时创建了子窗口,而传递给它的父窗口句柄是主窗口的 m_hWndClient 也就是 m_WndMDIClient 即 MDI 客户窗口句柄。由此可见同一个 MDI 应用程序的主窗口和子窗口的 m_hWndMDIClient 成员保存的是同一个窗口句柄,即 MDI 客户窗口句柄。同理 m_hMenu 亦是同一个菜单句柄:
template <class T, class TBase = CMDIWindow, class TWinTraits = ATL::CFrameWinTraits> class ATL_NO_VTABLE CMDIFrameWindowImpl : public CFrameWindowImplBase<TBase, TWinTraits > { ..... LRESULT DefWindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam) { return ::DefFrameProc(m_hWnd, m_hWndMDIClient, uMsg, wParam, lParam); } HWND CreateMDIClient(HMENU hWindowMenu = NULL, UINT nID = ATL_IDW_CLIENT, UINT nFirstChildID = ATL_IDM_FIRST_MDICHILD) { ..... // Create MDICLIENT window m_hWndClient = ::CreateWindowEx(dwExStyle, _T("MDIClient"), NULL, dwStyle, 0, 0, 1, 1, m_hWnd, (HMENU)LongToHandle(nID), ModuleHelper::GetModuleInstance(), (LPVOID)&ccs); // set as MDI client window m_hWndMDIClient = m_hWndClient; // update to proper size T* pT = static_cast<T*>(this); pT->UpdateLayout(); return m_hWndClient; } ..... } template <class T, class TBase = CMDIWindow, class TWinTraits = ATL::CMDIChildWinTraits> class ATL_NO_VTABLE CMDIChildWindowImpl : public CFrameWindowImplBase<TBase, TWinTraits > { public: HWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL, DWORD dwStyle = 0, DWORD dwExStyle = 0, UINT nMenuID = 0, LPVOID lpCreateParam = NULL) { ..... m_pfnSuperWindowProc = ::DefMDIChildProc; m_hWndMDIClient = hWndParent; ..... } ..... }
总结:
"MDICLIENT" 是操作系统默认注册的窗口类别,MDI应用程序的关键就是创建一个此类别的客户窗口填满主窗口的客户区,其作用仅仅是维护以其为父窗口创建的子窗口的排列消息响应等等事件。