引言
之前自己写界面库的时候,未找到好方法响应消息,使用的是最笨的方式,在主窗口的响应函数中,判断是哪个子窗口,进而再判断是哪个按钮。
最近使用云信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));