[Trick] Обработка сообщений
От: remark Россия http://www.1024cores.net/
Дата: 06.12.06 06:18
Оценка: 173 (13)
Хочу поделиться трюком, который сам с успехом применяю уже достаточное время. Надюсь кто-то тоже найдёт его полезным. Ну и, конечно, если у кого будут какие замечания и комменты, то обсудим

Проблема

Так утомительно писать все эти:

afx_msg void OnButton1();
afx_msg void OnButton2();
...
afx_msg void OnButtonN();

 
ON_BN_CLICKED(IDC_BUTTON1, OnButton1)
ON_BN_CLICKED(IDC_BUTTON2, OnButton2)
...
ON_BN_CLICKED(IDC_BUTTONN, OnButtonN)


Переключаться между h и cpp, перекомпилировать все связанные файлы, особенно если по стандарту эти обработчики надо ещё комментировать...

Решение

Что я делаю:

class MyDlg : public CDialog
{
...
  /** Обработчик всех команд
  */
  template<UINT ID> afx_msg void OnCommand();
};


В карте сообщений это теперь выглядит так:

ON_BN_CLICKED(IDC_BUTTON1, OnCommand<IDC_BUTTON1>)
ON_BN_CLICKED(IDC_BUTTON2, OnCommand<IDC_BUTTON2>)


И обработчики:

template<> void MyDlg::OnCommand<IDC_BUTTON1>()
{
...
}

template<> void MyDlg::OnCommand<IDC_BUTTON2>()
{
...
}


Что получаем

+ При добавлении и удалении обработчиков не надо менять заголочный файл и перекомпилировать связанные файлы
+ Не надо писать формальные комментарии для функций в заголочной файле, т.к. функция реально одна! (ну что тут комментировать, даже ёжику понятно, что OnCommand<IDC_BUTTON1> — это обработчик комманды IDC_BUTTON1)
+ Ещё один плюс не так очевиден, но очень приятен: достаточно часто встаёт задача по ID комманды найти её обработчик и наоборот. Обычно приходится вначале по идентификатору ID_SEND_REQUEST находить в карте сообщений функцию OnRequestSend() И потом находить тело функции OnRequestSend(). Теперь же можно по идентификатору команды сразу найти обработчик и наоборот, без лишнего шага
— Карту сообщений надо перенести в самый низ файла, что бы она было после всех обработчиков. Ну это не очень серьёзный минус. Я ни с какими реальными неудобствами не столкнулся.


Идём дальше

Во-первых, такие обработчики можно применять не только для команд. Ничто не мешает написать:

/**    Обработчик обновления элемента пользовательского интерфейса
 *    @template ID Идентификатор команды, для которой происходит обновление
 */
template<UINT ID> afx_msg void OnUpdateUI(CCmdUI* cmdUI);


И даже:

/**    Обработчик уведомления от контрола
 *    @template ControlID Идентификатор контрола, от которого приходит уведомление
 *    @template NotifyCode Код уведомления
 */
template<UINT ControlID, UINT NotifyCode>
void OnNotify(NMHDR* pNMHDR, LRESULT* pResult);


Ещё дальше

Зачастую в обработчиках команд должен присутствовать некий общий код — например, логирование и обработка исключений. Делаем следующее. Создаём базовый класс для диалогов (ну я надеюсь, Вы и так используете общие базовые классы для диалогов в приложениях ):

template<typename DerivedDlg>
class MyAppDialog : public CDialog
{
...
protected:
    // Обработчик всех комманд с общей частью
    template<UINT ID> afx_msg void OnCommandHandler()
    {
        LOG << "Command: " << ID;
        try
        {
            // Вот здесь вызываем конкретный обработчик
            static_cast<DerivedDlg*>(this)->OnCommand<ID>();
            LOG << "Success";
        }
        catch (CException* ex)
        {
            LOG << "Exception...";
            AfxMessageBox("...");
        }
        catch (const MyAppException& ex)
        {
            LOG << "Exception...";
            AfxMessageBox("...");
        }
        catch (const std::exception& ex)
        {
            LOG << "Exception...";
            AfxMessageBox("...");
        }
        catch (...)
        {
            LOG << "Exception...";
            AfxMessageBox("...");
        }
    }
};


Диалоги теперь наследуем от этого класса:

class MyDlg: public MyAppDialog<MyDlg>


И в карте сообщений прописываем уже базовый обработчик:

ON_BN_CLICKED(IDC_BUTTON1, OnCommandHandler<IDC_BUTTON1>)
ON_BN_CLICKED(IDC_BUTTON2, OnCommandHandler<IDC_BUTTON2>)


Всё! Все команды теперь будут автоматически содержать общую часть, её больше не надо дублировать!

Если у кого будут какие замечания и предложения по улучшению подхода (улучшить ещё можно, я уверен) буду рад обсудить.


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.