本文主要分析Visual Studio Samples\1033\C++\MFC\Visual C++ 2008 Feature Pack\MSMoneyDemo這個Sample
一般窗口的標(biāo)題欄上面都是只有固定的最小化,恢復(fù),最大化按鈕,這些按鈕的大小,圖標(biāo)都是系統(tǒng)自定義的,
本文分析VS2008 sp1 的事例代碼實現(xiàn)自己的標(biāo)題欄。
CMSMCaptionBar實現(xiàn)了,自定義的標(biāo)題欄窗口,類定義如下。
class CMSMCaptionBar : public CPane
{
DECLARE_DYNCREATE(CMSMCaptionBar)
// Construction
public:
CMSMCaptionBar ();
virtual ~CMSMCaptionBar ();
virtual void SetIcon (HICON hIcon);
void SetCaptionHeight (int nHeight);
int GetCaptionHeight () const;
void SetCaptionFont (const LOGFONT& lf);
HFONT GetCaptionFont () const;
virtual COLORREF GetCaptionTextColor () const;
void SetParentActive (BOOL bParentActive = true);
BOOL IsParentActive () const;
void SetParentMaximize (BOOL bParentMaximize = true);
BOOL IsParentMaximize () const;
// Attributes
public:
// Operations
public:
// Overrides
public:
virtual BOOL Create(CWnd* pParentWnd, UINT nID = uiCaprionBarID);
virtual BOOL CreateEx(CWnd* pParentWnd, UINT nID = uiCaprionBarID);
virtual CSize CalcFixedLayout(BOOL, BOOL);
protected:
virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
virtual void DoPaint(CDC* pDCPaint);
virtual BOOL PreTranslateMessage(MSG* pMsg);
virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam);
protected:
afx_msg LRESULT OnSetText(WPARAM, LPARAM lParam);
afx_msg void OnSize(UINT nType, int cx, int cy);
afx_msg void OnContextMenu(CWnd* pWnd, CPoint point);
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
DECLARE_MESSAGE_MAP()
virtual UINT HitTest (const CPoint& pt) const;
virtual void ShowSysMenu (const CPoint& point);
public:
CString m_strCaption;
HICON m_hIcon;
CSize m_szIcon;
BOOL m_bParentActive;
BOOL m_bParentMaximize;
int m_SystemHeight;
int m_CaptionHeight;
CFont m_CaptionFont;
CMSMCaptionBarButton m_BtnMinimize;
CMSMCaptionBarButton m_BtnMaximize;
CMSMCaptionBarButton m_BtnClose;
};
下面是類的派生關(guān)系圖:
CObject
CCmdTarget
CWnd
CBasePane
CPane
CMSMCaptionBar
從這個圖上可以看出:CMSMCaptionBar也是一個窗口,(從CWnd類派生的都是一個窗口WIndow)。
2 如何設(shè)計設(shè)計一個Caption Window
一個窗口主要由UI部分以及消息響應(yīng)部分組成。
UI部分通俗的說就是窗口的外觀描述,通過外觀描述可以繪制出窗口。
消息響應(yīng)部分就是該窗口會處理哪些消息。比如雙擊時最大化還是恢復(fù),點擊上面的關(guān)閉按鈕,程序關(guān)閉等等,下面會詳細描述。
2.1 Caption Window的組成
1)元素組成:標(biāo)題圖標(biāo),窗口標(biāo)題,最小化按鈕,恢復(fù)按鈕,最大化按鈕。
描述這些元素需要相應(yīng)的成員變量,也就是CMSMCaptionBar的public成員:
CString m_strCaption;//窗口標(biāo)題
CFont m_CaptionFont;//標(biāo)題字體
HICON m_hIcon;//標(biāo)題圖標(biāo)
CSize m_szIcon;//圖標(biāo)大小
//最小化按鈕,恢復(fù)按鈕,最大化按鈕。
CMSMCaptionBarButton m_BtnMinimize;
CMSMCaptionBarButton m_BtnMaximize;
CMSMCaptionBarButton m_BtnClose;
對于Caption Window,還有自己的一些屬性,比如窗口高度。
int m_SystemHeight;
int m_CaptionHeight;//標(biāo)題欄高度
2)如何繪制元素
為了保證窗口接收雙擊事件,需要組成S_DBLCLKS風(fēng)格的窗口。
LPCTSTR lpszClass = AfxRegisterWndClass(CS_DBLCLKS, ::LoadCursor(NULL, IDC_ARROW),
(HBRUSH)(COLOR_BTNFACE+1), NULL);
Create->CreateEx
創(chuàng)建窗口
CWnd::Create(lpszClass, NULL, dwStyle | WS_CLIPSIBLINGS, rect, pParentWnd, nID)
創(chuàng)建好窗口之后還必須加入到窗口的一個Pane的列表中去
if (pParentWnd->IsKindOf (RUNTIME_CLASS (CFrameWndEx)))
{
((CFrameWndEx*) pParentWnd)->AddPane (this);
}
加載Capation Icon,設(shè)置Caption 標(biāo)題
SetIcon (hIcon);
SetWindowText (strCaption);
創(chuàng)建最小化按鈕,恢復(fù)按鈕,最大化按鈕
m_BtnClose.Create (_T(""), BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE,
rt, this, SC_CLOSE);
m_BtnClose.SetTooltip (_T("Close"));
m_BtnMaximize.Create (_T(""), BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE,
rt, this, SC_MAXIMIZE);
m_BtnMaximize.SetTooltip (_T("Maximize"));
m_BtnMinimize.Create (_T(""), BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE,
rt, this, SC_MINIMIZE);
m_BtnMinimize.SetTooltip (_T("Minimize"));
對于按鈕上圖片的設(shè)置與加載是在CMSMVisualManager中設(shè)置的,這是在VS2008 Sp1中有的可以設(shè)置多種UI主題風(fēng)格,類似于換皮膚。
下面列出了與Caption Window相關(guān)的函數(shù)。
class CMSMVisualManager : public CMFCVisualManagerOffice2003
{
BOOL CMSMVisualManager::LoadMSMCaptionButtonsIcons (LPCTSTR lpszID);
virtual void MSMDrawCaptionButton (CDC* pDC, CRect rect, AFX_BUTTON_STATE state, UINT id);
BOOL LoadMSMCaptionButtonsIcons (LPCTSTR lpszID);
const CSize& GetMSMCaptionButtonsSize () const;
virtual void OnFillBarBackground (CDC* pDC, CBasePane* pBar,
CRect rectClient, CRect rectClip, BOOL bNCArea = FALSE);
CImageList m_CaptionButtonIconst;
CSize m_CaptionButtonSize;
}
通過這些函數(shù)可以設(shè)置Button的圖片以及Caption Windowd的背景。
通過上面就把窗口UI元素畫好了,下面介紹消息的處理
3)Caption Windows處理的消息又哪些?
鼠標(biāo)左鍵單擊(需要判斷是最小化,最大化,恢復(fù),還是關(guān)閉);
鼠標(biāo)右鍵單擊(彈出幫助菜單)
鼠標(biāo)左鍵雙擊(最大化或者恢復(fù))
WM_SIZE消息,當(dāng)窗口大小變化時,需要移動最小化,最大化,恢復(fù)按鈕的位置。
也就是下面的一些消息。
ON_MESSAGE(WM_SETTEXT, OnSetText)
ON_WM_SIZE()
ON_WM_CONTEXTMENU()
ON_WM_SYSCOMMAND()
4)對于按鈕消息的處理WM_XXX應(yīng)當(dāng)轉(zhuǎn)化成WM_NCXXX,也就是說客戶端消息要轉(zhuǎn)化成非客戶端消息。因為對于單文檔程序來說,
標(biāo)題欄屬于非客戶區(qū)。
BOOL CMSMCaptionBar::PreTranslateMessage (MSG* pMsg)
在其中調(diào)用HitTest函數(shù)判斷鼠標(biāo)的位置,如果是在Caption window中點擊,則
判斷uiHit = HTCAPTION;還是uiHit = HTSYSMENU;
再
switch (pMsg->message)
{
case WM_LBUTTONDOWN:
message = WM_NCLBUTTONDOWN;
break;
case WM_LBUTTONUP:
message = WM_NCLBUTTONUP;
break;
case WM_LBUTTONDBLCLK:
message = WM_NCLBUTTONDBLCLK;
break;
}
if (message != 0)
{
if (message == WM_NCLBUTTONDOWN && uiHit == HTSYSMENU)
{
CRect rt;
GetWindowRect (rt);
ShowSysMenu (CPoint (rt.left, rt.bottom));
}
else
{
pParentWnd->SendMessage (message, wParam, lParam);
}
}
對于Caption Window不感興趣的消息會發(fā)送給父窗口處理。
5)WM_SIZE消息的處理
對WM_SIZE的消息處理就是移動最下化,最大化,以及恢復(fù)按鈕。
3)使用Caption Window
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
設(shè)置風(fēng)格,去掉WS_CAPTION | FWS_ADDTOTITLE,即創(chuàng)建不帶Caption的Mainframe,
因為Caption是我們自己要創(chuàng)建的,而不是自動創(chuàng)建
ModifyStyle (WS_CAPTION | FWS_ADDTOTITLE, 0);
去掉窗口的邊框
ModifyStyleEx (WS_EX_CLIENTEDGE, 0);
設(shè)置窗口風(fēng)格
CMFCVisualManager::SetDefaultManager (RUNTIME_CLASS (CMSMVisualManager));