場景
-
我們在做
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; \
}
動態綁定窗口(按鈕)處理函數的方法1
- 我們如果要動態增加綁定按鈕的處理函數,方法之一即是需要新增一個宏,這個宏處理
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;
}
動態綁定窗口(按鈕)處理函數的方法2
- 除此之外我們還可以預先定義一堆的窗口
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
// View.h : interface of the CView class
//
/////////////////////////////////////////////////////////////////////////////
#pragma once
#include <GdiPlus.h>
#include <vector>
#include <map>
#include <functional>
#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
// View.cpp : implementation of the CView class
//
/////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "resource.h"
#include "View.h"
#include <CommCtrl.h>
#include <string>
#include <regex>
#include <assert.h>
#include <atlmisc.h>
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;
}
view_id.h
#ifndef VIEW_ID_H
#define VIEW_ID_H
#include <Windows.h>
enum{
kWindowMinId = WM_USER+1,
kWindowMaxId = kWindowMinId+100,
kWindowWidgetContainerMinId = kWindowMaxId+1,
kWindowWidgetContainerMaxId = kWindowWidgetContainerMinId+100
};
#endif
widget_container.h
#ifndef WIDGET_CONTAINER_H
#define WIDGET_CONTAINER_H
#include "atlbase.h"
#include "atlapp.h"
#include "atlmisc.h"
#include "atlctrls.h"
#include <vector>
#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
widget_container.cpp
#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