我们在做 WTL
或 MFC
开发的时候, 是通过消息映射宏来绑定消息处理函数的.如果是窗口里的子窗口需要绑定消息, 那么基本上就是通过子窗口的 ID
和类成员方法绑定某个消息处理宏,但是如何才可以绑定动态创建的由系统自动分配ID的窗口(按钮,菜单)呢?
动态创建的按钮,我们可能并不知道需要创建多少个,也由于某种原因无法分配指定的窗口ID
, 而消息映射宏只能在编译试确定绑定的窗口ID,怎么办?
BEGIN_MSG_MAP(CMainFrame)
COMMAND_ID_HANDLER(ID_APP_EXIT, OnFileExit)
COMMAND_ID_HANDLER(ID_FILE_NEW, OnFileNew)
COMMAND_ID_HANDLER(ID_APP_ABOUT, OnAppAbout)
END_MSG_MAP()
MFC
或者 WTL
都需要在声明时自定义窗口时就定义好消息处理宏. 我们看看 WTL
的消息映射宏开始定义,就是一个方法 ProcessWindowMessage
:#define BEGIN_MSG_MAP(theClass) \
public: \
BOOL ProcessWindowMessage(_In_ HWND hWnd, _In_ UINT uMsg, _In_ WPARAM wParam,\
_In_ LPARAM lParam, _Inout_ LRESULT& lResult, _In_ DWORD dwMsgMapID = 0) \
{ \
BOOL bHandled = TRUE; \
(hWnd); \
(uMsg); \
(wParam); \
(lParam); \
(lResult); \
(bHandled); \
switch(dwMsgMapID) \
{ \
case 0:
COMMAND_ID_HANDLER
宏展开是一个 if
语句, 这个 if
语句放在了 ProcessWindowMessage
方法里. 所以我们也可以定义自己的宏.#define COMMAND_ID_HANDLER(id, func) \
if(uMsg == WM_COMMAND && id == LOWORD(wParam)) \
{ \
bHandled = TRUE; \
lResult = func(HIWORD(wParam), LOWORD(wParam), (HWND)lParam, bHandled); \
if(bHandled) \
return TRUE; \
}
WM_COMMAND
消息,并且这个窗口的句柄属于新增的按钮的,那么即调用新增按钮绑定的处理函数. 绑定处理函数我们可以用 std::function
来创建.#define DYNAMIC_BINDING_COMMAND_PROCESS_FUNC(theChainClass) \
if(uMsg == WM_COMMAND) \
{ \
bHandled = theChainClass::ProcessDynamicButton((HWND)lParam); \
if(bHandled) \
return TRUE; \
}
BEGIN_MSG_MAP_EX(CView)
DYNAMIC_BINDING_COMMAND_PROCESS_FUNC(CView)
REFLECT_NOTIFICATIONS()
END_MSG_MAP()
std::map<HWND,std::function<void(HWND)>*> map_button_func_;
BOOL CView::ProcessDynamicButton(HWND hwnd)
{
auto ite = map_button_func_.find(hwnd);
if(ite == map_button_func_.end())
return FALSE;
auto func = ite->second;
(*func)(hwnd);
return TRUE;
}
ID
,比如在 kWindowMinId
和 kWindowMaxId
范围内, 之后把 ID
分配给新建的按钮,而这些 ID
通过范围宏来预先绑定处理函数 OnCommandRangeHandlerEX
, 之后我们就可以在这个函数对不同 ID
的按钮进行处理.
enum{
kWindowMinId = WM_USER+1,
kWindowMaxId = kWindowMinId+100,
kWindowWidgetContainerMinId = kWindowMaxId+1,
kWindowWidgetContainerMaxId = kWindowWidgetContainerMinId+100
};
BEGIN_MSG_MAP_EX(CView)
COMMAND_RANGE_HANDLER_EX(kWindowMinId,kWindowMaxId,OnCommandRangeHandlerEX)
REFLECT_NOTIFICATIONS()
END_MSG_MAP()
void OnCommandRangeHandlerEX(UINT uNotifyCode, int nID, CWindow wndCtl);
// View.h : interface of the CView class
//
/////////////////////////////////////////////////////////////////////////////
#pragma once
#include
#include
#include
#include
#include "view_id.h"
#include "widget_container.h"
#define DYNAMIC_BINDING_COMMAND_PROCESS_FUNC(theChainClass) \
if(uMsg == WM_COMMAND) \
{ \
bHandled = theChainClass::ProcessDynamicButton((HWND)lParam); \
if(bHandled) \
return TRUE; \
}
class CView : public CWindowImpl<CView>
{
public:
DECLARE_WND_CLASS(NULL)
BOOL PreTranslateMessage(MSG* pMsg);
BEGIN_MSG_MAP_EX(CView)
MSG_WM_CREATE(OnCreate)
MESSAGE_HANDLER(WM_PAINT, OnPaint)
COMMAND_RANGE_HANDLER_EX(kWindowMinId,kWindowMaxId,OnCommandRangeHandlerEX)
CHAIN_MSG_MAP_MEMBER(wc_)
DYNAMIC_BINDING_COMMAND_PROCESS_FUNC(CView)
REFLECT_NOTIFICATIONS()
END_MSG_MAP()
protected:
// Handler prototypes (uncomment arguments if needed):
// LRESULT MessageHandler(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
// LRESULT CommandHandler(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
// LRESULT NotifyHandler(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/)
int OnCreate(LPCREATESTRUCT lpCreateStruct);
LRESULT OnPaint(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/);
LRESULT OnCtlColor(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
void OnCommandRangeHandlerEX(UINT uNotifyCode, int nID, CWindow wndCtl);
CButton* AddButton(int _id);
BOOL ProcessDynamicButton(HWND hwnd);
void TestClickButton(HWND hwnd);
private:
WidgetContainer wc_;
std::vector<CButton*> buttons_;
std::map<HWND,std::function<void(HWND)>*> map_button_func_;
int window_id_;
int button_x_;
int button_y_;
};
// View.cpp : implementation of the CView class
//
/////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "resource.h"
#include "View.h"
#include
#include
#include
#include
#include
BOOL CView::PreTranslateMessage(MSG* pMsg)
{
pMsg;
return FALSE;
}
void CView::OnCommandRangeHandlerEX(UINT uNotifyCode, int nID, CWindow wndCtl)
{
static const wchar_t* kFormat = L"OnCommandRangeHandlerEX -> %d";
static wchar_t buf[MAX_PATH] = {0};
wsprintf(buf,kFormat,nID);
MessageBox(buf);
}
LRESULT CView::OnPaint(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
CRect rect;
GetClientRect(&rect);
CPaintDC dc(m_hWnd);
CMemoryDC mdc(dc,dc.m_ps.rcPaint);
mdc.FillSolidRect(rect,RGB(255,255,255));
return 0;
}
CButton* CView::AddButton(int _id)
{
static const wchar_t* kFormat = L"Button-%d";
static wchar_t buf[MAX_PATH] = {0};
wsprintf(buf,kFormat,button_x_);
auto button = new CButton();
CRect rect(button_x_,button_y_,button_x_+80,button_y_+30);
button->Create(m_hWnd,rect,buf,WS_CHILD|WS_VISIBLE,0,_id);
button_x_=rect.right+10;
return button;
}
int CView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
// 1. 动态窗口函数绑定方式之一,使用ID范围函数宏 COMMAND_RANGE_HANDLER 来绑定动态创建的按钮.
window_id_ = kWindowMinId;
button_x_ = 10;
button_y_ = 10;
for(int i = 0; i< 5;++i)
AddButton(window_id_++);
// 2. 动态窗口函数绑定方式之二,绑定容器,由容器来管理动态创建的按钮.
wc_.CreateWidget(m_hWnd);
// 3. 动态窗口函数绑定方式之三, 使用自定义宏处理消息. 不需要设置ID范围.
button_x_ = 10;
button_y_ = 100;
for(int i = 0; i< 5;++i){
auto button = AddButton(0); // ID自动分配.
map_button_func_[button->m_hWnd] = new std::function<void(HWND)>(
std::bind(&CView::TestClickButton,this,std::placeholders::_1));
}
return 0;
}
void CView::TestClickButton(HWND hwnd)
{
static const wchar_t* kFormat = L"TestClickButton -> %ld";
static wchar_t buf[MAX_PATH] = {0};
wsprintf(buf,kFormat,hwnd);
MessageBox(buf);
}
BOOL CView::ProcessDynamicButton(HWND hwnd)
{
auto ite = map_button_func_.find(hwnd);
if(ite == map_button_func_.end())
return FALSE;
auto func = ite->second;
(*func)(hwnd);
return TRUE;
}
#ifndef VIEW_ID_H
#define VIEW_ID_H
#include
enum{
kWindowMinId = WM_USER+1,
kWindowMaxId = kWindowMinId+100,
kWindowWidgetContainerMinId = kWindowMaxId+1,
kWindowWidgetContainerMaxId = kWindowWidgetContainerMinId+100
};
#endif
#ifndef WIDGET_CONTAINER_H
#define WIDGET_CONTAINER_H
#include "atlbase.h"
#include "atlapp.h"
#include "atlmisc.h"
#include "atlctrls.h"
#include
#include "view_id.h"
// 注意, 这个 WidgetContainer 不是窗口, 只是一个容器.
class WidgetContainer : public CMessageMap
{
BEGIN_MSG_MAP(WidgetContainer)
COMMAND_RANGE_HANDLER(kWindowWidgetContainerMinId,
kWindowWidgetContainerMaxId,OnOperator)
END_MSG_MAP()
void CreateWidget(HWND hwnd);
protected:
LRESULT OnOperator(WORD wNotify,WORD wID,HWND hCtrl, BOOL &bHandeld);
CButton* AddButton(int _id);
private:
HWND m_hWnd;
std::vector<CButton*> buttons_;
int window_id_;
};
#endif
#include "stdafx.h"
#include "widget_container.h"
void WidgetContainer::CreateWidget(HWND hwnd)
{
m_hWnd = hwnd;
window_id_ = kWindowWidgetContainerMinId;
for(int i = 0; i< 5;++i)
AddButton(window_id_++);
}
CButton* WidgetContainer::AddButton(int _id)
{
static int x = 10;
static const wchar_t* kFormat = L"wButton-%d";
static wchar_t buf[MAX_PATH] = {0};
wsprintf(buf,kFormat,x);
auto button = new CButton();
CRect rect(x,50,x+100,80);
button->Create(m_hWnd,rect,buf,WS_CHILD|WS_VISIBLE,0,_id);
x=rect.right+10;
return button;
}
LRESULT WidgetContainer::OnOperator(WORD wNotify,WORD wID,HWND hCtrl, BOOL &bHandeld)
{
static const wchar_t* kFormat = L"OnCommandRangeHandlerEX -> %d";
static wchar_t buf[MAX_PATH] = {0};
wsprintf(buf,kFormat,wID);
::MessageBox(m_hWnd,buf,L"",0);
bHandeld = TRUE;
return 0;
}
项目下载地址
https://download.csdn.net/download/infoworld/12403302