Lab_progr_Win / Lab5
.docЛабораторна робота №5.
Тема роботи: Створення дочірніх елементів керування: кнопки, прапорці, перемикачі, вікна редагування.
Ціль роботи: Придбання досвіду створення прикладного програмного забезпечення для Windows з використанням дочірніх елементів керування.
ТЕОРЕТИЧНА ЧАСТИНА
Будь-який стандартний додаток Windows використовує різні елементи керування, такі, як кнопки, смуги перегляду, редактори текстів і т.д, реалізовані у вигляді дочірніх вікон.
Тому що дочірні вікна розташовуються на поверхні вікна батька, “прилипаючи” до них, додаток може створити в будь-якому своєму вікні кілька елементів керування, що будуть переміщатися разом з вікном-батьком. Для цього досить створити потрібні дочірні вікна, указавши їхні розміри, розташування і деякі інші атрибути. Після цього додаток може взаємодіяти з елементами керування, передаючи чи одержуючи від них різні повідомлення.
Кожне дочірнє вікно створюється за допомогою виклику функції CreateWindow. Віконна процедура батьківського вікна посилає повідомлення дочірнім вікнам керування, а дочірні вікна керування посилають повідомлення назад віконній процедурі.
Дочірнє вікно керування обробляє повідомлення миші і клавіатури і сповіщає батьківське вікно про те, що стан дочірнього вікна змінився. У цьому випадку дочірнє вікно стає для батьківського вікна пристроєм уведення. Воно інкапсулює особливі дії, зв'язані з графічним представленням вікна на екрані, реакцією на користувальницьке введення, і повідомлення іншого вікна при уведенні важливої інформації.
Можна створювати свої власні дочірні елементи керування, але є також можливість використовувати переваги декількох уже визначених класів вікна (і віконних процедур), за допомогою яких додаток може створювати стандартні дочірні вікна керування, що є присутні звичайно у всіх Windows-додатках.
Стандартні дочірні вікна керування мають вид кнопок, прапорців, вікон редагування, списків, комбінованих списків, рядків тексту і смуг прокручування. Додатку немає необхідності турбуватися про логіка обробки миші цими вікнами, чи про логіка їх відмальовування. Усе це робиться в Windows, а усе, що залишається додатку – це обробляти повідомлення WM_COMMAND, якими дочірні вікна інформують віконну процедуру про різні події.
Якщо створюється одне з визначених дочірніх вікон керування, то для цього дочірнього вікна клас вікна реєструвати не треба. Такий клас вже існує в Windows і має одне з наступних імен: “button”, ”edit”, “static”, “listbox”, “combobox” і “scrollbar”. Додаток лише використовує одне з цих імен як параметр у функції CreateWindow.
Параметр стилю вікна функції CreateWindow більш точно визначає вид і властивості дочірнього вікна керування. Константи, що ідентифікують стилі визначених класів елементів керування, визначені в заголовних файлах Windows і мають відповідно наступні префікси: BS_ - “button”, ES_ - ”edit”, SS_ - “static”, LBS_ - “listbox”, CBS_ - “combobox” і SBS_ - “scrollbar”.
Windows містить у собі віконні процедури, що обробляють повідомлення тих дочірніх вікон, що створені на основі перерахованих класів.
Викликая спеціальні функції, можна
-
динамічно переміщати елемент керування (MoveWindow - функція визначає нове розташування і розміри вікна в системі координат, зв'язаної з батьківським вікном),
-
робити його активним чи неактивним (EnableWindow, для визначення, чи є вікно заблокованим, використовується функція IsWindowEnabled),
-
ховати його чи відображати (ShowWindow),
-
змінювати заголовок вікна (SetWindowText),
-
знищити створений елемент керування (DestroyWindow).
Віконна процедура може посилати повідомлення дочірньому вікну керування. П'ять спеціальних повідомлень для кнопок визначені в заголовних файлах Windows, кожне з який починається з префікса "BM", що означає "button message" (повідомлення кнопки). От ці повідомлення:
BM_GETCHECK
BM_SETCHECK
BM_GETSTATE
BM_SETSTATE
BM_SETSTYLE
Повідомлення BM_GETCHECK і BM_SETCHECK посилаються батьківським вікном дочірньому вікну керування для установки і зняття (тобто одержання поточного стану) контрольних міток прапорців (check boxes) і перемикачів (radio buttons). Повідомлення BM_GETSTATE і BM_SETSTATE стосуються звичайного чи "натиснутого" стану вікна при щиглику чи мишею натисканні клавіші <Spacebar>. Повідомлення BM_SETSTYLE дозволяє вам змінювати стиль кнопки після її створення.
Відправлення повідомлень батьківському вікну дочірнім і навпаки, здійснюється за допомогою функції SendMessage(), що у загальному виді виглядає у такий спосіб:
LRESULT SendMessage(
HWND hWnd, // дескриптор вікна призначення
UINT Msg, // повідомлення, що відправляється
WPARAM wParam, // перший параметр повідомлення
LPARAM lParam // другий параметр повідомлення
);
Для приклада створимо додаток, що використовує такі дочірні елементи керування як кнопки, прапорці, перемикачі і вікна редагування. У вікні редагування буде вводитися рядок символів, що по натисканню кнопки «Результат», у залежності від обраних прапорців і перемикачів, буде піддаватися визначеним змінам.
Зовнішній вигляд програми буде наступним:
Рисунок 2.6 Зовнішній вигляд програми CONTROLS.
Створення додатка, що використовує діалоги.
-
Створіть новий проект CONTROLS.
-
Для початку створіть файл controls.cpp і скопіюйте в нього вміст файлу sample.cpp, що був створений у Лабораторній роботі №1.
-
Крім заголовного файлу windows.h не знадобляться ніякі інші заголовні файли.
-
Функцію WinMain варто піддати наступним незначним змінам:
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
static char szAppName[] = " Controls" ;
MSG msg ;
WNDCLASS wndclass ;
…
RegisterClass (&wndclass) ;
hWnd = CreateWindow (szAppName, " Simple Controls ", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL) ;
…
-
Функцію WndProc необхідно піддати більш ретельній обробці. Дочірні елементи керування будуть створюватися відразу по створенню вікна. Отже, вони повинні створюватися при обробці повідомлення WM_CREATE. Створіть статичний елемент керування з текстом і вікно редагування для введення рядка символів, додавши до програми наступний код:
#include <windows.h>
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
HWND hWnd, hStatic1, hWndEdit1;
UINT ID_edit1=1;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
char szAppName[]="Controls";
…
case WM_CREATE:
hStatic1=CreateWindow("static","Уведіть рядок символів: ", WS_CHILD|WS_VISIBLE |WS_CLIPSIBLINGS| ES_LEFT, 25,50,200,25,hwnd,NULL, ((LPCREATESTRUCT) lParam) -> hInstance,NULL);
hWndEdit1=CreateWindow("edit",NULL, WS_CHILD|WS_VISIBLE|WS_CLIPSIB-LINGS|WS_BORDER|ES_LEFT,250,50,200,25,hwnd,(HMENU)ID_edit1,((LPCREATESTRUCT) lParam) -> hInstance,NULL);
return 0;
case WM_PAINT :
hdc = BeginPaint (hwnd, &ps) ;
EndPaint (hwnd, &ps) ;
return 0 ;
-
Для створення групи перемикачів, додайте до оброблювача події WM_CREATE наступний код:
#include <windows.h>
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
HWND hWnd, hWndEdit1, hStatic1, hGroup1, hRadio1, hRadio2;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
char szAppName[]="Controls";
…
case WM_CREATE:
hStatic1=CreateWindow("static","Уведіть рядок символів: ", WS_CHILD|WS_VISIBLE |WS_CLIPSIBLINGS| ES_LEFT, 25,50,200,25,hwnd,NULL, ((LPCREATESTRUCT) lParam) -> hInstance,NULL);
hWndEdit1=CreateWindow("edit",NULL,WS_CHILD|WS_VISIBLE|WS_CLIPSIBLINGS|WS_BORDER| ES_LEFT,250,50,200,25,hwnd,(HMENU)ID_edit1,((LPCREATESTRUCT) lParam) -> hInstance,NULL);
hGroup1=CreateWindow("button","Група перемикачів:", WS_CHILD| WS_VISIBLE|BS_GROUPBOX,25,100,200,100,hwnd,NULL,((LPCREATESTRUCT) lParam) -> hInstance,NULL);
hRadio1=CreateWindow("button","Верхній регістр", WS_CHILD| WS_VISIBLE|BS_AUTORADIOBUTTON, 35,133,150,20,hwnd,NULL,((LPCREATE-STRUCT) lParam) -> hInstance,NULL);
SendMessage(hRadio1, BM_SETCHECK, 1, 0);
hRadio2=CreateWindow("button","Нижній регістр", WS_CHILD| WS_VISIBLE| BS_AUTORADIOBUTTON ,35,166,150,20, hwnd, NULL, ((LPCREATE-STRUCT) lParam) -> hInstance,NULL);
return 0 ;
-
Для створення групи прапорців, додайте до оброблювача події WM_CREATE наступний код:
#include <windows.h>
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
HWND hWnd, hWndEdit1, hStatic1, hGroup1, hRadio1, hRadio2, hGroup2, hCheckBox1, hCheckBox2,;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
char szAppName[]="Controls";
…
case WM_CREATE:
…
hRadio2=CreateWindow("button","Нижній регістр", WS_CHILD| WS_VISIBLE| BS_AUTORADIOBUTTON,35,166,150,20,hwnd,NULL, ((LPCREATESTRUCT) lParam) -> hInstance,NULL);
hGroup2=CreateWindow("button","Група прапорців:", WS_CHILD|WS_VISIBLE| BS_GROUPBOX, 250,100,200,100,hwnd,NULL,((LPCREATESTRUCT) lParam) -> hInstance,NULL);
hCheckBox1=CreateWindow("button","Додати ~-!-~", WS_CHILD|WS_VISIBLE |BS_AUTOCHECKBOX, 260,133,150,20,hwnd,NULL,((LPCREATESTRUCT) lParam) -> hInstance,NULL);
hCheckBox2=CreateWindow("button","Додати ~_-_~", WS_CHILD| WS_VISIBLE| BS_AUTOCHECKBOX,260,166,150,20,hwnd,NULL,((LPCREATESTRUCT) lParam) -> hInstance,NULL);
return 0 ;
-
І нарешті створіть дві кнопок. Одну для одержання результату, а другу для виходу з програми.
#include <windows.h>
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
HWND hWnd, hWndEdit1, hStatic1, hGroup1, hRadio1, hRadio2, hGroup2, hCheckBox1, hCheckBox2, hButton1, hButton2;
UINT ID_edit1=1, ID_button1=2, ID_button2=3;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
char szAppName[]="Controls";
…
case WM_CREATE:
…
hCheckBox2=CreateWindow("button","Додати ~_-_~", WS_CHILD| WS_VISIBLE| BS_AUTOCHECKBOX,260,166,150,20,hwnd,NULL,((LPCREATESTRUCT) lParam) -> hInstance,NULL);
hButton1=CreateWindow("button", "Результат", WS_CHILD|WS_VISIBLE| WS_CLIPSIBLINGS|BS_PUSHBUTTON, 100,250,100,25,hwnd,(HMENU)ID_button1, ((LPCREATESTRUCT) lParam) -> hInstance,NULL);
hButton2=CreateWindow("button","Вихід",WS_CHILD|WS_VISIBLE|WS_CLIPSIBLINGS|BS_PUSHBUTTON,300,250,100,25,hwnd,(HMENU)ID_button2, ((LPCREATESTRUCT) lParam) -> hInstance,NULL);
return 0 ;
-
Т.к. головне вікно програми містить дочірні елементи керування, то в програмі потрібно обробляти повідомлення WM_COMMAND. Ідентифікатор дочірнього вікна елемента керування знаходиться в молодшому слові параметра wParam. По логіці програми, нас цікавлять тільки повідомлення головному вікну, що надходять від кнопок:
LRESULT CALLBACK WndProc (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam) {
…
switch (iMsg)
{
case WM_COMMAND:
if(LOWORD(wParam)==ID_button1) { … } // натиснуто кнопку «Результат»
if(LOWORD(wParam)==ID_button2) { … } // натиснуто кнопку «Вихід»
return 0 ;
case WM_PAINT :
hdc = BeginPaint (hwnd, &ps) ;
…
-
У випадку, якщо натиснута кнопка «Результат», необхідно обробляти введений рядок:
LRESULT CALLBACK WndProc (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
HDC hdc ;
PAINTSTRUCT ps ;
char buf1[32] ="", buf2[32]="Результат: ", str1[4]="-!-", str2[4]="_-_";
int iCheck;
case WM_COMMAND:
if(LOWORD(wParam)==ID_button1) {
GetWindowText(hWndEdit1, buf1, 32);
if(iCheck=(int)SendMessage(hRadio1, BM_GETCHECK, 0, 0))CharUpper(buf1);
if(iCheck=(int)SendMessage(hRadio2, BM_GETCHECK, 0, 0))CharLower(buf1);
if(iCheck=(int)SendMessage(hCheckBox1, BM_GETCHECK, 0, 0)) strcat(buf1,str1);
if(iCheck=(int)SendMessage(hCheckBox2, BM_GETCHECK, 0, 0)) strcat(buf1,str2);
strcat(buf2,buf1);
MessageBox(hwnd, buf2,"Result", MB_ICONQUESTION|MB_OKCANCEL);
}
return 0 ;
У даному фрагменті коду відбувається наступне: для початку, зчитуємо поточний текст вікна редагування в змінну buf1 за допомогою функції GetWindowText(). Ця функція в загальному виді виглядає так:
GetWindowText (hwnd, pszBuffer, iMaxLength);
Параметр iMaxLength задає максимальне число символів для копіювання в буфер, що визначається покажчиком pszBuffer. Hwnd — дескриптор вікна з якого зчитується текст. Значенням функції, що повертається, є довжина скопійованого рядка.
Наступні п'ять рядків коду формують результуючий рядок.
Поточний стан прапорців і перемикачів можна довідатися за допомогою відправлення повідомлення BM_GETCHECK потрібному дочірньому вікну керування. Якщо обраний перший перемикач, то результуюча рядок буде виведена на екран у верхньому регістрі (функція CharUpper()), а якщо другий — у нижньому (функція CharLower ()). За допомогою прапорців до результату можна додати визначені рядки: якщо обраний перший прапорець, то додається «-!-», якщо другий — «_-_», а якщо обрані обидва прапорці, то додаються обидві ці рядки.
Потім за допомогою операції конкатенації в зміннії buf2 формується остаточний результат, що згодом виводиться на екран за допомогою функції MessageBox().
-
У випадку, якщо натиснута кнопка «Вихід», необхідно здійснити вихід із програми. Але можна передбачити випадок, коли кнопка буде натиснута випадково. Для цього варто одержати від користувача підтвердження виходу:
case WM_COMMAND:
if(LOWORD(wParam)==ID_button1) {
MessageBox(hwnd, buf2,"Result", MB_ICONQUESTION|MB_OKCANCEL);
}
if(LOWORD(wParam)==ID_button2) if (MessageBox(hwnd, "Ви дійсно хочете вийти?", "Exit", MB_ICONQUESTION|MB_OKCANCEL)==IDOK)PostQuitMessage (0) ;
return 0 ;
Програма готова. Запустіть її на виконання.
ПРАКТИЧНА ЧАСТИНА
-
Створіть програму controls, описану в лабораторній роботі.
-
Написати програму, що має наступний зовнішній вигляд:
Рисунок 2.7 Зовнішній вигляд програми калькулятору
При натисканні ОК у поле редагування Edit3 заноситься відповідний результат обчислення.
Контрольні питання:
-
Чи є елементи керування вікнами? Як вони створюються, чи можуть вони спілкуватися за допомогою повідомлень зі своїм батьківським вікном?
-
На базі яких класів створюються стандартні елементи керування Windows?
-
Для чого при створенні елемента керування додаток повинний повідомити Windows дескриптор батьківського вікна й ідентифікатор створюваного елемента керування?
-
Що таке дескриптор елемента керування й ідентифікатор елемента керування?
-
Яке повідомлення звичайне приходить батьківському вікну, якщо щось відбувається з елементом керування? Яку додаткову інформацію несе із собою це повідомлення?
-
Що таке код повідомлення? Що він ідентифікує?
-
Якими способами батьківське вікно може передавати повідомлення елементам керування? Чим вони відрізняються?