摘要:本文將介紹如何在可停靠窗口(Dockable Pane)中使用對話框來來實現可視化設計,即將一個對話框(Dialog)做爲子窗口填充在可停靠窗格之中,這樣作的好處是使得能夠經過Visual Studio的對話框資源編輯功能可視化地設計窗口,並輕鬆地實現控件的消息處理程序。框架
關鍵字:Dockable Pane, Dialog, 可視化設計ide
1、使用可停靠窗格開發用戶界面函數
不少程序中都使用可停靠窗格做爲用戶界面中的重要組成部分,最熟悉的例子莫過於使用最多的Visual Studio。從VS2008 SP1和VS2010開始,新版的MFC框架中已經提供了一系列的類實現這樣的功能,其中最值得關注的是CDockablePane類。this
CDockablePane類表明了一個可停靠窗格,其用法能夠經過閱讀Visual Studio的「MFC應用程序嚮導」自動生成的源代碼來習得。建立一個可停靠窗格的步驟大體分爲五步:設計
(1) 定義一個類繼承自CDockablePane以實現特定的功能。code
(2) 在CMainFrame中定義上述類型成員變量。orm
(3) 在CMainFrame的OnCreate函數中調用CDockablePane的Create函數建立窗格。對象
(4) 調用CDockablePane的EnableDocking函數配置可停靠位置。blog
(5) 調用CMainFrame的DockPane函數停靠此窗格。繼承
詳情可參考Visual Studio生成的代碼,此處再也不贅述。
美中不足的是,Visual Studio並未提供對可停靠窗格進行可視化設計的支持。不過,若是須要,能夠經過對話框的功能來間接實現。
2、設計思路
依照使用方便、代碼可重用的設計原則,有如下幾個方面須要考慮:
(1) 應當提供用戶一個類來繼承使用(CDockableForm),爲了設計在對話框編輯視圖中添加控件關聯變量和事件處理程序,CDockableForm類應該是CDialog類的子類。
(2) 用戶建立此類的對象時,CDockablePane類的對象也應該隨之建立。該類提供一個Create函數,在該函數中應該同時完成可停靠窗格和對話框的建立過程。
(3) 對話框應該鋪滿可停靠窗格,並隨可停靠窗格隱藏而隱藏、顯示而顯示。這須要將對話框設置爲可停靠窗格的子窗口,並添加一個類(CDockablePaneAsContainer)繼承自CDockablePane,在其WM_SIZE消息處理函數中調整對話框的位置。
(4) 在可停靠窗格銷燬時,應該同時銷燬對話框。能夠可停靠窗格的WM_DESTROY消息處理函數中銷燬對話框。
(5) CDockablePane類應該提供一個成員函數使得用戶能夠訪問其CDockablePane成員,以實現其在主框架窗口(CMainFrame)中的停靠功能。
依上所述,設計的類如圖所示。
圖2-1 類圖
CDockableForm類和CDockablePaneAsContainter類的實現代碼見附錄1-4。
使用方法分爲四步:
(1) 建立、編輯對話框資源,添加對話框類,基類選擇CDialog類。
(2) 在項目中添加DockableForm.h和DockableForm.cpp(見附錄5),在對話框類的頭文件中包含DockableForm.h,將對話框類的基類改成CDockableForm,將對話框類的頭文件和源代碼文件中全部CDialog替換爲CDockableForm,同時也要修改一個對話框類的構造函數,由於CDockableForm類的構造函數與CDialog類的不同。
(3) 在CMainFrame類中添加第二步生成的類的成員,在其OnCreate函數中調用該成員的Create函數建立可停靠窗格,調用GetDockablePane方法獲得其可停靠窗格的引用以實現停靠功能。
使用示例見附錄5。
附錄
1. CDockableForm類的聲明代碼
class CDockablePaneAsContainer : public CDockablePane { public: CDockablePaneAsContainer(CDialog* pDialog) : m_pDialog(pDialog) { } private: CDialog* m_pDialog; public: DECLARE_MESSAGE_MAP() afx_msg void OnSize(UINT nType, int cx, int cy); afx_msg void OnDestroy(); };
2. CDockableForm的實現代碼。
BEGIN_MESSAGE_MAP(CDockablePaneAsContainer, CDockablePane) ON_WM_SIZE() ON_WM_DESTROY() END_MESSAGE_MAP() void CDockablePaneAsContainer::OnSize(UINT nType, int cx, int cy) { CDockablePane::OnSize(nType, cx, cy); // TODO: 在此處添加消息處理程序代碼 if (m_pDialog->GetSafeHwnd()) { CRect rc; GetClientRect(rc); m_pDialog->MoveWindow(rc); } } void CDockablePaneAsContainer::OnDestroy() { CDockablePane::OnDestroy(); // TODO: 在此處添加消息處理程序代碼 m_pDialog->DestroyWindow(); }
3. CDockableForm類的聲明代碼
class CDockableForm : public CDialog { public: CDockableForm(UINT nIDTemplate); virtual BOOL Create( LPCTSTR lpszCaption, CWnd* pParentWnd, const RECT& rect, BOOL bHasGripper, UINT nID, DWORD dwStyle, DWORD dwTabbedStyle = AFX_CBRS_REGULAR_TABS, DWORD dwControlBarStyle = AFX_DEFAULT_DOCKING_PANE_STYLE, CCreateContext* pContext = NULL); CDockablePane& GetDockablePane() { return m_wndPane; } private: CDockablePaneAsContainer m_wndPane; };
4. CDockablePaneAsContainer的實現代碼
CDockableForm::CDockableForm(UINT nIDTemplate) : CDialog(nIDTemplate, &m_wndPane) , m_wndPane(this) { } BOOL CDockableForm::Create(LPCTSTR lpszCaption, CWnd *pParentWnd, const RECT &rect, BOOL bHasGripper, UINT nID, DWORD dwStyle, DWORD dwTabbedStyle, DWORD dwControlBarStyle, CCreateContext *pContext) { m_wndPane.Create(lpszCaption, pParentWnd, rect, bHasGripper, nID, dwStyle, dwTabbedStyle, dwControlBarStyle, pContext); CDialog::Create(m_nIDHelp, &m_wndPane); SetParent(&m_wndPane); ShowWindow(SW_SHOW); return TRUE; }
5. 示例代碼