1、消息映射的链接
如果这个类是ATL窗口类,我们可以从这个窗口类派生自己的类,就象Base Class Chaining中描述的一样。
class CBase : public CWindowImpl<CBase>
{
public:
BEGIN_MSG_MAP(CBase)
MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
MESSAGE_HANDLER(WM_LBUTTONDOWN, OnButtonDown)
ALT_MSG_MAP(100)
MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
MESSAGE_HANDLER(WM_LBUTTONDOWN, OnButtonDown2)
END_MSG_MAP()
public:
CBase();
virtual ~CBase();
LRESULT OnDestroy(UINT, WPARAM, LPARAM, BOOL&)
{
PostQuitMessage(0);
return 0;
}
LRESULT OnButtonDown(UINT, WPARAM, LPARAM, BOOL&)
{
MessageBox("baseclass : button down\n");
return 0;
}
LRESULT OnButtonDown2(UINT, WPARAM, LPARAM, BOOL&)
{
MessageBox("baseclass : button down2\n");
return 0;
}
};
class CDerived: public CBase
{
BEGIN_MSG_MAP(CDerived)
MESSAGE_HANDLER(WM_LBUTTONDOWN, OnButtonDown)
CHAIN_MSG_MAP_ALT(CBase, 100) //链接到基类
END_MSG_MAP()
LRESULT OnButtonDown(UINT, WPARAM, LPARAM, BOOL& bHandle)
{
MessageBox("derivedclass : button down\n");
bHandle = FALSE; //决定是否继续传消息
return 0;
}
};
2、超类化
如果我们想扩展一个预定义的窗口类(如按纽类或列表框类)的功能,我们可以超类化它。就是创建一个基于这个预定义类的新类,并在消息映射表中添加消息映射以增强它的功能。这个在使用MFC开发使用最多。
class CBeepButton : public CWindowImpl<CBeepButton>
{
public:
CBeepButton();
virtual ~CBeepButton();
DECLARE_WND_SUPERCLASS(_T("BeepButton"), _T("Button"))
BEGIN_MSG_MAP(CBeepButton)
MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLButtonDown)
END_MSG_MAP()
LRESULT OnLButtonDown(UINT, WPARAM, LPARAM, BOOL& bHandled)
{
MessageBeep(MB_ICONASTERISK);
bHandled = FALSE; //继续调用基类的函数
return 0;
}
};
3、子类化
有些时候,我们需要改变一个已经存在的窗口实例的行为,而不是一个窗口类——或许我们要让一个对话框上的编辑框做点什么特别的事情。在这种情况下,我们可以写一个新的ATL窗口类,并子类化这个已经存在的编辑框。任何本该发送到这个编辑框的消息都会先被发送到这个子类的对象。
class CNoNumEdit : public CWindowImpl<CNoNumEdit>
{
public:
CNoNumEdit();
virtual ~CNoNumEdit();
BEGIN_MSG_MAP(CNoNumEdit)
MESSAGE_HANDLER(WM_CHAR, OnChar)
END_MSG_MAP()
LRESULT OnChar(UINT, WPARAM wParam, LPARAM, BOOL& bHandled)
{
TCHAR ch = wParam;
if(_T('0')<=ch && ch<=_T('9'))
MessageBeep(0);
else
bHandled = FALSE; //不处理,交给编辑框自己处理
return 0;
}
};
4、容器窗口
另外一种可选的方法:我们也可以让这个编辑框成为一个被包含的窗口,所有发送到这个编辑框的消息都会经过它的容器窗口;我们可以在这个容器窗口中为这个被包含的窗口实现特殊的消息处理。
class CMyWindow : public CWindowImpl<CMyWindow>
{
public:
CMyWindow();
virtual ~CMyWindow();
BEGIN_MSG_MAP( CMyWindow )
MESSAGE_HANDLER(WM_CREATE, OnCreate)
MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
ALT_MSG_MAP(99) //contained window's messages come here...
MESSAGE_HANDLER(WM_CHAR, OnChar)
END_MSG_MAP()
LRESULT OnCreate(UINT, WPARAM, LPARAM, BOOL&)
{
RECT rc = { 10, 10, 200, 35 };
m_wndContained.Create(m_hWnd, rc, _T("non-numeric edit"),
WS_CHILD|WS_VISIBLE|WS_BORDER, 0, 666);
return 0;
}
LRESULT OnChar(UINT, WPARAM wParam, LPARAM, BOOL& bHandled)
{
TCHAR ch = wParam;
if(_T('0')<=ch && ch<=_T('9'))
MessageBeep( 0 );
else
bHandled = FALSE; //不处理,交给编辑框自己处理
return 0;
}
LRESULT OnDestroy(UINT, WPARAM, LPARAM, BOOL&)
{
PostQuitMessage( 0 );
return 0;
}
private:
CContainedWindow m_wndContained;
};
CMyWindow::CMyWindow() : m_wndContained(_T("edit"), this, 99)
{
}
5、消息反射
最后的一种方法就是消息反射,当一个窗口收到一个消息后不处理它,而是反射给发送这个消息的窗口自己处理,这种技术可以用来创建自包含的控件。
class CStaticLink : public CWindowImpl<CStaticLink>
{
public:
DECLARE_WND_SUPERCLASS(_T("StaticLink"), _T("Static"))
CStaticLink();
virtual ~CStaticLink();
void SetLinkText(LPCTSTR szLink)
{
USES_CONVERSION;
m_bstrLink = T2OLE(szLink);
}
BEGIN_MSG_MAP(CStaticLink)
//uses message reflection: WM_* comes back as OCM_*
MESSAGE_HANDLER(OCM_COMMAND, OnCommand)
MESSAGE_HANDLER(OCM_CTLCOLORSTATIC, OnCtlColor)
MESSAGE_HANDLER(WM_DESTROY, OnDestroy) //not a reflected message
DEFAULT_REFLECTION_HANDLER()
END_MSG_MAP()
LRESULT OnDestroy(UINT, WPARAM, LPARAM, BOOL&)
{
if(m_hFont)
{
DeleteObject(m_hFont);
m_hFont = NULL;
}
return 0;
}
LRESULT OnCommand(UINT, WPARAM wParam, LPARAM, BOOL&)
{
USES_CONVERSION;
int code = HIWORD(wParam);
if(code==STN_CLICKED || code==STN_DBLCLK)
{
if(m_bstrLink.Length() == 0)
{
GetWindowText(&m_bstrLink);
}
if((int)ShellExecute(m_hWnd, _T("open"),
OLE2T(m_bstrLink), NULL, NULL, SW_SHOWNORMAL) > 32)
{
m_bVisited = TRUE; //return codes>32 => success
Invalidate();
}
else
{
MessageBeep(0);
ATLTRACE(_T("Error: CStaticLink couldn't open file"));
}
}
return 0;
}
LRESULT OnCtlColor(UINT, WPARAM wParam, LPARAM, BOOL&)
{
// notify bit must be set to get STN_* notifications
ModifyStyle(0, SS_NOTIFY);
HBRUSH hBr = NULL;
if((GetStyle()&0xff) <= SS_RIGHT)
{
// it's a text control: set up font and colors
if(!m_hFont)
{
LOGFONT lf;
GetObject(GetFont(), sizeof(lf), &lf);
lf.lfUnderline = TRUE;
m_hFont = CreateFontIndirect(&lf);
}
HDC hDC = (HDC)wParam;
SelectObject(hDC, m_hFont);
SetTextColor(hDC, m_bVisited?m_clrVisited:m_clrUnvisited);
SetBkMode(hDC, TRANSPARENT);
hBr = (HBRUSH)GetStockObject(HOLLOW_BRUSH);
}
return (LRESULT)hBr;
}
private:
COLORREF m_clrUnvisited;
COLORREF m_clrVisited;
BOOL m_bVisited;
HFONT m_hFont;
CComBSTR m_bstrLink;
};
class CReflectDlg : public CDialogImpl<CReflectDlg>
{
public:
enum { IDD = IDD_DIALOG1 };
BEGIN_MSG_MAP(CReflectDlg)
COMMAND_RANGE_HANDLER(IDOK, IDCANCEL, OnClose)
MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
REFLECT_NOTIFICATIONS() //reflect messages back to static links
END_MSG_MAP()
LRESULT OnInitDialog(UINT, WPARAM, LPARAM, BOOL&)
{
CenterWindow( GetParent() );
//a textual static control:
s1.SubclassWindow(GetDlgItem(IDS_TEST1));
//a static control displaying an icon
s2.SubclassWindow(GetDlgItem(IDS_TEST2));
//set the icon's link
s2.SetLinkText(_T("http://www.microsoft.com"));
return 0;
}
LRESULT OnClose(UINT, WPARAM wID, HWND, BOOL&)
{
EndDialog(wID);
return 0;
}
private:
CStaticLink s1, s2;
};
以上便是这5种方法,在软件设计时有时候达到一个目的的方法很多,这时候最重要的就是选择最适合的方法,使项目改动更小,扩展最好。
下载所有演示例子