Главная страница Случайная страница Разделы сайта АвтомобилиАстрономияБиологияГеографияДом и садДругие языкиДругоеИнформатикаИсторияКультураЛитератураЛогикаМатематикаМедицинаМеталлургияМеханикаОбразованиеОхрана трудаПедагогикаПолитикаПравоПсихологияРелигияРиторикаСоциологияСпортСтроительствоТехнологияТуризмФизикаФилософияФинансыХимияЧерчениеЭкологияЭкономикаЭлектроника |
💸 Как сделать бизнес проще, а карман толще?
Тот, кто работает в сфере услуг, знает — без ведения записи клиентов никуда. Мало того, что нужно видеть свое раписание, но и напоминать клиентам о визитах тоже.
Проблема в том, что средняя цена по рынку за такой сервис — 800 руб/мес или почти 15 000 руб за год. И это минимальный функционал.
Нашли самый бюджетный и оптимальный вариант: сервис VisitTime.⚡️ Для новых пользователей первый месяц бесплатно. А далее 290 руб/мес, это в 3 раза дешевле аналогов. За эту цену доступен весь функционал: напоминание о визитах, чаевые, предоплаты, общение с клиентами, переносы записей и так далее. ✅ Уйма гибких настроек, которые помогут вам зарабатывать больше и забыть про чувство «что-то мне нужно было сделать». Сомневаетесь? нажмите на текст, запустите чат-бота и убедитесь во всем сами! Структура простої MFC – програми
2.2.1 Послідовність створення MFC – програми Програма MFC має містити щонайменше два класи: клас прикладки та клас вікна. Фактично, ви можете створити мінімальну програму MFC в меншій кількості рядків програми, ніж передбачали. Вказана послідовність визначає кроки, які ви маєте виконати, щоб створити найменшу, функціональну програму MFC: 1) створити клас прикладки, похідний від CWinApp; 2) створити клас вікна, похідний від CFrameWnd та оголосити існування у ньому спеціальної карти повідомлень. 3) для класу прикладки реалізувати функцію InitInstance() – ініціалізації прикладки; 4) у конструкторі класу вікна викликати функцію Create() для створення вікна; 5) оголосити глобальним об’єкт класу прикладки; 6) створити карту повідомлень (хоча б і порожню); 7) підключити необхідні заготовочні файли та визначити тип проекту.
2.2.2 Створення класу прикладки Клас прикладки є дуже коротким і містить лише функцію ініціалізації прикладки – InitInstance(). Якщо система розробки сама створює конструктор та деструктор класу – на них можна не звертати увагу і вилучити з тексту програми. Батьківським для класу прикладки є клас CWinApp. У прикладі 2.1 ви можете побачити структуру класу прикладки (у прикладі – це клас СApp). Приклад 2.1 Опис класу прикладки // файл App.hclass CApp: public CWinApp // клас CApp є похідним від CWinApp {public: BOOL InitInstance(); // оголошення функції ініціалізації прикладки};2.2.3 Створення класу головного вікна програми Клас вікна у найпростішому випадку містить лише конструктор класу та оголошення карти повідомлень. Якщо необхідно до них додаються оголошення об’єктів елементів керування та функцій-обробників. Вміст класу вікна (CMainWin) можна побачити у прикладі 2.2:
Приклад 2.2 - опис класу головного вікна // файл App.h class CMainWin: public CFrameWnd // клас CMainWin є похідним від CFrameWnd {public: CMainWin(); // конструктор класу головного вікна DECLARE_MESSAGE_MAP() // оголошення карти обробки повідомлень}; Зазначимо, що якщо наша програма має обробляти повідомлення користувача, у класі головного вікна необхідно оголосити існування карти повідомлень. На відміну від класу головного вікна, клас прикладки не має необхідності обробляти повідомлення, хоча б тому, що прикладка не виводиться на дисплей.
2.2.4 Реалізація функції ініціалізації прикладки Функція ініціалізації прикладки входить до класу CApp. Її текст наведено у прикладі 2.3:
Приклад 2.3 – реалізація функції ініціалізації прикладки // файл App.cpp BOOL CApp:: InitInstance(){ m_pMainWnd = new CMainWin; // створення об’єкта вікна m_pMainWnd-> ShowWindow(m_nCmdShow); // відображення вікна m_pMainWnd-> UpdateWindow(); // оновлення вікна return TRUE; } Слід дещо прокоментувати написане. Першим рядком динамічно створюється об’єкт класу вікна, який пов’язується із покажчиком (m_pMainWnd – член класу CWinApp) на головне вікно, що пов’язане із прикладкою. Тобто надалі, m_pMainWnd описує вікно. Далі: головне вікно виводиться на екран функцією ShowWindow() та оновлюється UpdateWindow(). У разі успішного виконання функція InitInstance() завжди повертає TRUE.Функція ShowWindow(parameter) використовує один параметр – режим відображення вікна. Такими режимами можуть бути: SW_HIDE, SW_MINIMIZE, SW_SHOWMAXIMIZED, SW_SHOWMINIMIZED, SW_SHOWNORMAL – відповідно відображення скритого, мінімізованого, максимізованого за розміром вікна та його відображення у нормальному (за замовчуванням) режимі Windows. Якщо вікно відкривається вперше, у функцію ShowWindow() передається параметр m_pCmdShow (змінна-член класу CWinApp), який визначає спосіб вигляду го-ловного вікна при запуску програми, що й зроблено у нашому випадку. 2.2.5 Реалізація конструктора класу головного вікна Конструктор головного вікна викликається з функції InitInstance() процедурою створення об’єкта класу вікна у динамічній пам’яті (див. Приклад 2.4). Приклад 2.4 – Реалізація конструктора класу головного вікна// файл App.cpp CMainFrame:: CMainFrame(){Create(NULL, " Проста MFC-програма"); // Створення вікна із заголовком}
У функції CMainFrame(), використовується єдиний виклик – функція Create() з двома параметрами. У дійсності ця функція має складну структуру: BOOL Create(LPCTSTR lpszClassName, // ім'я класу вікна (з параметрами вікна) LPCTSTR lpszWindowName, // заголовок вікна WORD dwStyle = WS_OVERLAPPEDWINDOW, /* стиль вікна, заданий за замовчуванням стиль – з системним меню і трьома стандартними кнопками в кутку (WS_OVERLAPPEDWINDOW) */ const RECT& rect = rectDefault, // позиція й розмір вікна CWnd* pParentWnd = NULL, /* покажчик на батьківське вікно, має значення NULL якщо поточне вікно - основне вікно програми */ LPCTSTR lpszMenuName = NULL, // покажчик на меню вікна DWORD dwExStyle = 0, // визначення розширених стилів CCreateContext* pContext = NULL); // додаткові контекстні структури Можна побачити, що тільки два перших параметри обов’язково мають визначатися у програмі, інші – використовуються за необхідністю. Оголошення позиції й розміру вікна використовує спеціальний тип: typedef struct tagRECT { LONG left; // Визначає х-координату лівого верхнього кута прямокутника; LONG top; // Визначає у-координату лівого верхнього кута прямокутника; LONG right; // Визначає х-координату нижнього правого кута прямокутника; LONG bottom; // Визначає у-координату нижнього правого кута прямокутника; } RECT;Структура RECT визначає координати лівих верхніх і нижніх правих кутів прямокутника і зазвичай визначає розміри прямокутних об’єктів.
2.2.6 Створення об’єкта прикладки Створення цього об’єкта має надзвичайно важливе значення – саме з оголошення об’єкта прикладки розпочинається виконання програми. А після цього викликається функція InitInstance(), що у динамічній пам’яті створює об’єкт вікна, показує й оновлює вікно. У момент створення об’єкта вікна використовується конструктор вікна, що реалізує виклик функції Create() із відповідним набором параметрів. Але починається все із об’єкта прикладки, оголошення якого наведено у прикладі 2.5:
Приклад 2.5 // файл App.cpp CApp App; // створення об’єкта прикладки
Такий об’єкт оголошується глобально, тобто поза оголошеннями функцій та структур даних.
2.2.7 Створення карти повідомлень Будь-який клас, похідний від класу CCmdTarget може реагувати на пові-домлення Windows: прикладка, вікно, документ, елемент керування. Те, у який спосіб ваша програма оброблятиме повідомлення, залежатиме від концепції функціонування вашої програми. Як вже зазаначалося, по-перше, слід оголосити про наявність карти повідомлень у класі головного вікна:
DECLARE_MESSAGE_MAP()
Такий запис додається у кінець оголошення класу, якраз перед закриваючою дужкою. DECLARE_MESSAGE_MAP() є макрокомандою MFC. Після того, ви оголосили вашу карту повідомлень у заголовку вашого класу, прийшов час визначати карту повідомлень у файлі реалізації функцій ваших класів. Карта повідомлень теж є макросом. Пуста карта повідомлень виглядає таким чином: BEGIN_MESSAGE_MAP(CMainWnd, CFrameWnd) END_MESSAGE_MAP()
Текст карти повідомлень не належить функціям класів програми і є глобальним макросом, що зазвичай розташовують у *.cpp файлі проекту. Слід звернути увагу, що зазначена карта повідомлень належить класу CMainWin, про що нагадує перший параметр макросу. Це особливо важливо у випадку, коли наша програма містить декілька вікон, наприклад діалогових, і кожне вікно має власну карту повідомлень. Другий параметр макросу означає посилання на батьківський клас власника карти повідомлень (у нашому випадку CFrameWnd). Якщо певне повідомлення не обробляється класом-власником карти повідомлень, можливо воно обробляється стандартним обробником, який належить батьківському класу.
2.2.8 Підключення заголовочних файлів та визначення опцій проекту Підключення заголовочних файлів необхідно, власне, для використання функцій MFC і здійснюється директивою препроцесора #include. Слід запам’ятати, що якщо компілятор знаходить близько 120 помилок у програмі з 10 рядків, помилку слід шукати у тому, що не підключено необхідний заголовочний файл, або вказане таке ім’я батьківського класу, яке у цьому заголовочному файлі не визначено, причому помилку може дати й некоректний регістр символу, скажімо Сframewnd замість CFrameWnd. Приклад підключення заголовочних файлів подано нижче:
Приклад 2.6 – Приклад підключення заголовочних файлів
#include < afxwin.h> // підключення бібліотеки MFC #include “App.h” // підключення файла оголошень програми
Рядок із підключенням файла afxwin.h є обов’язковим для усіх MFC-програм і має вводитися завжди. Другий рядок прикладу зазвичай формується автоматично, коли засобами інтерфейсу Visual C++ формується клас CApp (див. Розділ 1) і автоматично формуються порожні тіла конструктора й деструктора. До речі, запис afx (Application Frameworks) є попередньою назвою MFC. На останнє, слід визначити опції проекту, для чого натискують Alt+F7. У полі „Microsoft Foundation Classes” обирають „Use MFC….” у першому або другому варіантах, тобто використання MFC. Після цього можна компілювати проект (натиснувши F7) та виконати його (натиснувши Сtrl+F5). У результаті, маємо отримати вікно, зображене на рисунку 2.2.
Рисунок 2.2 – Результат виконання простої MFC-програми
Повністю текст простої MFC-програми наведений у прикладі 2.6.
Приклад 2.6 – Повний текст простої MFC-програми // App.h: interface for the CApp class class CMainWin: public CFrameWnd {public: CMainWin(); DECLARE_MESSAGE_MAP()};
class CApp: public CWinApp {public: BOOL InitInstance(); };
// App.cpp: implementation of the CApp class. #include < afxwin.h> #include " App.h" CMainWin:: CMainWin() {Create(NULL, " Проста MFC-програма"); } BEGIN_MESSAGE_MAP(CMainWin, CFrameWnd) END_MESSAGE_MAP()
BOOL CApp:: InitInstance() {m_pMainWnd=new CMainWin; m_pMainWnd-> ShowWindow(m_nCmdShow); m_pMainWnd-> UpdateWindow(); return TRUE; }
CApp App;
2.3 Повідомлення та їх обробка у MFC-програмах
2.3.1 Загальна інформація про обробку повідомлень Після того, як у класі головного вікна програми оголошено карту повідомлень, а у файлі функцій класу вікна ця карта отримала свій початок і закінчення (див. 2.2.7), слід переходити до визначення обробки повідомлень програми. Windows, як будь-яка операційна система взаємодіє з програмою через механізм повідомлень. Повідомлення мають різну природу. Повідомленням може бути вибір користувачем пункту меню, натискання клавіш клавіатури, переміщення курсору миші екраном вашого комп’ютера, надходження інформації про системні події у мережі тощо. Програма має „знати” на які повідомлення їй слід реагувати, на які – не слід, а якщо необхідна реакція на повідомлення – визначати, яку функцію-обробник слід викликати у відповідь на надходження повідомлення. Саме таку послідовність роботи виконує карта повідомлень. Якщо, наприклад, головне вікно програми отримало повідомлення, програма здійснює пошук карти по-відомлень головного вікна і макрокоманди, відповідної повідомленню. У випадку, коли макрокоманду знайдено, відшукується функція-обробник повідомлення, який виконується; якщо ж відповідної макрокоманди нема, обробка повідомлення може забезпечуватися стандартним обробником батьківського класу або не здійснюватися зовсім. У Visual C++ програмах повідомлення описуються у дуже простий спосіб і містять префікс WM_, що означає повідомлення Windows (Window message) а також ключову частину, яка визначає повідомлення. Приклади повідомлень: WM_CHAR натискання клавіші клавіатури WM_PAINT необхідність оновлення вікна WM_MOVE переміщення вікна WM_CLOSE закриття вікна WM_LBUTTONDOWN натискання лівої клавіші миші WM_MOUSEMOVE переміщення курсору миші WM_COMMAND вибір ЕК (пункт меню та інші ЕК) WM_SIZE зміна розмірів вікна
2.3.2 Макрокоманди повідомлень Кожному повідомленню відповідає макрокоманда – команда, за допомогою якої програма реагує на повідомлення. Макрокоманда формується у такий спосіб: ON_+ < Назва повідомлення> +(), наприклад ON_WM_PAINT(), ON_WM_MOVE(), ON_WM_MOUSEMOVE(), але ON_COMMAND(ідентифікатор ЕК, обробник ЕК). Приклад карти повідомлень наведено нижче:
Приклад 2.7 – Приклад карти повідомлень BEGIN_MESSAGE_MAP(CMainWin, CFrameWnd) ON_WM_CHAR() // обробка натискання клавіші клавіатури ON_WM_LBUTTONDOWN() // обробка натискання лівої клавіші миші END_MESSAGE_MAP()
2.3.3 Оголошення та реалізація обробників повідомлень Відповідно макрокоманді, у класі – власнику карти повідомлень необхідно оголосити прототип функції – обробника повідомлення, наприклад:
Приклад 2.8 – Приклад запису класу вікна з прототипами обробників class CMainWin: public CFrameWnd {public: CMainWin(); void OnChar(UINT Ch, UINT Count, UINT Flags); // прототип функції обробника натискання клавіші клавіатури DECLARE_MESSAGE_MAP() };
Обробники повідомлень не повертають нічого, тому мають тип void. У застарілій літературі перед описом обробників розташовують ключове слово afx_msg, наприклад: afx_msg void OnChar(UINT Ch, UINT Count, UINT Flags); Такий запис не є помилкою, але його вже можна не використовувати. Нарешті, після визначення прототипу функції-обробника, необхідно, власне, реалізувати таку функцію, визначивши її текст, наприклад:
Приклад 2.9 – Приклад обробника повідомлення WM_CHAR void CMinWin:: OnChar(UINT ch, UINT count, UINT flags) { char *str; wsprintf(str, " %c", ch); MessageBox(str, " Pressed key! ", MB_ICONHAND|MB_ABORTRETRYIGNORE); }
Таким чином, для організації обробки повідомлень необхідно: 1) включити до карти повідомлень макрокоманду обробки; 2) включити прототип обробника повідомлення у опис класу, відповідного за обробку повідомлення; 3) включити до програми реалізацію функції-обробника. Повернемося до визначення функції-обробника OnChar(). Її прототип має такий вигляд: void СWnd:: OnChar (UINT nChar, UINT nRepCnt, UINT nFlags); де nChar – містить значення коду клавіші; nRepCnt – містить кількість разів повторного натиснення клавіші, або коли користувач утримує клавішу; nFlags – містить окремі значення прапорців, наприклад код попередньо натиснутої клавіші. Обробник OnChar(), наведений у прикладі 2.9, забезпечує виведення символу клавіші клавіатури в інформаційне вікно повідомлень.
2.3.4 Поняття контексту пристрою та простий вивід тексту у вікно У широкому сенсі контекст пристрою є шляхом, за яким Windows-програма, використовуючи відповідний драйвер, виводить інформацію у робочу область вікна. Контекст пристрою є структурою, що повністю визначає стан цього драйвера та спосіб виводу інформації. Перед тим, як програма розпочне виводити інформацію у робочу область вікна, вона має отримати контекст пристрою. Поки це не зроблено, немає зв’язку між програмою та вікном, призначеним для виведення. У традиційних Windows-програмах контекст пристрою отримують викликом функції GetDC(), а звільняють за допомогою ReleaseDC(). Оскільки Windows може надати лише невелику кількість контекстів, важливо, щоб програма звільнила контекст після закінчення роботи з ним. MFC має відповідні класи, здатні керувати цим процесом. Зокрема, при створенні екземпляра об’єкта типу CClientDC програмі надається контекст пристрою. Якщо цей об’єкт необхідно вилучити, викликається функція ReleaseDC() і контекст пристрою автоматично звільняється. Конструктор класу СClientDC записується у вигляді: СClientDC (CWnd *Windows); де параметр Windows є покажчиком на вікно, якому надається контекст пристрою. Для активного вікна можна сказати ключове слово this. Вивід у клієнтську область вікна програми може забезпечуватися за допомогою функції TextOut(): virtual BOOL CDC:: TextOut(int X, int Y, LPCSTR lpszStr, int Length); де X, Y – координати точки початку виводу у пікселях (відносно лівого верхнього кута вікна програми), lpszStr – покажчик на рядок, що виводиться, а Length – його довжина. Приклад 2.10 ілюструє ще один варіант реалізації обробника натискання клавіш клавіатури, на цей раз – у точку (1, 1) вікна програми.
Приклад 2.10 – Інший приклад обробника натискання клавіш клавіатури char str [80]; // рядок символів для виводу void CMainWin:: OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) {CClientDC dc(this); // отримання контексту вікна dc.TextOut(1, 1, " ", 3); // стирання старого тексту wsprintf(str, " %с", ch); // формування рядка із кодом клавіші dc.TextOut(1, 1, str, strlen(str)); // вивід рядка у координату (1, 1) } Таким чином, щоразу, коли необхідно вивести інформацію у робочу область вікна, необхідно отримати контекст пристрою за допомогою CClientDC. Окремим випадком є застосування повідомлення WM_PAINT.
2.3.5 Оновлення вмісту робочої області вікна програми Для оновлення вмісту робочого вікна програми необхідно надіслати повідомлення WM_PAINT. Поза його застосуванням вміст вікна може не відображатися. Тобто щоразу, коли є потреба виводити інформацію у вікно програми, слід звертатися до цього повідомлення. Повідомленню WM_PAINT відповідає макрокоманда ON_WM_PAINT(), а макрокоманді – обробник OnPaint(). Цей обробник може виглядати так: Приклад 2.11 – Приклад обробника повідомлення WM_PAINT void CMainWin:: OnPaint() {CPaintDC dc(this); // отримання контексту вікна dc.TextOut(1, 1, str, strlen(str)); // відображення символу }
У прикладі 2.11 наведено обробник OnPaint(), що забезпечує виведення на екран символу, введеного з клавіатури користувачем відповідно обробнику OnChar(), записаному у прикладі 2.10. Видно, що для отримання контексту пристрою тут використано об’єкт іншого типу, а саме CPaintDC. На відміну від CClientDC, що співпрацює тільки із клієнтською частиною вікна програми, CPaintDC забезпечує роботу із усією площиною вікна. Звісно, у програмі бажаним було б, щоб Windows самостійно вирішувала, коли їй викликати повідомлення WM_PAINT і це так відбувається, наприклад, коли користувач програми мінімізує вікно, максимізує, рухає екраном, змінює розміри вікна тощо. Але інколи необхідно проводити оновлення вікна примусово. Для того, щоб надіслати повідомлення WM_PAINT, програма викликає функцію InvalidateRect() – член класу CWnd, що має такий прототип:
void CWnd:: InvalidateRect(LPCRECT lpRegion, BOOL Erase=TRUE);
де lpRegion – покажчик на область вікна, що необхідно оновити, Erase – прапорець, який у значенні TRUE встановлює вилучення попереднього вмісту вікна. Якщо вказати першому параметра значення NULL, відбудеться повне оновлення вікна. Виклик функції InvalidateRect() забезпечує примусове надсилання повідомлення WM_PAINT і виконання обробника OnPaint(). Повне оновлення вікна, як наприклад:
InvalidateRect(NULL);
займає багато ресурсів системи, що візуально може виглядати мерехтінням робочого вікна програми. Цього можна уникнути, задавши окремо область оновлення, наприклад:
СRect region(10, 10, 100, 100); InvalidateRect(region); у такому випадку оновлюватиметься лише область, обмежена прямокутником із лівим верхнім кутом (10, 10) та нижнім правим кутом (100, 100). Такий варіант оновлення стає особливо цікавим, якщо необхідно забезпечити рухомість окремих елементів вікна, а цього можна досягнути із одночасним застосуванням обробки повідомлення таймера WM_TIMER.
2.3.6 Обробка повідомлень миші Маніпулятор „миша” є найбільш часто застосовуваним засобом керування в операційній системі Windows. Тому обробка повідомлень миші є типовим прикладом реалізації обробників. Миші відповідають такі повідомлення: WM_LBUTTONDOWN – натиснуто ліву клавішу; WM_RBUTTONDOWN – натиснуто праву клавішу; WM_MOUSEMOVE – пересунуто курсор миші; WM_MBUTTONDOWN – натиснуто середню клавішу; WM_LBUTTONUP – відпущено ліву клавішу; WM_RBUTTONUP – відпущено праву клавішу, та деякі інші повідомлення. Цим повідомленням відповідають стандартні обробники, які для перших трьох повідомлень мають вигляд: void CWnd:: OnLButtonDown(UINT Flags, CPoint Loc); void CWnd:: OnRButtonDown(UINT Flags, CPoint Loc); void CWnd:: OnMouseMove(UINT Flags, CPoint Loc); де Flags – вказує на те, що можливо одночасно із клавішею миші були натиснуті клавіші Ctrl, Shift або інша клавіша миші (константи MK_CONTROL, MK_SHIFT, MK_LBUTTON, MK_MBUTTON, MK_RBUTTON); Loc – містить координати курсору миші у момент надіслання повідомлення. Loc є об’єктом типу CPoint. У свою чергу CPoint породжений від структури POINT: typedef struct tagPoint {long X; long Y; }POINT; Маючи в обробнику повідомлення миші параметр Loc, можна отримати значення координат курсору миші: Loc.X – координата X, Loc.Y – координата Y. Знаючи все вищезазначене, для обробки, наприклад, повідомлення WM_LBUTTONDOWN, зробимо такі дії: 1. Включимо до карти повідомлень макрокоманду повідомлення: BEGIN_MESSAGE_MAP(CMainWin, CFrameWnd) ON_WM_LBUTTONDOWN() // обробка натискування лівої клавіші миші END_MESSAGE_MAP() 2. Включимо прототип обробника у клас головного вікна: сlass CMainWin: public CFrameWnd {public: CMainWin(); void OnLButtonDown(UINT Flags, CPoint Loc); // прототип функції обробника натискування лівої клавіші миші DECLARE_MESSAGE_MAP() };
3. Реалізуємо обробник, у якому натискування лівої клавіші миші забезпечує виведення рядка із координатами курсору та текстом:
Приклад 2.12 – Приклад обробника повідомлення WM_LBUTTONDOWN void CMainWin:: OnLButtonDown(UINT Flags, CPoint Loc) {CClientDC dc(this); // отримання контексту вікна char str[255]; // змінна для зберігання рядка wsprintf(str, “Натиснута ліва клавіша миші”, Loc.x, Loc.y); // копіювання тексту та координат миші у змінну str dc.TextOut(Loc.X, Loc.Y, str, strlen(str)); // відображення тексту } Приклад функціонування такого обробника зображено на рисунку 2.3.
Рисунок 2.3 – Результат використання обробника лівої клавіші миші
2.3.7 Обробка повідомлень таймера Під час розробки програм доволі часто необхідно враховувати час. Наприклад, для організації виведення на екран рухомих об’єктів застосовують такий спосіб роботи програми: на одному такті роботи програми виводиться статичний набір графічних об’єктів, на другому такті старе зображення стирається і відбувається вивід нового, вже зміненого набору графічних об’єктів. Якщо зазначені зміни відбуваються достатньо швидко, користувачу здається, що об’єкт рухається. Подібна схема використовується і у графічних бібліотеках, наприклад OpenGL. Ми про це поговоримо дещо пізніше. Як уже згадувалося, роботі таймера відповідає повідомлення WM_TIMER. У карті повідомлень йому належить макрокоманда ON_WM_TIMER(), якій у свою чергу відповідає обробник OnTimer(). Для роботи з таймером MFC використовує декілька функцій, серед них функції запуску таймера та його вилучення. Запуск таймера забезпечується функцією SetTimer():
UINT CWnd:: SetTimer(UINT nIDEvent, UINT nElapse, void (CALLBACK EXPORT* lpfnTimer)(HWND, UINT, UINT, DWORD)); де nIDEvent – визначає ідентифікатор таймера відмінний від нуля, nElapse – визначає часовий інтервал в мілісекундах між моментами активації таймера; lpfnTimer – визначає покажчик на можливу функцію зворотного виклику, яка обробляє повідомлення WM_TIMER. Якщо цей параметр встановити рівним NULL, повідомлення WM TIMER обробляється об'єктом класу CWnd. Вилучення таймера проводиться за допомогою функції KillTimer(): BOOL CWnd:: KillTimer(UINT ID); Повідомлення таймера обробляються функцією OnTimer(UINT ID). В усіх випадках ID є ідентифікатором таймера (у простішій інтерпретації – його номер). Приклад 2.13 демонструє використання функції таймера для виведення інформації про поточний системний час.
Приклад 2.13 – Реалізація обробника повідомлення таймера void CMainWin:: OnTimer(UINT ID) {CTime curtime = CTime:: GetCurrentTime(); // отримуємо значення системного часу struct tm *newtime; newtime = curtime.GetLocalTm(); // повертаємо покажчик на структуру tm wsprintf(str, asctime(newtime)); // перетворюємо дані часу у рядок str[strlen(str)-1] = ’\0’; // видаляємо останній символ рядка InvalidateRect(NULL, 0); // оновлюємо вікно }
Якщо програма закінчує свою роботу, необхідно надіслати інше повідомлення – WM_DESTROY (повідомлення про руйнування вікна). Йому відповідає інший обробник – OnDestroy(). Саме у ньому можна вилучити таймер:
void CMainWin:: OnDestroy (){KillTimer (1); }
Повний текст програми для роботи із таймером, яка виводить поточний системний час у заголовок вікна, наведено у Прикладі 2.14
Приклад 2.14 – Програма роботи з таймером
// файл st1.h class CApp: public CWinApp {public: BOOL InitInstance(); };
class CMainWin: public CFrameWnd {public: void OnTimer(UINT); CMainWin(); DECLARE_MESSAGE_MAP()};
// файл st1.cpp #include < afxwin.h> #include " st1.h"
CMainWin:: CMainWin() {Create(NULL, NULL); // cтворення вікна з пустим заголовком SetTimer(1, 500, NULL); // запуск таймера з інтервалом 500 мілісекунд } BOOL CApp:: InitInstance() {m_pMainWnd = new CMainWin; m_pMainWnd-> ShowWindow(m_nCmdShow); m_pMainWnd-> UpdateWindow(); return TRUE; }
CApp App;
BEGIN_MESSAGE_MAP(CMainWin, CFrameWnd) ON_WM_TIMER() // макрокоманда обробки таймера END_MESSAGE_MAP()
void CMainWin:: OnTimer(UINT i) {char s[250]; CTime curtime = CTime:: GetCurrentTime(); struct tm *newtime; newtime=curtime.GetLocalTm(); wsprintf(s, asctime(newtime)); s[strlen(s)-1]='\0'; SetWindowText(s); // копіювання рядка зі значенням часу у заголовок вікна CClientDC dc(this); dc.TextOut(10, 10, s, strlen(s)); // копіювання рядка із часом у вікно } Результат роботи програми із прикладу 2.14 наведено на рисунку 2.4.
Рисунок 2.4 – Результат використання програми із таймером
2.4 Вікна повідомлень у MFC-програмах
Операційна система Microsoft Windows забезпечена стандартними засобами інформування користувача про свої події, зокрема запити користувачу про режими роботи програми, помилки, інші повідомлення. Зазвичай вікна повідомлень виводять інформацію для користувача і чекають на його реакцію. Це забезпечується функцією MessageBox(). MessageBox() створює і відображає вікно, яке містить необхідне інформаційне повідомлення і заголовок, а також комбінацію визначених піктограм і кнопок. Функція MessageBox() має такий прототип:
int CWnd:: MessageBox(LPCTSTR lpszText, LPCTSTR lpszCaption=NULL, UINT nType = MB_OK); Параметри: lpszText – покажчик на рядок всередині вікна повідомлень; lpszCaption – покажчик на рядок у заголовку вікна повідомлень. Якщо lpszCaption має значення NULL, за замовчуванням використовується заголовок " Error". nType – визначає стиль оформлення піктограм та кнопок всередині вікна повідомлень. Останній параметр – nType може дорівнювати різним стандартним значенням, серед яких – константи, наведені на Рисунку 2.5 [1]. Рисунок 2.5 – Різні типи зображень у вікнах повідомлень та їх константи
Користувач може на свій розсуд визначати також і комбінації клавіш, що необхідно вивести у вікно повідомлень. Це можуть бути такі стандартні набори: MB_OK – клавіша з надписом OK; MB_OKCANCEL - комбінація OK та Cancel; MB_ABORTRETRYIGNORE – клавіші “Abort”, “Retry”, “Ignore”, MB_YESNO – клавіші “Yes”, “No”, MB_YESNOCANCEL. – клавіші “Yes”, “No”, “Cancel”. Ці набори можна комбінувати з різними зображеннями. Комбінація забезпечується застосуванням операції „|”. Наприклад: MB_ICONQUESTION | MB_YESNO. Абревіатуру “MB” легко зрозуміти: це MessageBox. Якщо просто описати параметри функції MessageBox(), то першим пара-метром є текст у вікні повідомлень, другий – текст у заголовку вікна повідомлень, третій – тип оформлення графіки та клавіш вікна повідомлень. Наприклад:
MessageBox(“Помилка”); // Примітка st1- ім’я проекту MessageBox(“Ви бажаєте закінчити програму”, MB_ICONQUESTION | MB_YESNO); MessageBox(“Файл не знайдено”, MB_ICONSTOP | MB_ABORTRETRYIGNORE);
На рисунку 2.6 зображено зовнішній вигляд вікон повідомлень, відповідних попереднім прикладам.
Рисунок 2.6 – Різні типи вікон повідомлень
Функція MessageBox() повертає ідентифікатор клавіші, обраної у вікні повідомлень. Повертаються, зокрема такі значення: IDABORT, IDRETRY, IDIGNORE, IDCANCEL, IDNO, IDYES, IDOK. Вони відповідають клавішам „Abort”, „Retry”, „Ignore”, „Cancel”, „No”, „Yes”, „OK”. Якщо недостатньо пам'яті, щоб створити вікно повідомлень, повертається 0. Приклад із визначенням натиснутої клавіші наведено нижче:
Приклад 2.15 – Обробник лівої клавіші миші із вікнами повідомлень
void CMainWin:: OnLButtonDown(UINT flags, CPoint loc) {int i = MessageBox(“Натисніть клавішу”, ”Ліва клавіша миші”, MB_ABORTRETRYIGNORE); switch(i) {case IDABORT: MessageBox(“”, ”Abort”); break; case IDRETRY: MessageBox(“”, ”Retry”); break; case IDIGNORE: MessageBox(“”, ”Ignore”); break; }}
Додатково зазначимо, що разом з описаною функцією MessageBox() може використовуватися й глобальна функція AfxMessageBox(), яку можна розмістити у будь-якому місці програми, навіть у функції InitInstance() перед створенням головного вікна програми.
2.5 Створення ресурсів програми. Використання меню та акселераторів 2.5.1 Створення ресурсів та включення їх у проект Раніше вже розглядалися основні положення побудови проектів у Visual C++. Тепер розглянемо включення ресурсів у проект. Створення ресурсів забезпечується за допомогою внутрішнього редактора ресурсів Visual С++. Для додавання ресурсу необхідно в існуючому відкритому проекті натиснути комбінацію клавіш Ctrl+R або знайти відповідний пункт “Resource” у меню “Insert” Visual C++. Після такого вибору з’являється діалогове вікно, зображене на рисунку 2.7.
Рисунок 2.7 – Діалогове вікно додавання ресурсів у проект
У цьому діалоговому вікні обирають необхідний тип ресурсу. Далі відкривається, власне, редактор ресурсів, наприклад на рисунку 2.8 зображено його вигляд у випадку створення меню користувача.
Рисунок 2.8 – Вигляд елемента вікна редактора ресурсів
Після того, як елементи меню (або будь-якого іншого ресурсу) визначені, необхідно зберегти ресурсний файл (із розширенням *.rc) – натиснувши відповідну піктограму збереження. Наступний етап – додавання ресурсного файла у проект. Для цього слід обрати пункт ”Project” меню Visual C++, далі “Add to project”, “Files” і обрати необхідний ресурсний файл. Після цього відповідна сторінка ResourceView з’являється внизу вікна структури проекту. Це – вірний знак того, що ресурси підключені у проект. Зазначимо, що надалі до ресурсу можна буде звертатися за його ідентифікатором.
Рисунок 2.9 – Вигляд структури проекту із підключеним ресурсним файлом
Ідентифікатором елемента керування програми є ціле число, яким уні-кально позначається даний елемент керування. Наприклад, меню програми може мати ідентифікатор 101. Усі ідентифікатори зберігаються у спеціальному файлі проекту – resource.h. Але цей файл автоматично не включається у проект. Його треба включити вручну – директивою #include “resource.h”. Якщо зазирнути у текст цього файла, можна побачити рядки вигляду: #define IDR_MENU1 101 #define ID_FILE_EXIT 40001 Звичайно ідентифікатори створюються автоматично редактором ресурсів, але, за бажанням, їх можна задати і власноруч, додавши подібний запис у *.сpp файл проекту, або навіть у resource.h”.
2.5.2 Підключення меню Процес створення та застосування меню у MFC є досить простим. Насамперед необхідно створити графічний ресурс вашого меню, тобто визначити, які пункти входитимуть у меню. Далі, кожному пункту меню (який має власний унікальний ідентифікатор) за допомогою макрокоманди у карті повідомлень ставиться у відповідність функція-обробник. Якщо у класі головного вікна програми оголошено існування карти повідомлень, а у тексті програми реалізована хоча б і пуста карта повідомлень, можна переходити до визначення меню програми. Створення та підключення меню програми можна описати такою послідовністю: 1. Створіть графічний ресурс меню, використовуючи редактор ресурсів Visual C++. Визначте пункти меню програми. Діалог формування пункту меню зображено на рисунку 2.10.
Рисунок 2.10 – Визначення пункту меню головного вікна
Далі збережіть графічний ресурс меню (файл із розширенням *.rc) та додайте його у проект у спосіб, зазначений у п. 2.5.1. 2. У карті повідомлень програми кожному пункту меню, що поданий власним ідентифікатором, за допомогою макрокоманди ON_COMMAND() поставте у відповідність функцію-обробник, наприклад:
ON_COMMAND(ID_FILE_NEWITEM, OnNewItem) ON_COMMAND(ID_FILE_EXIT, OnExit)
Тобто тут пункту з ідентифікатором ID_FILE_NEWITEM відповідає функція OnNewItem(), а пункту ID_FILE_EXIT – функція OnExit(). 3. У класі головного вікна визначте (у спосіб, зазначений у пункті 1.2) прототипи обробників пунктів меню. Після цього клас вікна виглядатиме наступним чином:
Приклад 2.16 – Вигляд класу із обробниками пунктів меню class CMainWin: public CFrameWnd {public: void OnExit(); void OnNewItem(); CMainWin(); DECLARE_MESSAGE_MAP() };
4. Реалізуйте тексти обробників пунктів меню, наприклад:
void CMainWin:: OnExit() {int response=MessageBox(" Закінчити роботу", " Інформація", MB_ ICONQUESTION | MB_YESNO); if(response==IDYES)SendMessage(WM_CLOSE); }
5. Підключіть меню до виведення головного вікна програми. Це можна зробити у два способи, наведені у прикладах конструкторів головного меню:
Приклад 2.17 – Конструктор із макросом MAKEINTRESOURCE() CMainWin:: CMainWin() {Create(NULL, " Проста MFC-програма", WS_OVERLAPPEDWINDOW, rectDefault, NULL, MAKEINTRESOURCE(IDR_MENU1)); } Приклад 2.18 – Конструктор із об’єктом меню CMainWin:: CMainWin() { Create(NULL, " Проста MFC-програма "); my_menu.LoadMenu(IDR_MENU1); SetMenu(& my_menu); } Перший приклад виглядає дещо складнішим. В ньому завантаження меню забезпечується макросом MAKEINTRESOURCE() (зробити внутрішнім ресурс), який йде тільки 6-м параметром, що й створює довгий рядок параметрів. Другий приклад потребуватиме оголошення змінної для меню, що слід включити у клас головного вікна, наприклад:
CMenu my_menu;
Також тут необхідні два виклики функцій – завантаження ресурсу меню у відповідний об’єкт (my_menu. LoadMenu()) та підключення меню до вікна (SetMenu()). Приклад 2.19 містить повний текст програми із використання меню.
Приклад 2.19 – Повний текст програми із реалізацією обробників миші та меню головного вікна
// Main.h: interface for the classes. class CMainWin: public CFrameWnd {public: void OnExit(); void OnNewItem(); CMainWin(); void OnMouseMove(UINT flag, CPoint loc); void OnLButtonDown(UINT flag, CPoint loc); DECLARE_MESSAGE_MAP() private: CMenu my_menu; };
class CApp: public CWinApp {public: BOOL InitInstance(); };
// Main.cpp: implementation of the classes.
#include < afxwin.h> #include " Main.h" #include " resource.h"
CMainWin:: CMainWin() { Create(NULL, " Проста MFC-програма"); my_menu.LoadMenu(IDR_MENU2); SetMenu(& my_menu); } CApp App;
BEGIN_MESSAGE_MAP(CMainWin, CFrameWnd) ON_WM_LBUTTONDOWN() ON_WM_MOUSEMOVE() ON_COMMAND(ID_FILE_NEWITEM, OnNewItem) ON_COMMAND(ID_FILE_EXIT, OnExit) END_MESSAGE_MAP()
BOOL CApp:: InitInstance() {m_pMainWnd=new CMainWin; m_pMainWnd-> ShowWindow(m_nCmdShow); m_pMainWnd-> UpdateWindow(); return TRUE; }
void CMainWin:: OnLButtonDown(UINT flag, CPoint loc) { CClientDC dc(this); dc.TextOut(loc.x, loc.y, " Натиснута ліва клавіша миші "); }
void CMainWin:: OnMouseMove(UINT flag, CPoint loc) {CClientDC dc(this); dc.TextOut(loc.x, loc.y, " Курсор миші! "); }
void CMainWin:: OnNewItem() {MessageBox(" Обрано новий пункт меню"); }
void CMainWin:: OnExit() {int response=MessageBox(" Закінчити роботу", " Інформація", MB_ICONQUESTION | MB_YESNO); if(response==IDYES)SendMessage(WM_CLOSE)}
2.5.3 Використання акселераторів Акселератором є спеціальна спеціально визначена комбінація клавіш, що дозволяє прискорити виконання обробників програми. Акселератори є ресурсами, які визначаються у звичайний спосіб. При створенні ресурсу акселератора необхідно вказати необхідну ключову комбінацію клавіш клавіатури і зафіксувати призначений акселератору ідентифікатор (або обрати, наприклад, ідентифікатор відповідний пункту меню), як наведено на рисунку 2.11.
Рисунок 2.11 – Визначення акселератора (ключова комбінація Alt+E), відповідного пунктові меню із ідентифікатором ID_FILE_EXIT
В принципі акселератору можна поставити у відповідність будь-який обробник. Для цього достатньо вказати відповідність ідентифікатора та обробника у карті повідомлень головного вікна програми. Якщо акселератор визначено, його слід завантажити у конструкторі го-ловного вікна за допомогою функції LoadAccelTable(), як наведено у прикладі.
Приклад 2.20 – Конструктор головного вікна програми із завантаженням меню та акселераторів CMainWin:: CMainWin() { Create(NULL, " Проста MFC-програма"); my_menu.LoadMenu(IDR_MENU2); SetMenu(& my_menu); LoadAccelTable(MAKEINTRESOURCE(IDR_ACCELERATOR1)); }
2.6 Контрольні завдання
1. Охарактеризувати основні риси ієрархії MFC-класів. 2. Пояснити окремо роль класів прикладки та головного вікна у створенні MFC-програми. 3. Пояснити послідовність запуску і функціонування простої MFC-програми. 4. Пояснити порядок обробки повідомлень у MFC-програмах. 5. Пояснити особливості підключення меню до головного вікна програми. 6. Розробити просту MFC-програму. Дослідити вплив параметрів функції Create() класу CFrameWnd на параметри головного вікна програми. 7. Розробити просту MFC-програму. Реалізувати обробку повідомлень WM_CHAR, WM_MOVE, WM_SIZE. 8. Розробити просту MFC-програму із обробкою повідомлень миші. 9. Розробити просту MFC-програму із обробкою повідомлення таймера та. Реалізувати три таймери із виведенням часу із різними інтервалами. 10. Реалізувати MFC-програму із меню та акселераторами. Реалізувати пункти меню у вигляді виведення вікон повідомлень.
|