一、事件拦截(wxApp::FilterEvent())
wxApp::FilterEvent()
函数是wxWidgets中的一个可选重载函数,它允许您在事件到达目标对象之前对事件进行预处理。这个函数可以用来在事件分发之前修改事件,或者直接处理并阻止它进一步传播。
函数原型如下:
/**
* @param event 要处理的事件对象的引用。可以是一个wxEvent的派生类,例如wxCommandEvent、wxMouseEvent等
* @retval wxEventFilter::Event_Skip 继续处理事件,将事件传递给目标对象(默认实现返回的就是这个值)
* @retval wxEventFilter::Event_Ignore 已经处理了事件,不需要进一步传递
* @retval wxEventFilter::Event_Processed 停止处理事件,不要传递给目标对象
*/
virtual int wxAppConsole::FilterEvent(wxEvent &event);
要使用 wxAppConsole::FilterEvent()
,你就需要在 wxAPP
中重载它,wxAPP
其实就是程序入口。废话少说,上才艺:
#include <wx/wx.h>
class MyFrame : public wxFrame
{
public:
explicit MyFrame(const wxString &title)
: wxFrame(nullptr, wxID_ANY, title, wxDefaultPosition, wxSize(400, 250))
{
new wxButton(this, wxID_ANY, "Click me!", wxDefaultPosition, wxDefaultSize); // 注意,这个按钮会占满整个窗口,原因我已经在之前的文章中有说
}
};
class MyApp : public wxApp
{
public:
bool OnInit() override
{
auto *frame = new MyFrame("FilterEvent Example");
frame->Show(true);
return true;
}
int FilterEvent(wxEvent &event) override
{
// 检查是否是按钮点击事件
if (event.GetEventType() == wxEVT_BUTTON) {
wxMessageBox("Button clicked!", "Information", wxOK | wxICON_INFORMATION);
return wxEventFilter::Event_Ignore; // 表示事件已经处理,不需要进一步传递
}
return wxEventFilter::Event_Skip; // 对于其他事件,继续处理并传递给目标对象
}
};
IMPLEMENT_APP(MyApp)
在这个示例中,我们重载了 MyApp
类的 FilterEvent()
函数,检查事件类型是否为 wxEVT_BUTTON
。如果是按钮点击事件,我们显示一个消息框,并返回 wxEventFilter::Event_Ignore
(也就是0),表示事件已经处理,不需要进一步传递。对于其他事件,我们返回 wxEventFilter::Event_Skip
,让它们继续传递并被相应的处理器处理。有图有真相:
二、事件处理流程
说到事件处理流程,就不得不说一下 wxEvtHandler::ProcessEvent()
函数。它就是事件处理的入口,甚至连上面所说的 FilterEvent()
函数也是在它内部被调用的,看图:
很明显,FilterEvent()
函数是被 wxEvtHandler::ProcessEvent()
调用的。
其实官方也很明确说到,一般情况下我们不需要调用这个函数(它是个虚函数),除非我们在开发一个新功能(如新控件)并定义新的事件类型时,就可能需要调用它。
需要注意的是,我们通常不需要重写 ProcessEvent()
函数来自定义事件处理,重写 TryBefore()
和 TryAfter()
函数通常就足够了。
事件处理的顺序如下:
- 调用
wxApp::FilterEvent()
。如果返回值不是-1(默认,等同于wxEventFilter::Event_Skip
),则处理停止。 - 调用
TryBefore()
(如果对象是wxWindow且有关联的验证器,将会在这一步被处理)。如果返回true,则函数退出。 - 如果对象被禁用(通过调用
wxEvtHandler::SetEvtHandlerEnabled()
),则跳到第7步。 - 搜索使用
Bind<>()
绑定的动态事件表。按照从最近绑定到最早绑定的顺序查找。如果找到处理程序,执行它并返回true。但是,如果处理程序使用了wxEvent::Skip()
表示没有处理事件,则继续搜索。 - 搜索使用事件表宏绑定的静态事件表。按照源代码中事件表宏出现的顺序查找(就是我们代码中的先后顺序)。如果查找失败,尝试基类事件表,依此类推。如果找到处理程序,应用前一步的相同逻辑。
- 将搜索应用于整个事件处理链。链的长度通常为1。使用
wxEvtHandler::SetNextHandler()
可以构建这样的链。如果链中的任何处理器返回true,函数将退出。 - 调用
TryAfter()
。对于wxWindow
对象,这可能会将事件传播(默认仅wxCommandEvent
派生的事件传播)到窗口的父级(递归,又从第2步开始)。 - 如果事件仍未处理,将调用
wxTheApp
(指向单例wxApp
对象的全局指针)对象上的ProcessEvent()
作为最后一步。
事件篇Ⅲ 至此完毕,欢迎大家指正!还请大家点点赞,给我点动力~~