在可停靠窗格中使用對話方塊來實現視覺化設計
摘要:本文將介紹如何在可停靠視窗(Dockable Pane)中使用對話方塊來來實現視覺化設計,即將一個對話方塊(Dialog)作為子視窗填充在可停靠窗格之中,這樣做的好處是使得可以通過Visual Studio的對話方塊資源編輯功能視覺化地設計視窗,並輕鬆地實現控制元件的訊息處理程式。
關鍵字:Dockable Pane, Dialog, 視覺化設計
一、使用可停靠窗格開發使用者介面
很多程式中都使用可停靠窗格作為使用者介面中的重要組成部分,最熟悉的例子莫過於使用最多的Visual Studio。從VS2008 SP1和VS2010開始,新版的MFC框架中已經提供了一系列的類實現這樣的功能,其中最值得關注的是CDockablePane類。
CDockablePane類代表了一個可停靠窗格,其用法可以通過閱讀Visual Studio的“MFC應用程式嚮導”自動生成的原始碼來習得。建立一個可停靠窗格的步驟大致分為五步:
(1) 定義一個類繼承自CDockablePane以實現特定的功能。
(2) 在CMainFrame中定義上述型別成員變數。
(3) 在CMainFrame的OnCreate函式中呼叫CDockablePane的Create函式建立窗格。
(4) 呼叫CDockablePane的EnableDocking函式配置可停靠位置。
(5) 呼叫CMainFrame的DockPane函式停靠此窗格。
詳情可參考Visual Studio生成的程式碼,此處不再贅述。
美中不足的是,Visual Studio並未提供對可停靠窗格進行視覺化設計的支援。不過,如果需要,可以通過對話方塊的功能來間接實現。
二、設計思路
依照使用方便、程式碼可重用的設計原則,有以下幾個方面需要考慮:
(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. 示例程式碼下載地址: 下載地址