[WTL/ATL]_[C/C++]_[如何給動態創建的按鈕綁定處理函數]

場景

  1. 我們在做 WTLMFC 開發的時候, 是通過消息映射宏來綁定消息處理函數的.如果是窗口裏的子窗口需要綁定消息, 那麼基本上就是通過子窗口的 ID和類成員方法綁定某個消息處理宏,但是如何纔可以綁定動態創建的由系統自動分配ID的窗口(按鈕,菜單)呢?

  2. 動態創建的按鈕,我們可能並不知道需要創建多少個,也由於某種原因無法分配指定的窗口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()

說明

  1. 無論是 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:
  1. 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

  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

  1. 除此之外我們還可以預先定義一堆的窗口 ID,比如在 kWindowMinIdkWindowMaxId 範圍內, 之後把 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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章