本文主要分析Visual Studio Samples\1033\C++\MFC\Visual C++ 2008 Feature Pack\MSMoneyDemo这个Sample
一般窗口的标题栏上面都是只有固定的最小化,恢复,最大化按钮,这些按钮的大小,图标都是系统自定义的,
本文分析VS2008 sp1 的事例代码实现自己的标题栏。
CMSMCaptionBar实现了,自定义的标题栏窗口,类定义如下。
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;
};
下面是类的派生关系图:
CObject
CCmdTarget
CWnd
CBasePane
CPane
CMSMCaptionBar
从这个图上可以看出:CMSMCaptionBar也是一个窗口,(从CWnd类派生的都是一个窗口WIndow)。
2 如何设计设计一个Caption Window
一个窗口主要由UI部分以及消息响应部分组成。
UI部分通俗的说就是窗口的外观描述,通过外观描述可以绘制出窗口。
消息响应部分就是该窗口会处理哪些消息。比如双击时最大化还是恢复,点击上面的关闭按钮,程序关闭等等,下面会详细描述。
2.1 Caption Window的组成
1)元素组成:标题图标,窗口标题,最小化按钮,恢复按钮,最大化按钮。
描述这些元素需要相应的成员变量,也就是CMSMCaptionBar的public成员:
CString m_strCaption;//窗口标题
CFont m_CaptionFont;//标题字体
HICON m_hIcon;//标题图标
CSize m_szIcon;//图标大小
//最小化按钮,恢复按钮,最大化按钮。
CMSMCaptionBarButton m_BtnMinimize;
CMSMCaptionBarButton m_BtnMaximize;
CMSMCaptionBarButton m_BtnClose;
对于Caption Window,还有自己的一些属性,比如窗口高度。
int m_SystemHeight;
int m_CaptionHeight;//标题栏高度
2)如何绘制元素
为了保证窗口接收双击事件,需要组成S_DBLCLKS风格的窗口。
LPCTSTR lpszClass = AfxRegisterWndClass(CS_DBLCLKS, ::LoadCursor(NULL, IDC_ARROW),
(HBRUSH)(COLOR_BTNFACE+1), NULL);
Create->CreateEx
创建窗口
CWnd::Create(lpszClass, NULL, dwStyle | WS_CLIPSIBLINGS, rect, pParentWnd, nID)
创建好窗口之后还必须加入到窗口的一个Pane的列表中去
if (pParentWnd->IsKindOf (RUNTIME_CLASS (CFrameWndEx)))
{
((CFrameWndEx*) pParentWnd)->AddPane (this);
}
加载Capation Icon,设置Caption 标题
SetIcon (hIcon);
SetWindowText (strCaption);
创建最小化按钮,恢复按钮,最大化按钮
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"));
对于按钮上图片的设置与加载是在CMSMVisualManager中设置的,这是在VS2008 Sp1中有的可以设置多种UI主题风格,类似于换皮肤。
下面列出了与Caption Window相关的函数。
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;
}
通过这些函数可以设置Button的图片以及Caption Windowd的背景。
通过上面就把窗口UI元素画好了,下面介绍消息的处理
3)Caption Windows处理的消息又哪些?
鼠标左键单击(需要判断是最小化,最大化,恢复,还是关闭);
鼠标右键单击(弹出帮助菜单)
鼠标左键双击(最大化或者恢复)
WM_SIZE消息,当窗口大小变化时,需要移动最小化,最大化,恢复按钮的位置。
也就是下面的一些消息。
ON_MESSAGE(WM_SETTEXT, OnSetText)
ON_WM_SIZE()
ON_WM_CONTEXTMENU()
ON_WM_SYSCOMMAND()
4)对于按钮消息的处理WM_XXX应当转化成WM_NCXXX,也就是说客户端消息要转化成非客户端消息。因为对于单文档程序来说,
标题栏属于非客户区。
BOOL CMSMCaptionBar::PreTranslateMessage (MSG* pMsg)
在其中调用HitTest函数判断鼠标的位置,如果是在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不感兴趣的消息会发送给父窗口处理。
5)WM_SIZE消息的处理
对WM_SIZE的消息处理就是移动最下化,最大化,以及恢复按钮。
3)使用Caption Window
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
设置风格,去掉WS_CAPTION | FWS_ADDTOTITLE,即创建不带Caption的Mainframe,
因为Caption是我们自己要创建的,而不是自动创建
ModifyStyle (WS_CAPTION | FWS_ADDTOTITLE, 0);
去掉窗口的边框
ModifyStyleEx (WS_EX_CLIENTEDGE, 0);
设置窗口风格
CMFCVisualManager::SetDefaultManager (RUNTIME_CLASS (CMSMVisualManager));