From: Vadim Zeitlin Date: Tue, 15 Apr 2008 23:17:11 +0000 (+0000) Subject: added a section about Connect(), improve/streamline the one about event tables X-Git-Url: https://git.saurik.com/wxWidgets.git/commitdiff_plain/722f74cec895bbc284ba7b7a7213ee5cf2a1ab95 added a section about Connect(), improve/streamline the one about event tables git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@53185 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- diff --git a/docs/doxygen/overviews/eventhandling.h b/docs/doxygen/overviews/eventhandling.h index 3a0b0d9155..9f94ba6c43 100644 --- a/docs/doxygen/overviews/eventhandling.h +++ b/docs/doxygen/overviews/eventhandling.h @@ -13,6 +13,8 @@ Classes: wxEvtHandler, wxWindow, wxEvent @li @ref overview_eventhandling_introduction +@li @ref overview_eventhandling_eventtables +@li @ref overview_eventhandling_connect @li @ref overview_eventhandling_processing @li @ref overview_eventhandling_prog @li @ref overview_eventhandling_pluggable @@ -26,77 +28,260 @@ Classes: wxEvtHandler, wxWindow, wxEvent @section overview_eventhandling_introduction Introduction -Before version 2.0 of wxWidgets, events were handled by the application -either by supplying callback functions, or by overriding virtual member -functions such as @b OnSize. +There are two principal ways to handle events in wxWidgets. One of them uses +event table macros and allows to define the connection between events +and their handlers only statically, i.e. during program compilation. The other +one uses wxEvtHandler::Connect() call and can be used to connect, and +disconnect, the handlers dynamically, i.e. during run-time depending on some +conditions. It also allows directly connecting the events of one object to a +handler method in another object while the static event tables can only handle +events in the object where they are defined so using Connect() is more flexible +than using the event tables. On the other hand, event tables are more succinct +and centralize all event handlers connection in one place. You can either +choose a single approach which you find preferable or freely combine both +methods in your program in different classes or even in one and the same class, +although this is probably sufficiently confusing to be a bad idea. + +But before you make this choice, let us discuss these two ways in some more +details: in the next section we provide a short introduction to handling the +events using the event tables, please see @ref overview_eventhandling_connect +for the discussion of Connect(). + +@section overview_eventhandling_eventtables Event Handling with Event Tables + +To use an event table you must first decide in which class you wish to +handle the events. The only requirement imposed by wxWidgets is that this class +must derive from wxEvtHandler and so, considering that wxWindow derives from +it, any classes representing windows can handle events. Simple events such as +menu commands are usually processed at the level of a top-level window +containing the menu, so let's suppose that you need to handle some events in @c +MyFrame class deriving from wxFrame. + +First thing to do is to define one or more event handlers. They +are just simple (non-virtual) methods of the class which take as a parameter a +reference to an object of wxEvent-derived class and have no return value (any +return information is passed via the argument, which is why it is non-const). +You also need to insert a macro -From wxWidgets 2.0, @e event tables are used instead, with a few exceptions. -An event table is placed in an implementation file to tell wxWidgets how to map -events to member functions. These member functions are not virtual functions, but -they are all similar in form: they take a single wxEvent-derived argument, -and have a void return type. -Here's an example of an event table. +@code +DECLARE_EVENT_TABLE() +@endcode + +somewhere in the class declaration. It doesn't matter where does it occur but +it's customary to put it at the end of it because the macro changes the access +type internally and so it's safest if there is nothing that follows it. So the +full class declaration might look like this: + +@code +class MyFrame : public wxFrame +{ +public: + MyFrame(...) : wxFrame(...) { } + + ... + +protected: + int m_whatever; + +private: + // notice that as the event handlers normally are not called from outside + // the class, they normally be private, in particular they don't need at + // all to be public + void OnExit(wxCommandEvent& event); + void OnButton1(wxCommandEvent& event); + void OnSize(wxSizeEvent& event); + + // it's common to call the event handlers OnSomething() but there is no + // obligation to it, this one is an event handler too: + void DoTest(wxCommandEvent& event); + + DECLARE_EVENT_TABLE() +}; +@endcode + +Next the event table must be defined and, as any definition, it must be placed +in an implementation file to tell. The event table tells wxWidgets how to map +events to member functions and in our example it could look like this: @code BEGIN_EVENT_TABLE(MyFrame, wxFrame) -EVT_MENU(wxID_EXIT, MyFrame::OnExit) -EVT_MENU(DO_TEST, MyFrame::DoTest) -EVT_SIZE(MyFrame::OnSize) -EVT_BUTTON(BUTTON1, MyFrame::OnButton1) + EVT_MENU(wxID_EXIT, MyFrame::OnExit) + EVT_MENU(DO_TEST, MyFrame::DoTest) + EVT_SIZE(MyFrame::OnSize) + EVT_BUTTON(BUTTON1, MyFrame::OnButton1) END_EVENT_TABLE() @endcode -The first two entries map menu commands to two different member functions. The -EVT_SIZE macro doesn't need a window identifier, since normally you are only -interested in the current window's size events. +Notice that you must mention a method you want to use for the event handling in +the event table definition, just defining it in MyFrame class is @e not enough. + +Let us now look at the details of this definition: the first line means that we +are defining the event table for MyFrame class and that its base class is +wxFrame, so events not processed by MyFrame will, by default, be handled to +wxFrame. The next four lines define connections of individual events to their +handlers: the first two of them map menu commands from the items with the +identifiers specified as the first macro parameter to two different member +functions. In the next one, @c EVT_SIZE means that any changes in the size of +the frame will result in calling OnSize() method. Note that this macro doesn't +need a window identifier, since normally you are only interested in the current +window's size events. The EVT_BUTTON macro demonstrates that the originating event does not have to come from the window class implementing the event table -- if the event source is a button within a panel within a frame, this will still work, because event -tables are searched up through the hierarchy of windows for the command events. -In this case, the button's event table will be searched, then the parent -panel's, then the frame's. - -As mentioned before, the member functions that handle events do not have to be -virtual. Indeed, the member functions should not be virtual as the event -handler ignores that the functions are virtual, i.e. overriding a virtual -member function in a derived class will not have any effect. These member -functions take an event argument, and the class of event differs according to -the type of event and the class of the originating window. For size events, -wxSizeEvent is used. For menu commands and most control commands -(such as button presses), wxCommandEvent is used. When controls get more -complicated, then specific event classes are used, such as wxTreeEvent for +tables are searched up through the hierarchy of windows for the command events +(but only command events, so you can't catch mouse move events in a child +control in the parent window in the same way because wxMouseEvent doesn't +derive from wxCommandEvent, see below for how you can do it). In this case, the +button's event table will be searched, then the parent panel's, then the +frame's. + +Finally, you need to implement the event handlers. As mentioned before, all +event handlers take a wxEvent-derived argument whose exact class differs +according to the type of event and the class of the originating window. For +size events, wxSizeEvent is used. For menu commands and most control commands +(such as button presses), wxCommandEvent is used. And when controls get more +complicated, more specific wxCommandEvent-derived event classes providing +additional control-specific information can be used, such as wxTreeEvent for events from wxTreeCtrl windows. -As well as the event table in the implementation file, there must also be a -DECLARE_EVENT_TABLE macro somewhere in the class declaration. For example: +In the simplest possible case an event handler may not use the @c event +parameter at all, e.g. @code -class MyFrame : public wxFrame +void MyFrame::OnExit(wxCommandEvent&) { -public: -... -void OnExit(wxCommandEvent& event); -void OnSize(wxSizeEvent& event); + // when the user selects "Exit" from the menu we should close + Close(true); +} +@endcode -protected: -int m_count; -... +In other cases you may need some information carried by the @c event argument, +as in: -DECLARE_EVENT_TABLE() -}; +@code +void MyFrame::OnSize(wxSizeEvent& event) +{ + wxSize size = event.GetSize(); + + ... update the frame using the new size ... +} @endcode -Note that this macro may occur in any section of the class (public, protected -or private) but that it is probably better to insert it at the end, as shown, -because this macro implicitly changes the access to protected which may be -quite unexpected if there is anything following it. +You will find the details about the event table macros and the corresponding +wxEvent-derived classes in the discussion of each control generating these +events. + + +@section overview_eventhandling_connect Dynamic Event Handling + +As with the event tables, you need to decide in which class do you intend to +handle the events first and, also as before, this class must still derive from +wxEvtHandler (usually indirectly via wxWindow), see the declaration of MyFrame +in the previous section. However the similarities end here and both the syntax +and the possibilities of this way of handling events in this way are rather +different. + +Let us start by looking at the syntax: the first obvious difference is that you +don't need to use neither @c DECLARE_EVENT_TABLE() nor @c BEGIN_EVENT_TABLE and +associated macros any more. Instead, in any place in your code, but usually in +the code of the class defining the handlers itself (and definitely not in the +global scope as with the event tables), you should call its Connect() method +like this: -Finally, if you don't like using macros for static initialization of the event -tables you may also use wxEvtHandler::Connect to -connect the events to the handlers dynamically, during run-time. See the -@ref page_samples_event for an example of doing it. +@code +MyFrame::MyFrame(...) +{ + Connect(wxID_EXIT, wxEVT_COMMAND_MENU_SELECTED, + wxCommandEventHandler(MyFrame::OnExit)); +} +@endcode +This class should be self-explanatory except for wxCommandEventHandler part: +this is a macro which ensures that the method is of correct type by using +static_cast in the same way as event table macros do it inside them. + +Now let us describe the semantic differences: + + +To summarize, using Connect() requires slightly more typing but is much more +flexible than using static event tables so don't hesitate to use it when you +need this extra power. On the other hand, event tables are still perfectly fine +in simple situations where this extra flexibility is not needed. @section overview_eventhandling_processing How Events are Processed