20.2 更改事件处理机制

在通常情况下,wxWidgets将事件发送到产生这个事件的窗口(或者别的事件处理器).对于命令事件,还将以特定的方式遍历整个窗口继承树(详情参考附录H,"wxWidgets的事件处理机制")来处理.举例来说,如果你点击工具条上的任何一个工具按钮,产生的事件将首先发送给这个工具按钮的事件表处理,然后是包含这个工具条的frame窗口的时间表,然后是整个应用程序类的事件表.通常情况下,这样的作法是满足要求的,但是如果有时候,你希望超过一个控件都可以使用工具条上的拷贝命令的时候,就会有一些问题,比如,你的主程序中有一个主窗口(假设是一个绘画程序)和一个文本编辑框,这个文本编辑框可能永远也无法处理工具条上的拷贝命令,因为这个命令并不会调用这个编辑框的事件处理函数.在这种情况下,你可能希望事件能够首先交给当前处于活动状态的控件处理,然后再按照正常的处理顺序执行.这样,如果当前的活动控件内部实现了针对这个事件的处理函数(比如wxID_COPY),那么这个函数就将被调用,否则就在窗口继承树中向上查找对应的处理函数,直到它找到一个这样的函数.这种命令总是针对当前活动控件的作法会更符合用户的使用习惯.

我们可以通过下面的方法重载主窗口的ProcessEvent函数,以便拦截命令事件并将其首先交给当前活动的控件处理,如下所示:

bool MainFrame::ProcessEvent(wxEvent& event)
{
    static wxEvent* s_lastEvent = NULL;
    // 避免死循环
    if (& event == s_lastEvent)
        return false;
    if (event.IsCommandEvent() &&
       !event.IsKindOf(CLASSINFO(wxChildFocusEvent)) &&
       !event.IsKindOf(CLASSINFO(wxContextMenuEvent)))
    {
        s_lastEvent = & event;
        wxControl *focusWin = wxDynamicCast(FindFocus(), wxControl);
        bool success = false;
        if (focusWin)
            success = focusWin->GetEventHandler()
                                ->ProcessEvent(event);
        if (!success)
            success = wxFrame::ProcessEvent(event);
        s_lastEvent = NULL;
        return success;
    }
    else
    {
        return wxFrame::ProcessEvent(event);
    }
}

就目前的情况来看,这种作法在那些当前活动控件可能为一个wxTextCtrl控件的时候显的更有用(在大多数平台上), wxWidgets为这种控件实现了多种内置的命令,包括wxID_COPY, wxID_CUT, wxID_PASTE, wxID_UNDO和wxID_REDO,还实现了一些默认的用户界面行为.不过,你也可以给任意的控件通过实现其派生类的方式增加这些默认的事件处理函数,比如说wxStyledTextCtrl控件(参考examples/chap20/pipedprocess中的例子,这个例子为 wxStyledTextCtrl控件提供了这种增强处理).