Хочу поделиться трюком, который сам с успехом применяю уже достаточное время. Надюсь кто-то тоже найдёт его полезным. Ну и, конечно, если у кого будут какие замечания и комменты, то обсудим
Так утомительно писать все эти:
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>)
Всё! Все команды теперь будут автоматически содержать общую часть, её больше не надо дублировать!
Если у кого будут какие замечания и предложения по улучшению подхода (улучшить ещё можно, я уверен) буду рад обсудить.