2.3 更加灵活的事件控制方式
在MFC中,我们控制一个已有控件的行为可以通过子类继承的的方式。在wxWidgets中提高了两种更加方便的方式对已有的事件处理程序进行扩展那就是 动态方式和 插入方式。
2.3.1 动态方式
所有从wxevtHandler继承的类都提供了两个特殊的函数Bind和Connect,Connect是一种以前版本的处理方式,Bind是connect的替代方法,他使用了模板方法更加灵活和安全,如果是新程序建议不要使用Connect而直接应该使用Bind。
我们在讨论事件处理的时候总是提到的是静态方式,然而wxwidgets为我们提供了一种在运行时刻维护event table的方法,暂时称为动态方式吧。我们可以在某一个特定的时刻让我们的控件(窗口)对同一个事件产生不同的行为,这是非常cool的一种方法,而且通过动态方法,我们可以使得同一种控件(窗体)在某种情况下产生不同的行为。
这是Bind的函数定义,我只列出一种定义其他的定义我们可以通过帮助自行进行了解。
template<typename EventTag , typename Class , typename EventArg , typename EventHandler >
Parameters:
|
我们来看一个例子,我们自己做一个Textbox使得他只能接受字符,其实使用Bind可以更加灵活,我只是想通过这个例子对应MFC中的子类化
#define ID_MyText wxID_HIGHEST+1 class MyTextCtrl:public wxTextCtrl { public : MyTextCtrl(wxWindow *parent,wxWindowID id) : wxTextCtrl(parent,id,_T("MyText"),wxPoint(30,50),wxSize(100,26)) { Bind(wxEVT_CHAR,&MyTextCtrl::OnChar,this, ID_MyText); } void OnChar(wxKeyEvent& event); }; |
注意这里的OnChar是一个事件处理函数,前面说个了wxwidgets不使用虚函数机制进行事件处理,因此我们的OnChar并没有override wxtextCtrl的onchar函数,在myTextCtrl中我们使用Bind替换了wxTextCtrl中的按键事件的处理函数。
然后我们在myframe中建立我们自己的控件就可以了。
MyTextCtrl* txtbox=new MyTextCtrl(this, ID_MyText);
void MyTextCtrl::OnChar(wxKeyEvent &event) { if(wxIsalpha(event.m_keyCode)) { event.Skip(); } else { wxBell(); } } |
这里由一点需要注意的就是event.Skip,它有点想windows的defaultwndProc当我们不想处理的就调用skip(),由默认行为接管。
2.3.2 插入方式:
插入方式同动态方式类似,也是改变控件行为的一种方式,同样你不必从window类继承一个新类来处理事件,你可以直接从wxEvtHandler建立一个新的事件处理类,然后使用PushEventHandler将新的事件处理行为加入到已有控件或窗体的事件处理表中,这个被加入的新事件处理方法会先接受事件,如果你的事件处理函数没有对事件进行处理,wxwidgets才会去搜索事件处理表,你也可以用PopEventHandler将事件加入的事件移除,并且如果你提供了true作为实参的化这个类就会被删除。使用这种机制我们可以定义一种行为,供很多的控件进行使用,非常的方便。同样是上个例子,我们来换一种实现方式。
首先定义一个新类,并处wxEvtHanler继承
class TextctrlEVHandler:public wxEvtHandler { DECLARE_EVENT_TABLE() public: void OnChar(wxKeyEvent& event); }; |
需要注意我们自己定义了eventtable,并且在实现文件中我们需要用BEGIN_EVENT_TABLE、END_EVENT_TABLE和EVT_XXXXXX
BEGIN_EVENT_TABLE(TextctrlEVHandler, wxEvtHandler) EVT_CHAR(TextctrlEVHandler::OnChar) END_EVENT_TABLE()
void TextctrlEVHandler::OnChar(wxKeyEvent &event) { if(wxIsalpha(event.m_keyCode)) { event.Skip(); } else { wxBell(); } } |
在需要的时候我们将这个事件类Push到相应的位置
wxTextCtrl* tctrl= new wxTextCtrl(this,wxID_Any,_T("Hello"),wxPoint(0,0),wxSize(100,26)); TextctrlEVHandler* myevhandler=new TextctrlEVHandler(); tctrl->PushEventHandler(myevhandler); |
此时的tctrl就具有了只接受字符,不接受数字的能力了。但我们一定要注意push进去的类在退出程序前一定要pop出来,否则会引发一个异常,语法如下:
tctrl->PopEventHandler(true);
这里需要说明的是另外一点内容wxID_Any这个资源识别符,我们对这类识别符并不陌生,就相当于我们给控件的名字,wxID_Any在系统中的定义是-1,也就是说当我们不关心控件的ID时可以使用wxID_Any作为识别符,这样编译器会自动给我们分配一个。WxWidget对控件ID的定义是在同一个父下识别符必须唯一,换句话说也就是不同父的控件可以使用相同的资源识别符,还有我们可以自定义这个符号,系统占用了从wxID_LOWEST到wxID_HIGHEST的识别符号,我们自定义的识别符可以从wxID_HIGHEST+1开始。