引言
之前自己写界面库的时候,未找到好方法响应消息,使用的是最笨的方式,在主窗口的响应函数中,判断是哪个子窗口,进而再判断是哪个按钮。
最近使用云信duilib,正好研究了下duilib按钮与响应绑定的方法。
duilib事件绑定
每个控件都可以单独设置自己的事件处理函数,一般在InitWindow
方法中初始化各个控件的事件处理函数。每个控件都有许多形如Attach···
的方法,比如按钮控件Button
有AttachMouseEnter
、AttachButtonDown
、AttachClick
方法,他们分别用于指定控件鼠标进入、鼠标按下、鼠标单击的事件处理函数。
一个常见的例子:
void MyForm::InitWindow()
{
ui::Button* btn_login = static_cast<ui::Button*>(FindControl(L"login_button"));
btn_login->AttachClick(nbase::Bind(&MyForm::OnLoginClicked, this, std::placeholders::_1, true));
}
bool MyForm::OnLoginClicked(ui::EventArgs * msg)
{
std::wstring name = msg->pSender->GetName();
if (msg->Type == ui::kEventClick)
{
if (name == L"login_button")
{
DoLogin();
}
}
return true;
}
本立而道生
很多东西不知道根本,总感觉很玄,不知道怎么实现,也不知道其能够做什么,不能做什么,为什么能这么做。当然了,很多东西也不必追究根本,而这一个事件绑定,对我而言,有必要探究一下。
跟踪AttachClick进去看看,这是一个模板类,在这个类里定义了AttachClick函数,
namespace ui
{
template<typename InheritType = Control>
class UILIB_API ButtonTemplate : public LabelTemplate<InheritType>
{
public:
ButtonTemplate();
virtual void Activate() override;
virtual void HandleMessage(EventArgs& event) override;
void AttachClick(const EventCallback& callback) { OnEvent[kEventClick] += callback; }
};
#include "ButtonImpl.h"
typedef ButtonTemplate<Control> Button;
typedef ButtonTemplate<Box> ButtonBox;
} // namespace ui
这个函数是这么定义的
void AttachClick(const EventCallback& callback) {
OnEvent[kEventClick] += callback;
}
其中kEventClick是定义在duilib/CORE/define.h中的消息类型,如:
//定义所有消息类型
enum EventType
{
kEventInternalDoubleClick,
kEventInternalMenu,
kEventInternalSetFocus,
kEventInternalKillFocus ,
kEventNone,
kEventFirst,
kEventAll,
。。。
OnEvent[kEventClick]是数组的写法,也可能是MAP的写法。
这是定义在所有控件的基类Control中的一个成员变量。
protected:
EventMap OnEvent;
EventMap OnXmlEvent;
GifEventMap OnGifEvent;
EventMap是这种类型
typedef std::map<EventType, CEventSource> EventMap;
因此
OnEvent[kEventClick],指的是kEventClick单击事件所对应的事件处理方式CEventSource
CEventSource是这么定义的,继承自vector容器类,容器中保存着bool(ui::EventArgs*)类型的消息处理函数,重载了+操作符。
typedef std::function<bool(ui::EventArgs*)> EventCallback;
class CEventSource : public std::vector<EventCallback>
{
public:
CEventSource& operator += (const EventCallback& item)
{
push_back(item);
return *this;
}
bool operator() (ui::EventArgs* param) const
{
for (auto it = this->begin(); it != this->end(); it++) {
if(!(*it)(param)) return false;
}
return true;
}
};
到这里,大体明白点是怎么回事了,attackClick…是将按钮的单击事件,与事件对应的响应函数,以map映射的方式关联在一起。
AttachClick(nbase::Bind(&MyForm::OnLoginClicked,this,std::placeholders::_1, true));