0,完整实例
#include "wx/wx.h"
// 定义应用程序类
class MyApp : public wxAppvirtual bool OnInit();
};
// 定义主窗口类
class MyFrame : public wxFrameDECLARE_APP(MyApp)
// 告诉wxWidgets主应用程序是哪个类
IMPLEMENT_APP(MyApp)
// 初始化程序
bool MyApp::OnInit(){
// 创建主窗口
MyFrame *frame = new MyFrame(wxT("Minimal wxWidgets App"));
// 显示主窗口
frame->Show(true);
// 开始事件处理循环
return true;}
// MyFrame类的事件表
BEGIN_EVENT_TABLE(MyFrame, wxFrame)wxMessageBox(msg, wxT("About Minimal"), wxOK | wxICON_INFORMATION, this);
}{
// 释放主窗口
Close();
}
#include "mondrian.xpm"{
// 设置窗口图标
SetIcon(wxIcon(mondrian_xpm));
// 创建菜单条
wxMenu *fileMenu = new wxMenu;
// 添加“关于”菜单项
wxMenu *helpMenu = new wxMenu;// 将菜单项添加到菜单条中
wxMenuBar *menuBar = new wxMenuBar();menuBar->Append(helpMenu, wxT("&Help"));
// ...然后将菜单条放置在主窗口上
SetMenuBar(menuBar);
// 创建一个状态条来让一切更有趣些。
CreateStatusBar(2);SetStatusText(wxT("Welcome to wxWidgets!"));
}
这个小例子创建了一个主窗口(是一个wxFrame类的实例),这个主窗口有一个菜单条和一个状态条。菜单条上的菜单按照你的命令显示一个关于窗口或者退出这个小程序。class MyApp : public wxApp
{
public:
virtual bool OnInit();
};
在这个OnInit函数中,你通常应该创建至少一个窗口,对传入的命令行参数进行解析,为应用程序进行数据设置和其它的一些初始化的操作.如果这个函数返回真,wxWidgets将开始事件循环用来处理用户输入并且在必要的情况下处理这些输入。如果OnInit函数返回假, wxWidgets将会释放它内部已经分配的资源,然后结束整个程序的运行。接下来我们看一个最简单的OnInit函数的实现:bool MyApp::OnInit()
{
MyFrame *frame = new MyFrame(wxT("Minimal wxWidgets App"));
frame->Show(true);
return true;
}
你可能还会注意到上面例子中的wxT这个宏,在接下来的例子中,这个宏还会被频繁用到。它的作用是让你的代码兼容Unicode模式。这个宏和另外一个_T宏的作用是完全一样的。使用这个宏也不会带来运行期的性能损失。(你可能还会遇到另外一个类似的"_()"标记,这个标记是用来告诉 wxWidgets将其中的字符串翻译成其它语言的版本.一点提示:
即使没有声明DECLARE_APP,你仍然可以不用进行类型强制转化就直接对wxTheApp变量调用wxApp的方法.这可以避免在所有的头文件中包含MyApp的头文件,对于那些库文件而不是应用程序的代码来说也更有意义,而且还可以缩短编译的时间。
二,Frame窗口类
我们来看一看自定义的Frame窗口类MyFrame.一个Frame窗口是一个可以容纳别的窗口的顶级窗口,通常拥有一个标题栏和一个菜单栏。下面是我们的例子中这个类的定义,可以将其放在MyApp的定义之后:class MyFrame : public wxFrame
{
public:
MyFrame(const wxString& title);
void OnQuit(wxCommandEvent& event);
void OnAbout(wxCommandEvent& event);
private:
DECLARE_EVENT_TABLE()
};
这个窗口类的定义有一个构造函数,两个用来把菜单命令和C++代码相连的事件处理函数,还有一个宏来告诉wxWidgets这个类想要自己处理某些事件。
#include "mondrian.xpm"MyFrame::MyFrame(const wxString& title) : wxFrame(NULL, wxID_ANY, title)
{
SetIcon(wxIcon(mondrian_xpm));
wxMenu *fileMenu = new wxMenu;
wxMenu *helpMenu = new wxMenu;
helpMenu->Append(wxID_ABOUT, wxT("&About.../tF1"), wxT("Show about dialog"));
fileMenu->Append(wxID_EXIT, wxT("E&xit/tAlt-X"), wxT("Quit this program"));
wxMenuBar *menuBar = new wxMenuBar();
menuBar->Append(fileMenu, wxT("&File"));
menuBar->Append(helpMenu, wxT("&Help"));
SetMenuBar(menuBar);
CreateStatusBar(2);
SetStatusText(wxT("Welcome to wxWidgets!"));
}
这个构造函数首先调用它的基类(wxFrame)的构造函数,使用的参数是父窗口(还没有父窗口,所以用NULL),窗口标识(wxID_ANY标识让 wxWidgets自己选择一个)和标题。这个基类的构造函数才真正创建了一个窗口的实例。除了这样的调用方法,还有另外一种方法是直接在构造函数里面显式调用基类默认的构造函数,然后调用wxFrame::Create函数来创建一个frame窗口的实例。
小图片或者是图标在所有的平台上都可以用XPM格式来表示。XPM文件其实是一个ASCII编码的完全符合C++语法的文本文件,所以可以直接用C++的方式包含到代码中(译者注:显然这样的包含方式在分发软件的时候是不需要分发这个图片文件的)。SetIcon那一行代码使用 mondrian_xpm变量在堆栈上创建了一个图标(这个mondrian变量是在mondrian.xpm文件里定义的)。然后将这个图标和 frame窗口关联在一起。
接下来创建了菜单条。增加菜单项的Append函数的三个参数的意义分别为:菜单项标识,菜单上的文本以及一个稍微长一些的帮助字符串。这个帮助字符串会自动在菜单项被高亮显示的时候自动显示在状态栏上。菜单上的文本中由"&"符号前导的字符将成为菜单的快捷操作符,在实际的显示中用下划线表示。而"/t"符号则前导一个全局的快捷键,这个快捷键甚至可以在菜单项没有显示的时候触发菜单功能。
这个构造函数所做的最后一件事是创建一个由两个区域组成的状态条并且在状态条的第一个区域写上欢迎的字样。
BEGIN_EVENT_TABLE(MyFrame, wxFrame)
EVT_MENU(wxID_ABOUT, MyFrame::OnAbout)
EVT_MENU(wxID_EXIT, MyFrame::OnQuit)
END_EVENT_TABLE()
所谓事件表,是一组位于类的实现文件(.cpp文件)中的宏,用来告诉wxWidgets来自用户或者其它地方的事件应该怎样和类的成员函数对应起来。
前面展示的事件表表明,要把鼠标点击标识分别为wxID_EXIT和wxID_ABOUT的菜单项的事件和MyFrame的成员函数OnAbout和 OnQuit关联起来。这里的EVT_MENU宏只是很多中事件宏的其中之一,事件宏的作用是告诉wxWidgets哪种事件应该被关联到哪个成员函数。这里的两个标识wxID_ABOUT和wxID_EXIT是wxWidgets预定义的宏,通常你应该通过枚举,常量或者预编译宏的方式定义你自己的标识。
用上面的方法定义的时间表是一种静态的事件表,它不可以在运行期改变,在下一章里,我们将描述怎样定义可以在运行期改变的动态事件表。现在,让我们来看看这两个事件处理函数.
void MyFrame::OnAbout(wxCommandEvent& event)
{
wxString msg;
msg.Printf(wxT("Hello and welcome to %s"),
wxVERSION_STRING);
wxMessageBox(msg, wxT("About Minimal"), wxOK | wxICON_INFORMATION, this);
}
void MyFrame::OnQuit(wxCommandEvent& event)
{
Close();
}
当用户点击关于菜单项的时候,MyFrame::OnAbout函数弹出一个消息框。这用到了wxWidgets提供的API wxMessageBox,它的四个参数分别代表消息内容,标题,窗口类型以及父窗口。当用户点击退出菜单项的时候,MyFrame::OnQuit函数被调用(你已经意识到了,这是事件表的功劳)。它调用wxFrame类的Close函数来释放frame窗口。因为没有别的窗口存在了,这触发了应用程序的退出,实际上,wxFrame类的Close函数并不直接关闭 frame窗口,而是产生一个wxEVT_CLOSE_WINDOW事件,这个事件默认的处理函数调用wxWindow::Destroy函数释放了 frame窗口。
用户还可以通过别的方法关掉应用程序,比如通过点击标题栏上的关闭按钮或者是通过系统菜单中的关闭菜单,在这种情况下,OnQuit函数是怎样被调用的呢?事实上,在这种情况,OnQuit函数并没有被调用。这时,wxWidgets会通过Close函数(象OnQuit中的那样),给 frame窗口发送一个wxEVT_CLOSE_WINDOW事件,这个事件默认的处理函数会释放掉frame窗口。在你的应用程序中,可以通过重载这个处理函数来增加改变这种默认的行为,比如:如果你想问一问你的用户是不是真的要关闭窗口。关于这种处理的细节,可以参见第4章,"窗口基础"。
另外,大多数的应用程序类还应该重载一个OnExit函数,以便在任何时候程序退出时,执行一下清理和资源回收的动作。需要注意的是,这个函数只有在OnInit函数返回真的时候才会被执行。当然,我们这个小例子程序就用不着定义这个函数了。
当程序员们首次面对苹果公司的第一个图形界面个人电脑的时候,他们为这种和以前所有的经验都不同的电脑操作方法感到惊奇。鼠标指针在一个个的窗口之间移来移去,滚动条,菜单,文本编辑框等等等等,真的很难以想象,这么多让人眼花缭乱的东西,其背后的代码该是多么复杂和不可思议。似乎所有这一切都是以完全并行的方式运行的,虽然这只是一个假象。对于很多人来说,苹果个人电脑是他们对事件驱动编程的第一印象。
所有的GUI程序都是事件驱动的。换句话说,应用程序一直停留在一个循环中,等待着来自用户或者其他地方(比如窗口刷新或网络连接)的事件,一旦收到某种事件,应用程序就将其扔给处理这个事件的函数。虽然看上去不同的窗口是同时被刷新的,但实际上,绝大多数的GUI程序都是单线程的,因此窗口的刷新是依次按顺序进行的。如果由于某种意外你的电脑变得很慢导致窗口刷新的过程变的很明显,你就会注意到这一点。
不同的GUI编程架构用不同的方法将它内部的事件处理机制展现给程序开发者。对于wxWidgets来说,事件表机制是最主要的方法。在下一小节我们会对此进行进一步的解释。
wxWidgets事件处理系统比起通常的虚方法机制来说要稍微复杂一点,但它的一个好处是可以避免需要实现基类中所有的虚方法,因为实现所有的基类虚方法有时候是不切实际的或者是效率很低的。
每一个wxEvtHandler的派生类,例如frame,按钮,菜单以及文档等,都会在其内部维护一个事件表,用来告诉wxWidgets事件和事件处理过程的对应关系。所有继承自wxWindow的窗口类,以及应用程序类都是wxEvtHandler的派生类.
要创建一个静态的事件表(意味着它是在编译期间创建的),你需要下面几个步骤:
定义一个直接或者间接继承自wxEvtHandler的类.在事件表的实现中增加事件宏,来实现从事件到事件处理过程的映射。
所有的事件处理函数拥有相同的形式。他们的返回值都是void,他们都不是虚函数,他们都只有一个事件对象作为参数。(如果你熟悉MFC,这可能会让你觉得轻松,因为在MFC中消息处理函数并没有一个统一的形式。)这个事件对象的类型是随这个处理函数要处理的事件的变化而变化的。例如简单控件(比如按钮)的命令处理函数和菜单命令的处理函数的参数都是wxCommandEvent类型,而size事件(这个事件通常是由用户改变窗口的客户区尺寸而引起的)处理函数的参数则是wxSizeEvent的类型。不同的事件参数类型可以调用的方法也不相同,通过这些方法,你可以获得事件产生的原因以及产生这个事件的控件的值的改变情况(比如,文本框中的值的改变)。当然最简单的情形是你完全不需要访问这个参数的任何方法,比如按钮点击事件。
让我们来扩展一下前一章中的例子,来增加一个窗口大小改变事件的处理和一个确定按钮的处理。下面是扩展以后的MyFrame的定义:
class MyFrame : public wxFrame
{
public:
MyFrame(const wxString& title);
void OnQuit(wxCommandEvent& event);
void OnAbout(wxCommandEvent& event);
void OnSize(wxSizeEvent& event);
void OnButtonOK(wxCommandEvent& event);
private:
DECLARE_EVENT_TABLE()
};
增加菜单项的代码和前一章的代码类似,而在frame窗口增加一个按钮的代码也只需要在MyFrame的构造函数中增加下面的代码:BEGIN_EVENT_TABLE(MyFrame, wxFrame)
EVT_MENU(wxID_ABOUT, MyFrame::OnAbout)
EVT_MENU(wxID_EXIT, MyFrame::OnQuit)
EVT_SIZE( MyFrame::OnSize)
EVT_BUTTON(wxID_OK, MyFrame::OnButtonOK)
END_EVENT_TABLE()
当用户点击关于菜单或者退出菜单的时候,这个事件被发送到frame窗口,而MyFrame的事件表告诉wxWidgets,对于标识符为 wxID_ABOUT的菜单事件应该发送到MyFrame::OnAbout函数,而标识符为wxID_EXIT的菜单事件应该发送到MyFrame:: OnQuit函数。换句话说:当事件处理循环处理这两个事件的时候,相应的函数将会以一个的wxCommandEvent类型的参数被调用。
EVT_SIZE事件映射宏不需要标识符参数因为这个事件只会被产生这个事件的控件所处理。
EVT_BUTTON这一行将导致当frame窗口及其子窗口中标识符为wxID_OK的按钮被点击的时候,OnButtonOK函数被调用。这个例子表明,事件可以不必被产生这个事件的控件所处理。让我们假定这个按钮是MyFrame的子窗口。当按钮被点击的时候,wxWidgets会首先检查wxButton类是否指定了相应的处理函数,如果找不到,则在其父亲的类所属的事件表中进行查找,在这个例子中,按钮的父亲是MyFrame类型的一个实例,在其事件表中指明了一个对应的处理函数,因此MyFrame::OnButtonOK函数就被调用了。类似的搜索过程不光发生在窗口控件的父子继承树中,也发生在普通的类继承关系中,这意味着你可以选择在哪里定义事件的处理函数。举例来说,如果你设计了一个对话框,这个对话框需要响应的类似标识符为wxID_OK的command事件。但是你可能需要把这个控件的创建工作留给使用你的代码的其他的程序员,只要他在创建这个控件的时候使用同样的标识符,你仍然可以给这个控件为这个事件定义默认的处理函数。
下图中演示了一次普通的按钮点击事件发生以后,wxWidgets搜索所有事件表的顺序。图中只演示了wxButton和 MyFrame两次继承关系。当用户点击了确认按钮的时候,一个新的wxCommandEvent事件被创建,其中包含标识符wxID_OK和事件类型 wxEVT_COMMAND_BUTTON_CLICKED,然后这个按钮的事件表开始通过wxEvtHandler::ProcessEvent函数进行匹配,事件表中的每一个条目都会去尝试匹配,然后是其父类wxControl的事件表,然后是wxWindow的。如果都没有匹配到, wxWidgets会搜索其父亲的类事件表,然后就找到了一条匹配条目:
EVT_BUTTON(wxID_OK,MyFrame::OnButtonOK)
这些事件不会传递给其父窗口,是因为这些事件仅对产生这个事件的窗口才有意义,举例来说,把一个子窗口的重绘事件发送给它的父窗口,其实是没有任何意义的。
6,过滤某个事件
wxWidgets事件处理系统实现了一些和C++中的虚方法非常类似的机制,通过这种机制,你可以通过重载某种基类的事件表的方法来改变基类的默认的事件处理过程。在多数情况下,通过这种方法,你甚至可以改变本地原生控件的默认行为。举例来说,你可以过滤某些按键事件以便本地原生的编辑框控件不处理这些按键。要达到这个目的,你需要实现一个继承自wxTextCtrl的新的类,然后在其事件表中使用EVT_KEY_DOWN事件映射宏。过滤所有的按键事件也许不是你想要的,这时候,你可以通过调用wxEvent::Skip函数来提示事件处理过程对于其中的某些按键事件应该继续寻找其父类的事件表。
总的来说,在wxWidgets中,你应该通过调用事件的Skip方法,而不是通过显式直接调用其父类对应函数的方法来实现对特殊事件的过滤。下面的这个例子演示怎样让你自己的文本框控件只接受"a"到"z"和"A"到"Z"的按键,而忽略其它按键的方法:
void MyTextCtrl::OnChar(wxKeyEvent& event)
{
if ( wxIsalpha( event.KeyCode() ) )
{
//这些按键在可以接受的范围,所以按照正常的流程处理
event.Skip();
}
else
{
// 这些事件不在我们可以接受的范围,所以我们不调用Skip函数
// 由于事件表已经匹配并且没有调用Skip函数,所以事件处理过程不会
// 再继续匹配别的事件表,而是认为事件处理已经结束
wxBell();
}
}
7,挂载事件表你并不一定非要实现继承自某个类的新类,才可以改变它的事件表。对于那些继承自wxWindow的类来说,有另外一种可取代的方法。你可以通过实现一个新的直接继承自wxEvtHandler的新类,然后定义这个新类事件表,然后使用wxWindow::PushEventHandler函数将这个事件表压入到某个窗口类的事件表栈中。最后压入的那个事件表在事件匹配过程中将会被最先匹配,如果在其中没有匹配到对应的事件处理过程,那么栈中以前的事件表仍将被匹配,如此类推。你还可以用wxWindow::PopEventHandler函数来弹出最顶层的事件表,如果你给wxWindow:: PopEventHandler函数传递的是True的参数,那么这个弹出的事件表将被删除。
这种方法可以避免大量的类重载,也使不同的类的实例共享同一个事件表成为可能。
有时候,你需要手动调用窗口类的事件表,这时候你应该使用wxWindow::GetEventHandler方法,而不是直接使用调用这个窗口类的成员函数。虽然wxWindow::GetEventHandler通常返回这个窗口类本身。但是如果你之前曾经使用 PushEventHandler压入另外一个事件表,那么函数将会返回处于最顶层的事件表。因此使用wxWindow:: GetEventHandler函数才可以保证事件被正确的处理。
PushEventHandler的方法通常用来临时的或者永久的改变图形界面的行为。举例来说,加入你想在你的应用程序实现对话框编辑的功能。你可以捕获这个对话框和它的内部控件的所有的鼠标事件,先使用你自己的事件表处理这些事件,来进行类似拖拽控件,改变控件大小以及移动控件动作。这在联机教学中也是很有用技术。你可以在你自己的事件表中过滤收到的事件,如果是可以接受的,则调用wxEvent::Skip函数正常处理
前面我们讨论的事件处理方法,都是静态的事件表,这也是我们处理事件最常用的方式。接下来,我们来讨论一下事件表的动态处理,也就是说在运行期改变事件表的映射关系。使用这种事件映射方法的原因,可能是你想在程序运行的不同时刻使用不同的映射关系,或者因为你使用的那种语言(例如python)不支持静态映射,或者仅仅是因为你更喜欢动态映射。因为动态映射的方法可以使你更精确的控制事件表的细节,你甚至可以单独的将事件表中的某一个条目在运行期打开或者关闭,而前面说的PushEventHandler和PopEventHandler的方法只能针对整个事件表进行处理。除此以外,动态事件处理还允许你在不同的类之间共享事件函数。
和动态事件处理相关的API有两个:wxEvtHandler::Connect和wxEvtHandler::Disconnect。大多数情况下你不需要手动调用wxEvtHandler::Disconnect函数,这个函数将在窗口类被释放的时候自动。下面我们还用前面的MyFrame类来举例说明:
class MyFrame : public wxFrame我们传递给Connect函数的三个参数分别为菜单标识符,事件标识符和事件处理函数指针。要注意这里的事件标识符 wxEVT_COMMAND_MENU_SELECTED不同于前面在静态事件表中用于表示事件映射的宏EVT_MENU,实际上EVT_MENU内部也使用了wxEVT_COMMAND_MENU_SELECTED.EVT_MENU其实也自动包含了用于对事件处理指针类型强制转换的宏 wxCommandEventHandler()。一般说来,如果事件处理函数的参数类型是wxXYZEvent,那么其处理函数的类型就应该用 wxXYZEventHandler宏进行强制转换.
如果我们希望在某个时候中止上面的事件映射,可以使用下面的方法:
frame->Disconnect( wxID_EXIT, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MyFrame::OnQuit) );
frame->Disconnect( wxID_ABOUT, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventFunction(MyFrame::OnAbout) );
9,自定义事件
如果你要使用自定义的事件,你需要下面的几个步骤:4,为每个你的事件类支持的事件定义一个事件映射宏。
我们还是通过例子来让上面这段绕口的话显的更生动一些。假如我们要实现一个新的控件wxFontSelectorCtrl,这个控件将可以显示字体的预览。用户通过点击字体的预览来弹出一个对话框让用户可以更改字体。应用程序也许想拦截这个字体改变事件,因此我们在我们的底层鼠标消息处理过程中将会给应用程序发送一个自定义的字体改变事件。
因此我们需要定义一个新的事件wxFontSelectorCtrlEvent.应用程序可以通过事件映射宏 EVT_FONT_SELECTION_CHANGED(id, func)来增加对这个事件的处理。我们还需要给这个事件定义一个事件类型: wxEVT_COMMAND_FONT_SELECTION_CHANGED. 这样,我们的头文件看上去就象下面的样子:
/*! * Font selector event class */(wxFontSelectorCtrlEvent&);
/*! * Font selector control events and macros for handling them */
BEGIN_DECLARE_EVENT_TYPES()
DECLARE_EVENT_TYPE(wxEVT_COMMAND_FONT_SELECTION_CHANGED, 801)
END_DECLARE_EVENT_TYPES()DEFINE_EVENT_TYPE(wxEVT_COMMAND_FONT_SELECTION_CHANGED)
IMPLEMENT_DYNAMIC_CLASS(wxFontSelectorCtrlEvent, wxNotifyEvent)
然后,在我们的新控件的鼠标处理函数中,可以通过下面的方法在检测到用户选择了一个新的字体的时候,发送一个自定义的事件:wxFontSelectorCtrlEvent event(wxEVT_COMMAND_FONT_SELECTION_CHANGED, GetId());event.SetEventObject(this);
GetEventHandler()->ProcessEvent(event);
现在,在使用我们的新控件的应用程序的代码中就可以通过下面代码来处理我们定义的这个新事件了:BEGIN_EVENT_TABLE(MyDialog, wxDialog)
EVT_FONT_SELECTION_CHANGED(ID_FONTSEL, MyDialog::OnChangeFont)
END_EVENT_TABLE()
void MyDialog::OnChangeFont(wxFontSelectorCtrlEvent& event)
{ // 字体已经更改了,可以作一些必要的处理。 ...}
上面用到的事件标识符801在最新的版本中已经没有用处了,之所以这样写只是为了兼容wxWidgets2.4的版本。让我们再来看一眼事件映射宏的定义:#define EVT_FONT_SELECTION_CHANGED(id, fn) DECLARE_EVENT_TABLE_ENTRY( wxEVT_COMMAND_FONT_SELECTION_CHANGED, id, -1, (wxObjectEventFunction)
(wxEventFunction)(wxFontSelectorCtrlEventFunction) & fn,
(wxObject *) NULL ),
这个宏的作用其实是把一个五元组放入到一个数组中,所以这段代码的语法看上去是奇怪了一些,这个五元组的意义分别解释如下:窗口标识符是在事件系统中用来唯一确定窗口的整数。事实上,在整个应用程序的范围内,窗口标识符不必一定是唯一的,而只要在某个固定的上下文(比如说,在一个frame窗口和它的所有子窗口)内是唯一的就可以了。举例来说:你可以在无数个对话框中使用wxID_OK这个标识符,只要在某个对话框内不要重复使用就可以了。
如果在窗口的构造函数中使用wxID_ANY作为其标识符,则意味着你希望wxWidgets自动为你生成一个标识符。这或者是因为你不关心这个标识符的值,或者是因为这个窗口不需要处理任何事件,或者是因为你将在同一个地方处理所有的事件。如果是最后一种情况,在使用 wxEvtHandler::Connect函数或者在静态事件表中,你应该使用wxID_ANY作为窗口的标识符。wxWidgets自动创建的标识符是总是一个负数,所以永远不会和用户定义的窗口标识符重复,用户定义的窗口标识符只能是正整数。
下表列举了wxWidgets提供的一些标准的标识符。你应该尽可能的使用这些标识符,这是由于下面一些原因。某些系统会给特定的标识符提供一些小图片(例如GTK+系统上的OK和取消按钮)或者提供默认的处理函数(例如自动产生wxID_CANCEL事件来响应Escape键)。在 Mac OS X系统上,wxID_ABOUT, wxID_PREFERENCES和wxID_EXIT菜单项也有特别的处理。另外一些wxWidgets的控件也会自动处理标识符为 wxID_COPY, wxID_PASTE或 wxID_UNDO等的一些菜单或者按钮的命令。
标识符名称 描述