[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 
#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

// 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;
}

view_id.h


#ifndef VIEW_ID_H
#define VIEW_ID_H

#include 

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 
#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;
}

截图

[WTL/ATL]_[C/C++]_[如何给动态创建的按钮绑定处理函数]_第1张图片

下载

项目下载地址
https://download.csdn.net/download/infoworld/12403302

你可能感兴趣的:(ATL/WTL界面开发)