Обробка повідомлень в MFC
Реєстрація класу вікна та його створення
Реєстрація та створення основного вікна програми на основі класів MFC майже нічим не відрізняється від подібної процедури на основі Win32 API, що розглядалася раніше. Основні відмінності спостерігаються при використання архітектури “Документ/вигляд”, яка буде розглянута на наступній лекції.
Основний цикл обробки повідомлень реалізований в методі Run() класу CwinnApp. Він діє аналогічно до розглянутого раніше циклу обробки повідомлень Win32 API за винятком запуску методу OnIdle() для виконання фонових операцій коли черга повідомлень порожня.
Категорії повідомлень MFC. У бібліотеці MFC прийнята класифікація повідомлень відмінна від Win32 API. Усі повідомлення розділено на три категорії:
- повідомлення Windows;
- повідомлення елементів керування;
- командні повідомлення (команди).
Цей розподіл зумовлений, що повідомлення кожної з категорій обробляються по-різному. В першу категорію входять повідомлення імена яких починаються з префіксу WM_, за винятком WM_COMMAND. Ці повідомлення обробляються вікнами і мають відповідні параметри які визначають алгоритм обробки. Сюди входять і апаратні повідомлення.
Друга категорія повідомлення (ивещения – notification message) від елементів керування та інших дочірніх вікон, що направляються батьківським вікнам. Наприклад, елемент керування посилає повідомлення своєму батьківському вікну, коли необхідно обновити інформацію про елементи списку. Батьківське вікно заповнює відповідну структуру та відсилає її назад елементу керування.
Механізми передачі та обробки повідомлень перших двох категорій повідомлень в основному однакові. Вони призначені для обробки віконними процедурами.
Третя категорія охоплює всі повідомлення WM_COMMAND, що називаються командами (або командними повідомленнями), від інтерфейсу користувача до якого входять меню, кнопки панелей інструментів и акселератори. Обробка команд відрізняється від обробки інших повідомлень і може проводитися багатьма об’єктами (документами, шаблонами документів і самим об’єктом “програма”).
Але незалежно від категорії повідомлення їх обробка здійснюється спеціальними методами якого-небудь класу, що мають спеціальну назву обробник повідомлень та призначені для обробки тільки одного повідомлення.
Карта повідомлень. Для того, щоб об’єкт міг обробляти повідомлення необхідно привести у відповідність повідомлення та обробник, що відповідає за його опрацювання. Для цього служить карта повідомлень. Карта повідомлень реалізує механізм пересилання повідомлень та команд Windows у вікна, документи та інші об’єкти реалізовані на базі MFC. Карти повідомлень перетворюють повідомлення у виклик відповідних функцій класів, що їх обробляють. Кожен клас, який може отримати повідомлення, повинен мати свою карту повідомлень. Вона повинна визначатися за межами будь-якого С-блоку.
Для визначення карти повідомлень в MFC використовують три макроси: BEGIN_MESSAGE_MAP, END_MESSAGE_MAP та DECLARE_MESSAGE_MAP.
Макрос DECLARE_MESSAGE_MAP повинен розміщуватися в кінці оголошення класу, що використовує карту повідомлень.
Структура карти повідомлень є набором макросів, що взяті в спеціальні “операторні дужки” (формуються автоматично при використання майстрів створення класу в середовищі Visual C++):
BEGIN_MESSAGE_MAP(CTheClass, CBaseClass)
//{{AFX_MSG_MAP(CTheClass)
ON_WM_CREATE()
ON_WM_DESTROY()
ON_COMMAND(ID_CHAR_BOLD, OnCharBold)
ON_UPDATE_COMMAND_UI(ID_CHAR_BOLD, OnUpdateCharBold)
//}}AFX_MSG_MAP
ON_NOTIFY(FM_GETFORMAT, ID_VIEW_FORMATBAR, OnGetCharFormat)
ON_NOTIFY(FM_SETFORMAT, ID_VIEW_FORMATBAR, OnSetCharFormat)
END_MESSAGE_MAP()
Параметрами макросу BEGIN_MESSAGE_MAP() є ім’я класу для якого описується карта повідомлень (CTheClass) та ім’я його батьківського класу (CBaseClass). Між викликами цих двох макросів розміщені компоненти карти повідомлень.
Компоненти карти повідомлень. Для стандартного повідомлення Windows визначений свій макрос у формі:
ON_WM_XXX // XXX – ім’я повідомлення, наприклад ON_WM_CREATE
Імена обробників повідомлень формуються на основі наступної угоди: ім’я завжди починається з префіксу On, за яким йде ім’я відповідного повідомлення Windows без префіксу WM_. Наприклад для повідомлення WM_CREATE в класі CWnd визначено обробник:
afx_msg void OnCreate();
Описання всіх стандартних обробників повідомлень Windows можна знайти в файлі AFXWIN.H.
Командні повідомлення від меню, акселераторів та кнопок панелей інструментів обробляються макросом
ON_COMMAND(id, memberFunc)
Параметрами цього макросу є: id – ідентифікатор команди та memberFunc – довільне ім’я (але бажано дотримуватися угод Windows при іменування обробників) функції обробника повідомлення.
Для команд оновлення елементів керування використовується той же механізм але інший макрос
ON_UPDATE_COMMAND_UI(id, memberFunc)
А обробники таких команд мають вигляд.
afx_msg void memberFunc(CCmdUI *pCmdUI);
В обох випадках обробник memberFunc буде викликано тільки тоді, коли у віконну процедуру поступить команда з ідентифікатором id у параметрі wParam.
Наступні команди дозволяють поставити у відповідність команди, що посилаються елементами керування та дочірніми вікнами своїм предкам та відповідні обробники. Для цього застосовується макрос
ON_CONTROL(wNotifyCode, id, memberFunc)
Цей макрос використовує як параметри код повідомлення wNotifyCode від елементу керування id та довільний обробник memberFunc. Обробник викликається тільки тоді, якщо код повідомлення елемента (наприклад BN_CLICKED) співпаде із зареєстрованим в карті повідомлень значенням wNotifyCode, а значення параметра співпаде з id.
Для найбільш поширених повідомлень від елементів керування використовуються спеціальні макроси. Наприклад:
ON_BN_CLICKED(id, memberFunc)
ON_EN_FOCUS(id, memberFunc)
ON_LBN_BDLCLK(id, memberFunc)
та інші. Повний список макросів цієї групи є в файлі AFXMSG.H.
Існують макроси для зв’язування одного обробника з декількома командами чи повідомленнями від елементів керування.
ON_COMMAND_RANGE(idFirst, idLast, memberFunc)
ON_CONTROL_RANGE(wNotifyCode, idFirst, idLast, memberFunc)
ON_NOTiFY_RANGE(wNotifyCode, idFirst, idLast, memberFunc)
ON_UPDATE_COMMAND_UI_RANGE(idFirst, idLast, memberFunc)
Команди, що визначаються користувачем включаються в карту повідомлень за допомогою макросу:
ON_MESSAGE(WM_NAMEMSG, OnNameMsg)
Параметрами якого є номер (WM_MESSAGE) та ім’я (OnNameMsg) обробника повідомлення. Для того щоб номер повідомлення був унікальним в межах системи (програми) застосовують наступне оголошення:
#define WM_NAMEMSG (WM_USER + numb)
де numb – довільне додатне число (комбінація повинна бути унікальною).
Повідомлення, що визначаються користувачем включаються в карту за допомогою макросу:
ON_REGISTERED_MESSAGE(nMessageVariables, memberFunc)
де nMessageVariables – зареєстроване в Windows повідомлення (для отримання унікального ідентифікатора необхідно викликати функцію RegisterWindowMessage()), а memberFunc його обробник.
Для команд та повідомлень користувача обробники мають наступний прототип:
afx_msg LRESULT memberFunc(WPARAM wParam, LPARAM lParam);
де через wParam та lParam у функцію передається додаткова до повідомлення інформація.
Стандартний маршрут команди. Команди обробляються за механізмом трохи відмінним від стандартного циклу повідомлень Windows. Оскільки команди формуються в результаті взаємодії користувача з програмою, то, зазвичай, команда починає свій шлях до отримувача від основного вікна програми.
Кожен об’єкт, що має карту повідомлень здатний обробляти команди. Якщо в карті повідомлень не знайдено відповідного обробника, то команда посилається батьківському об’єкту і так далі. Якщо відповідного обробника і далі не знайдено, то команда посилається процедурі обробки команд за замовчуванням.
Порядок в якому адресати пересилають команди зашитий в бібліотеці класів MFC і містить наступні кроки:
1. Команду отримує фрейм MDI (об’єкт класу CMDIFrameWnd). Порядок отримувачів:
- активне вікно класу CMDIChildWnd;
- сам об’єкт класу CMDIFrameWnd;
- програма класу CWinApp.
2. Команду отримує фрейм документу (об’єкт класу CFrameWnd, CMDIChildWnd) Порядок отримувачів:
- активний вид (view);
- біжучий фрейм документу;
- програма класу CWinApp.
3. Команду отримує вид (view). Порядок отримувачів:
- біжучий вид;
- документу приєднаний до виду.
4. Команду отримує документ. Порядок отримувачів:
- біжучий документ;
- шаблон документу, приєднаний до нього.
5. Команду отримує блок діалогу. Порядок отримувачів:
- біжучий блок діалогу;
- вікно, що володіє цим блоком діалогу;
- програма класу CWinApp.
Стандартне повідомлення Windows не шукає відповідного обробника повідомлення воно посилається безпосередньо у віконну процедуру того об’єкту якому воно призначене.
Функції роботи з повідомленнями. Загальні функції роботи з повідомленнями, такі як SednMessage(), PosrMessage() розглядалися раніше їх призначення таке ж як і в відповідних системних функцій. Додатково розглянемо функцію реєстрації повідомлень користувача. Її прототип:
UINT ::RegisterWindowMessage(LPCTSTR lpString);
Повертає унікальний ідентифікатор (в діапазоні від 0xC000 до 0xFFF) нового повідомлення. Як параметр lpString вона використовує вказівник на символьний рядок, що визначає ім’я повідомлення. Зареєстроване повідомлення діє до кінця сеансу роботи з Windows.
Приклад реєстрації повідомлення користувача.
class CUserWnd : public CParentWnd {
…
//{{AFX_MSG_MAP(CUserWnd)
afx_msg LRESULT OnSample(WPARAM wParam, LPARAM lParam);
//}}AFX_MSG_MAP
DECLARE_MESSAGE_MAP()
}
UINT WM_SAMPLE = RegisterWindowMessage(“MESSAGE_SAMPLE”);
BEGIN_MESSAGE_MAP(CUserWnd, CParentWnd)
//{{AFX_MSG_MAP(CUserWnd)
ON_REGISTERED_MESSAGE(WM_SAMPLE, OnSample)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()