Главная :: Программы для программирования :: wxWidgets :: Перевод книги "Programming with wxWidgets" :: Глава III – Обработка сообщений. Часть 3
Можно ли сообщение "Программа выполнила недопустимую операцию... обратитесь к разработчику" считать официальным вызовом в США?

Глава III – Обработка сообщений. Часть 3

Игнорирование сообщения

Система обработки сообщений в wxWidgets реализует нечто похожее на виртуальные методы обычного C++, а это означает, что возможно переопределить поведение класса, перегружая его таблицу обработки сообщений. В большинстве случаев даже возможно изменение поведения родных элементов управления. Например, возможно фильтровать выбранные нажатия на клавишу, которые система посылает текстовому элементу управления с помощью создания наследника от wxTextCtrl и определения обработчика для нажатий клавиш, используя EVT_KEY_DOWN. Этого вполне достаточно, чтобы перехватить события от любой клавиши, посылаемые родному элементу управления, что делает не совсем то, что нам нужно. В этом случае обработчик сообщения может вызвать метод wxEvent::Skip, чтобы показать, что поиск для обработчика сообщений должен быть продолжен. Таким образом, вместо того, чтобы непосредственно вызвать метод базового класса (как вы бы сделали в случае использования виртуальных функция языка C++), вы просто должны вызвать метод Skip у вашего объекта сообщения.

Для примера реализуем текстовый элемент управления, который принимает только символы от a до z и от A до Z:

void MyTextCtrl::OnChar(wxKeyEvent& event) { if ( wxIsalpha( event.KeyCode() ) ) { // Клавиша допустима, поэтому обрабатываем как обычно event.Skip(); } else { // Недопустимый символ. Мы не вызываем event.Skip(), // поэтому сообщение не обрабатывается нигде более wxBell(); } }

Подключаемые обработчики событий

Вместо того, чтобы наследовать новый класс от класса окна для обработки сообщений вы можете поступить проще. Создайте новый класс-наследник от wxEvtHandler, определите в нем соответствующую таблицу сообщений, а потом вызовете wxWindow::PushEventHandler для добавления этого объекта в стек обработчиков сообщений для данного окна. Теперь ваш новый обработчик сообщений перехватывает все приходящие окну сообщению, и если они в нем не обрабатываются, то ищется следующий объект в стеке и так далее. Метод wxWindow::PopEventHandler используется, чтобы извлечь обработчик сообщений с вершины стека. Если передать этому методу true, то извлеченный объект будет уничтожен.

Используя указанный прием вы можете избежать многочисленных наследований классов и теоретически использовать одинаковый обработчик для обработки сообщений от экземпляров различных классов.

Обычно значение возвращаемое wxWindow::GetEventHandler является указателем на само окно, однако при использовании PushEventHandler это естественно будет не так. Даже если вы просто хотите вручную вызвать обработчик сообщения для окна всегда используйте функцию GetEventHandler, чтобы получить самый верхний обработчик в стеке и используйте это значение, чтобы быть уверенным в корректной обработке вашего сообщения.

Использование PushEventHandler может помочь временно или навсегда изменить поведение интерфейса приложения. Например, вы можете захотеть реализовать в своем приложении с его помощью встроенный редактор для диалогов. Для этого вам необходимо перехватывать все сообщения от мыши для существующего диалога и всех его элементов управления, обработать их, а после всех изменений можно восстановить обычное поведение мыши. Технология также может быть полезна для организации интерактивных учебников, где вы проводите пользователя через серию шагов и не хотите, чтобы он отклонялся от последовательности шагов урока. Таким образом вы можете, например, перехватывать сообщения от кнопок и окон и, если всё делается верно, передавать сообщения исходным обработчикам сообщений используя wxEvent::Skip. Сообщения не перехватываемые вашим обработчиком сообщений передаются через таблицу сообщений окна.

Динамические обработчики сообщений

До этого мы рассматривали обработчики сообщений в терминах статических таблиц сообщений потому, что это самый распространенный метод обработки. Однако есть возможность определять связь между сообщением и его обработчиком динамически. Это может потребоваться, если вам необходимо применять различные механизмы обработки сообщений на разных этапах выполнения программы, или потому что вы используете другой язык (такой как Python), который не поддерживает статические таблицы сообщений, или вам просто нравится писать оригинальные программы.

Динамические обработчики сообщений позволяют большую гибкость, так как позволяют оперировать отдельными сообщениями, тогда как PushEventHandler и PopEventHandler оперируют целыми таблицами сообщений. Кроме того, динамические обработчики позволяют разделать единую функцию обработки сообщений между объектами разных типов.

Для работы с динамическими таблицами сообщений применяются две функции: wxEvtHandler::Connect и wxEvtHandler::Disconnect. На практике вам достаточно редко придется вызывать wxEvtHandler::Disconnect, так как разъединение автоматически происходит при уничтожении объекта окна.

Рассмотрим простейший класс фрейма с двумя обработчиками сообщений:

// Объявляем наш класс для фрейма class MyFrame : public wxFrame { public: // Конструктор MyFrame(const wxString& title); // Обработчики сообщений void OnQuit(wxCommandEvent& event); void OnAbout(wxCommandEvent& event); private: };

Обратите внимание, что мы не используем макрос DECLARE_EVENT_TABLE. Чтобы динамически определить обработчику сообщений мы добавим несколько строчек в функцию OnInit нашего класса:

frame->Connect( wxID_EXIT, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MyFrame::OnQuit) ); frame->Connect( wxID_ABOUT, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MyFrame::OnAbout) );

Мы передаем методу Connect идентификатор окна, идентификатор сообщения и, наконец, указатель на функцию его обработки. Обратите внимание, что имя идентификатора сообщения (wxEVT_COMMAND_MENU_SELECTED) отличается от имени макроса для таблицы сообщений (EVT_MENU). Макрос таблицы сообщений в своем коде использует данный идентификатор сообщения. Макрос wxCommandEventHandler, в который помещена функция необходим для корректного преобразования компилятором типа функции для использования в таблице сообщений. В общем случае, если у вас есть обработчик сообщения, который получает сообщение wxXYZEvent, то при вызове функции Connect вам необходимо заключить вашу функцию в макрос wxXYZEventHandler.

Если нам понадобится удалить связь между сообщением и обработчиком сообщений, то для этого необходимо вызвать функцию wxEvtHandler::Disconnect:

frame->Disconnect( wxID_EXIT, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MyFrame::OnQuit) ); frame->Disconnect( wxID_ABOUT, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventFunction(MyFrame::OnAbout) );