Сообщений 0 Оценка 1 Оценить |
Собственно говоря, класс CNotifyIcon изначально был написан для использования в приложениях, использующих "голый" Win32 API, но ничто не препятствует использовать его и в MFC-приложениях. Рассмотрим, каким образом это реализуется.
Для начала напомним, каким образом используется CNotifyIcon в WinAPI-приложениях. В нужном месте (согласно логики программы) создается экземпляр объекта класса, которому через параметры конструктора передается хэндл окна, создающего иконку в System Area (ему впоследствии система будет слать сообщения о действиях пользователя) и обобщенный идентификатор ресурсов (если ресурсы модуля, хэндл которого передается в одном из следующих параметров, содержат иконку, меню и текст подсказки с указанным идентификатором, то они будут загружены объектом класса). Два следующих параметра дают возможность явно задать хэндл модуля, содержащего ресурсы, и строку подсказки. При их отсутствии ресурсы будут загружаться из модуля приложения.
Класс имеет конструктор с одним параметром, который строит "пустой" объект, не выводящий в System Area ничего. Чтобы вывести при помощи такого объекта иконку, нужно для него вызвать метод Modify(). Кроме того, класс содержит еще два конструктра, в которых имеется возможность раздельно задать идентификаторы ресурсов меню, иконки и строки подсказки. |
Отдельно необходимо сказать об идентификаторе нотификационного сообщения m_uRegisteredMessage. Для оповещения о действиях пользователя, система использует посылку окну-создателю сообщений с идентификатором, переданным системе при создании иконки. Вы можете самостоятельно выбрать его значение, а можете передать значение, равное нулю (нуль передается по-молчанию и в случае, когда этот параметр не указан). В последнем случае код класса самостоятельно назначит идентификатор.
Для того, чтобы код класса имел возможность обработать сообщения с указанным идентификатором (класс самостоятельно реализует вызов контекстного меню), необходимо обеспечить маршрутизацию оконных сообщений окна коду класса. Делается это посредством вызова метода Dispatch() в оконной процедуре (либо, в зависимости от типа приложения, в процедуре диалога).
Для наглядности приведем пример фрагмента клиентского кода, реализующего все вышеперeчисленные манипуляции.
BOOL CALLBACK DlgProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) { static CNotifyIcon* pNI = NULL; // все сообщения прокачиваются через Dispatch() if( pNI ) pNI->Dispatch( hDlg, msg, wParam, lParam ); switch( msg ) { case WM_INITDIALOG: // создадим экземпляр класса на основе ресурсов с единым // идентификатором, выбор идентификатора нотификационного // сообщения предоставим классу pNI = new CNotifyIcon( hDlg, ID_NOTIFYICON_RES ); . . . break; case WM_COMMAND: switch( LOWORD( wParam ) ) { case IDCANCEL: if( BN_CLICKED == HIWORD( wParam) ) { delete pNI; pNI = NULL; // чтобы предотвратить вызов Dispatch() для // недействительного уже указателя pNI EndDialog( hDlg, 0 ); } } break; } return 0; } |
Вот и настало время вспомнить, собственно, о теме разговора. Статический указатель на экземпляр класса CNotifyIcon станет открытым членом класса, скорее всего класса главного окна - наследника CDialog или CFrameWnd, создание экземпляра класса переместится в один из инициализационных методов. А вот вызов Dispatch() не имеет альтернативы - его придется поместить в перекрытый метод функции окна, которое зарегистрируется получателем нотификационных сообщений - опять таки, скорее всего это будет главное окно приложения. Хотя нет, альтернатива все же есть - вместо перекрытой WindowProc() для наших целей в большинстве случаев подойдет и DefWindowProc(). О редких исключениях мы можем поговорить отдельно (например посредством mail :-)), тем более что основной способ всегда в вашем распоряжении.
Таким образом приведенный выше фрагмент кода в интерпретации MFC будет выглядеть следующим образом:
class CMyDialog : public CDialog { public: . . . CNotifyIcon* m_pNI; . . . }; CMyDialog::CMyDialog(. . .) { m_pNI = NULL; // это нужно сделать обязательно, чтобы предотвратить вызовы // Dispatch() до корректной инициализации m_pNI } BOOL CMyDialog::OnInitDialog() { CDialog::OnInitDialog(); . . . m_pNI = new CNotifyIcon( m_hWnd, ID_NOTIFYICON_RES ); . . . return TRUE; } void CMyDialog::OnCancel() { delete m_pNI; m_pNI = NULL; // чтобы предотвратить вызовы Dispatch() // для недействительного указателя m_pNI CDialog::OnCancel(); } LRESULT CMyDialog::WindowProc( UINT msg, WPARAM wParam, LPARAM lParam ) { if( m_pNI ) m_pNI->Dispatch( m_hWnd, msg, wParam, lParam ); return CDialog::WindowProc( msg, wParam, lParam ); } |
Адаптировать приведенный код к другому типу MFC-проектов, на мой взгляд, не составит труда. Совершенно необязательно делать указатель на экземпляр класса CNotifyIcon членом класса главного окна, главное - обеспечить его видимость в тех участках кода, где вы будете вызывать методы класса.
На этом можно было бы и закончить. Но может быть у кого-то из читающих эти строки закралась мысль: "А так ли необходимо прокачивать всю груду оконных сообщений через Dispatch()?"
Достаточно ознакомиться с исходными текстами класса, чтобы убедиться, что накладные расходы вызова метода Dispatch() минимальны. Метод выполняет несколько проверок и возвращает управление программе в случае, если только это не сообщение от иконки о том, что пользователь отпустил правую кнопку мыши, либо если это не сообщение системы о пересоздании панели задач. В последнем случае выполнится восстановление иконки в System Area в том виде, как она выглядела в момент закрытия панели задач.
И именно для тех, кому подобная реализация покажется неоптимальной, метод Dispatch() сделан виртуальным - наследуйтесь от класса и определяйте собственную реализацию Dispatch() (хотя при доступных исходниках ничто не помешает вам внести изменения прямо в них, однако мне все же этого не хотелось бы :-)). Именно этим способом автор действовал при написании класса CNotifyIconEx, расширяющего возможности по добавлению пользовательских обработчиков сообщений мыши.
Сообщений 0 Оценка 1 Оценить |