![]() |
Wyjście Spis treści Poprzedni Następny
Autor:
©Iczelion |
©2008 mgr
Jerzy Wałaszek
|
W tej lekcji zbadamy kontrolki okna dialogowego, które są bardzo ważnymi elementami do wprowadzania i wyprowadzania informacji w naszych programach.

Pobierz plik z przykładem {z tego archiwum}.
System Windows udostępnia kilka predefiniowanych klas okien, które możemy bezpośrednio użyć we własnych programach. Przez większość czasu używamy ich jako komponenty okna dialogowego, dlatego noszą nazwę kontrolek okna dialogowego (child window controls). Kontrolki te samodzielnie przetwarzają wiadomości od myszki i klawiatury powiadamiając okno nadrzędne o zmianie swojego stanu. Ogromnie ułatwiają życie programistom, więc powinieneś z nich korzystać tak często, jak to możliwe. W tej lekcji umieściłem je w zwykłym oknie, aby zademonstrować sposób ich tworzenia i użycia, lecz w rzeczywistości powinieneś umieszczać je w oknie dialogowym.
Przykładami predefiniowanych klas okien są przycisk (button), lista (listbox), pole wyboru (checkbox), pole wyboru wyłącznego (radio button), pole edycji (edit box) itp.
Aby używać kontrolkę okna dialogowego, musisz utworzyć ją przy pomocy wywołania CreateWindow lub CreateWindowEx. Zwróć uwagę, iż nie musisz już rejestrować klasy okna, ponieważ została ona już zarejestrowana dla ciebie przez system Windows. Nazwa klasy MUSI być predefiniowaną nazwą klasy. Powiedzmy, iż chcesz utworzyć przycisk, zatem musisz określić nazwę klasy dla CreateWindowEx jako "button". Pozostałymi parametrami, które należy wpisać, są uchwyt okna nadrzędnego oraz numer kontrolki ID. Numer ten nie może się powtarzać wśród kontrolek okna. Służy on do identyfikacji i rozróżnienia kontrolek.
Po utworzeniu kontrolki będzie ona wysyłała wiadomości do swojego okna nadrzędnego, gdy zmieni się jej stan. Zwykle okna potomne tworzymy podczas przetwarzania wiadomości WM_CREATE okna nadrzędnego. Okno potomne wysyła wiadomości WM_COMMAND do okna nadrzędnego ze swoim numerem ID w młodszym słowie wParam i kodem powiadomienia w starszym słowie wParam, a swoim uchwytem okna w lParam. Każda kontrolka okna dialogowego posiada swój własny zbiór kodów powiadamiania. Więcej informacji na ten temat musisz poszukać w pliku pomocy dla funkcji API Win32.
Okno nadrzędne może również wysyłać wiadomości do swoich okien potomnych przez wywołanie funkcji SendMessage, która przesyła wiadomości wraz z towarzyszącymi im wartościami w wParam i lParam do okna określonego przez uchwyt okna (window handle). Jest to niesamowicie użyteczna funkcja, ponieważ potrafi ona wysłać wiadomość do dowolnego okna, o ile znasz jego uchwyt.
Zatem po utworzeniu okien potomnych okno nadrzędne musi przetwarzać wiadomości WM_COMMAND, aby było w stanie otrzymywać kody powiadomień z okien potomnych.
Utworzymy okno zawierające kontrolkę edycyjną oraz przycisk. Gdy klikniesz w przycisk, pojawi się okno wiadomości pokazujące wprowadzony do pola edycyjnego tekst. Występuje również menu z 4 elementami:
.386
.MODEL FLAT, STDCALL
OPTION CASEMAP:NONE
WinMain PROTO :DWORD, :DWORD, :DWORD, :DWORD
INCLUDE \masm32\include\windows.inc
INCLUDE \masm32\include\user32.inc
INCLUDE \masm32\include\kernel32.inc
INCLUDELIB \masm32\lib\user32.lib
INCLUDELIB \masm32\lib\kernel32.lib
.DATA
ClassName DB "SimpleWinClass",0
AppName DB "Nasze pierwsze okno",0
MenuName DB "FirstMenu",0
ButtonClassName DB "button",0
ButtonText DB "Nasz pierwszy przycisk",0
EditClassName DB "edit",0
TestString DB "Och! Jestem w edytorze",0
.DATA?
hInstance HINSTANCE ?
CommandLine LPSTR ?
hwndButton HWND ?
hwndEdit HWND ?
buffer DB 512 DUP(?)
.CONST
ButtonID EQU 1
EditID EQU 2
IDM_HELLO EQU 1
IDM_CLEAR EQU 2
IDM_GETTEXT EQU 3
IDM_EXIT EQU 4
.CODE
start:
INVOKE GetModuleHandle, NULL
mov hInstance, eax
INVOKE GetCommandLine
mov CommandLine, eax
INVOKE WinMain, hInstance, NULL, CommandLine, SW_SHOWDEFAULT
INVOKE ExitProcess, eax
WinMain PROC hInst: HINSTANCE,\
hPrevInst: HINSTANCE,\
CmdLine: LPSTR,\
CmdShow: DWORD
LOCAL wc: WNDCLASSEX
LOCAL msg: MSG
LOCAL hwnd: HWND
mov wc.cbSize, SIZEOF WNDCLASSEX
mov wc.style, CS_HREDRAW OR CS_VREDRAW
mov wc.lpfnWndProc, OFFSET WndProc
mov wc.cbClsExtra, NULL
mov wc.cbWndExtra, NULL
push hInst
pop wc.hInstance
mov wc.hbrBackground, COLOR_BTNFACE+1
mov wc.lpszMenuName, OFFSET MenuName
mov wc.lpszClassName, OFFSET ClassName
INVOKE LoadIcon, NULL, IDI_APPLICATION
mov wc.hIcon, eax
mov wc.hIconSm, eax
INVOKE LoadCursor, NULL, IDC_ARROW
mov wc.hCursor, eax
INVOKE RegisterClassEx, ADDR wc
INVOKE CreateWindowEx, WS_EX_CLIENTEDGE,\
ADDR ClassName, ADDR AppName,\
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT,\
CW_USEDEFAULT, 300, 200, NULL, NULL,\
hInst, NULL
mov hwnd, eax
INVOKE ShowWindow, hwnd, SW_SHOWNORMAL
INVOKE UpdateWindow, hwnd
.WHILE TRUE
INVOKE GetMessage, ADDR msg, NULL, 0, 0
.BREAK .IF (!eax)
INVOKE TranslateMessage, ADDR msg
INVOKE DispatchMessage, ADDR msg
.ENDW
mov eax, msg.wParam
ret
WinMain ENDP
WndProc PROC hWnd: HWND, uMsg: UINT, wParam: WPARAM, lParam: LPARAM
.IF uMsg==WM_DESTROY
INVOKE PostQuitMessage, NULL
.ELSEIF uMsg==WM_CREATE
INVOKE CreateWindowEx, WS_EX_CLIENTEDGE,\
ADDR EditClassName, NULL,\
WS_CHILD OR WS_VISIBLE OR\
WS_BORDER OR ES_LEFT OR\
ES_AUTOHSCROLL,\
50, 35, 200, 25, hWnd,\
EditID, hInstance, NULL
mov hwndEdit, eax
INVOKE SetFocus, hwndEdit
INVOKE CreateWindowEx, NULL,\
ADDR ButtonClassName, ADDR ButtonText,\
WS_CHILD OR WS_VISIBLE OR BS_DEFPUSHBUTTON,\
50, 70, 200, 25, hWnd, ButtonID,\
hInstance, NULL
mov hwndButton, eax
.ELSEIF uMsg==WM_COMMAND
mov eax, wParam
.IF lParam==0
.IF ax==IDM_HELLO
INVOKE SetWindowText, hwndEdit, ADDR TestString
INVOKE SendMessage, hwndEdit, WM_KEYDOWN, VK_END, NULL
.ELSEIF ax==IDM_CLEAR
INVOKE SetWindowText, hwndEdit, NULL
.ELSEIF ax==IDM_GETTEXT
INVOKE GetWindowText, hwndEdit, ADDR buffer, 512
INVOKE MessageBox, NULL, ADDR buffer, ADDR AppName, MB_OK
.ELSE
INVOKE DestroyWindow, hWnd
.ENDIF
.ELSE
.IF ax==ButtonID
shr eax, 16
.IF ax==BN_CLICKED
INVOKE SendMessage, hWnd, WM_COMMAND, IDM_GETTEXT, 0
.ENDIF
.ENDIF
.ENDIF
.ELSE
INVOKE DefWindowProc, hWnd, uMsg, wParam, lParam
ret
.ENDIF
xor eax, eax
ret
WndProc ENDP
END start
Plik zasobów:
#define IDM_HELLO 1
#define IDM_CLEAR 2
#define IDM_GETTEXT 3
#define IDM_EXIT 4
FirstMenu MENU
{
POPUP "&Testowe Kontrolki"
{
MENUITEM "Powiedz &Cześć", IDM_HELLO
MENUITEM "&Wyczyść pole edycyjne", IDM_CLEAR
MENUITEM "&Pobierz tekst", IDM_GETTEXT
MENUITEM SEPARATOR
MENUITEM "&Koniec", IDM_EXIT
}
}
Przeanalizujmy ten program.
.ELSEIF uMsg==WM_CREATE
INVOKE CreateWindowEx, WS_EX_CLIENTEDGE,\
ADDR EditClassName, NULL,\
WS_CHILD OR WS_VISIBLE OR\
WS_BORDER OR ES_LEFT OR\
ES_AUTOHSCROLL,\
50, 35, 200, 25, hWnd,\
EditID, hInstance, NULL
mov hwndEdit, eax
INVOKE SetFocus, hwndEdit
INVOKE CreateWindowEx, NULL,\
ADDR ButtonClassName, ADDR ButtonText,\
WS_CHILD OR WS_VISIBLE OR BS_DEFPUSHBUTTON,\
50, 70, 200, 25, hWnd, ButtonID,\
hInstance, NULL
mov hwndButton, eax
Kontrolki tworzymy podczas przetwarzania wiadomości WM_CREATE.
Funkcję
CreateWindowEx wywołujemy z dodatkowym stylem
okna -
WS_EX_CLIENTEDGE, który sprawia, iż obszar
roboczy będzie wyglądał na zagłębiony. Nazwa każdej kontrolki jest
predefiniowana, "edit" dla pola edycji, "button"
dla kontrolki przycisku. Następnie określamy style okna potomnego. Każda
kontrolka posiada dodatkowe style oprócz zwykłych styli okna. Na przykład style
przycisku mają przedrostek "BS_" od angielskiej nazwy "button
style", style pola edycyjnego mają przedrostek "ES_"
od "edit style". Opisu tych styli musisz poszukać w pliku
pomocy dla API Win32. Zwróć uwagę, iż w miejscu uchwytu
menu umieszczasz numer ID kontrolki. Nie powoduje to
żadnych zakłóceń, ponieważ okna potomne nie mogą posiadać menu.
Po utworzeniu każdej kontrolki zachowujemy jej uchwyt w zmiennej dla przyszłego użytku.
Funkcja SetFocus jest wywoływana, aby uaktywnić pole edycji, co umożliwi użytkownikowi natychmiastowe rozpoczęcie wprowadzania tekstu.
Teraz nadchodzi naprawdę interesujący moment. Każde okno potomne wysyła powiadomienia do swojego okna nadrzędnego za pomocą wiadomości WM_COMMAND.
.ELSEIF uMsg==WM_COMMAND
mov eax, wParam
.IF lParam==0
Przypomnij sobie, iż menu również wysyła wiadomości
WM_COMMAND, aby poinformować okno o swoim stanie. Jak rozróżnić
wiadomości
WM_COMMAND
pochodzące od menu i od kontrolki? Poniżej jest odpowiedź.
| Młodsze słowo wParam | Starsze słowo wParam | lParam | |
| Menu | Numer ID elementu menu | 0 | 0 |
| Kontrolka | Numer ID kontrolki | Kod powiadomienia | Uchwyt Okna Potomnego |
Wynika stąd, iż musisz sprawdzić wartość lParam. Jeśli wynosi zero, to bieżąca wiadomość WM_COMMAND pochodzi od elementu menu. Nie można zastosować wParam do rozróżnienia pomiędzy menu a kontrolką, ponieważ numery ID elementu menu i kontrolki mogą być identyczne, a kod powiadomienia może mieć wartość zero.
.IF ax==IDM_HELLO
INVOKE SetWindowText, hwndEdit, ADDR TestString
INVOKE SendMessage, hwndEdit, WM_KEYDOWN, VK_END, NULL
.ELSEIF ax==IDM_CLEAR
INVOKE SetWindowText, hwndEdit, NULL
.ELSEIF ax==IDM_GETTEXT
INVOKE GetWindowText, hwndEdit, ADDR buffer, 512
INVOKE MessageBox, NULL, ADDR buffer, ADDR AppName, MB_OK
W polu edycyjnym można umieścić łańcuch tekstowy wywołując funkcję
SetWindowText. Zawartość pola edycyjnego czyścimy wywołując
SetWindowText z parametrem NULL. Funkcja
ta jest ogólną funkcją API. Możesz jej używać do
zmiany tytułu okna lub napisu na przycisku.
Odczyt tekstu z pola edycyjnego uzyskujemy za pomocą funkcji GetWindowText.
.IF ax==ButtonID
shr eax, 16
.IF ax==BN_CLICKED
INVOKE SendMessage, hWnd, WM_COMMAND, IDM_GETTEXT, 0
.ENDIF
.ENDIF
Powyższy fragment kodu obsługuje przypadek, gdy użytkownik naciska nasz
przycisk. Najpierw sprawdza on, czy młodsze słowo wParam
odpowiada numerowi ID przycisku. Jeśli tak, to
sprawdza starsze słowo, czy jest ono równe kodowi powiadomienia o
kliknięciu przycisku - BN_CLICKED. Teraz nastąpi
interesujący moment. Chcemy pobrać tekst z pola edycyjnego i wyświetlić
go w okienku wiadomości. Możemy zdublować kod w powyższej sekcji
IDM_GETTEXT, lecz to nie ma sensu. Jeśli moglibyśmy w jakiś
sposób przesłać wiadomość WM_COMMAND z młodszym
słowem wParam zawierającym wartość
IDM_GETTEXT do naszej własnej procedury okna, moglibyśmy wtedy
uniknąć dublowania kodu i uprościć nasz program. Rozwiązaniem jest
funkcja SendMessage, która wysyła dowolną
wiadomość do dowolnego okna z dowolnymi wartościami
wParam i lParam. Zatem zamiast dublowania
kodu wywołujemy SendMessage z uchwytem okna
nadrzędnego, WM_COMMAND,
IDM_GETTEXT i 0. Ma to identyczny efekt jak wybranie z menu opcji
"Pobierz tekst". Procedura okna nie wykryje
między nimi żadnej różnicy.
Powinieneś intensywnie wykorzystywać tę technikę, aby twój kod był bardziej zwarty.
I ostatnia, ale nie mniej ważna uwaga, nie zapomnij umieścić w pętli wiadomości wywołania funkcji TranslateMessage. Ponieważ musisz wpisywać jakiś tekst do pola edycyjnego, twój program musi przekształcać kody matrycowe klawiatury na czytelny tekst. Jeśli pominiesz tę funkcję, nie będziesz mógł nic wpisać w swoje pole edycyjne.
Ta sama aplikacja w Pascalu:
{********************************
** I Liceum Ogólnokształcące **
** w Tarnowie **
** mgr Jerzy Wałaszek **
********************************}
program DialogControls;
uses Windows;
const
ClassName = 'SimpleWinClass';
AppName = 'Nasze pierwsze okno';
MenuName = 'FirstMenu';
ButtonClassName = 'button';
ButtonText = 'Nasz pierwszy przycisk';
EditClassName = 'edit';
TestString = 'Och! Jestem w edytorze';
ButtonID = 1;
EditID = 2;
IDM_HELLO = 1;
IDM_CLEAR = 2;
IDM_GETTEXT = 3;
IDM_EXIT = 4;
var
hInstance : HINST;
CommandLine : LPSTR;
hwndButton : HWND;
hwndEdit : HWND;
buffer : array[0..511] of byte;
function WndProc(h:HWND;uMsg:UINT;wP:WPARAM;lP:LPARAM) : longint;
begin
WndProc := 0;
case uMsg of
WM_DESTROY: PostQuitMessage(0);
WM_CREATE :
begin
hwndEdit := CreateWindowEx( WS_EX_CLIENTEDGE,EditClassName,0,
WS_CHILD or WS_VISIBLE or
WS_BORDER or ES_LEFT or
ES_AUTOHSCROLL,50,35,200,25,h,
EditID,hInstance,0);
SetFocus(hwndEdit);
hwndButton := CreateWindowEx(0,ButtonClassName,ButtonText,
WS_CHILD or WS_VISIBLE or BS_DEFPUSHBUTTON,
50,70,200,25,h, ButtonID,hInstance,Nil);
end;
WM_COMMAND :
begin
if lP = 0 then
case (wP and $ffff) of
IDM_HELLO: begin
SetWindowText(hwndEdit,TestString);
SendMessage(hwndEdit,WM_KEYDOWN,VK_END,0);
end;
IDM_CLEAR: SetWindowText(hwndEdit,0);
IDM_GETTEXT : begin
GetWindowText(hwndEdit,@buffer,512);
MessageBox(0,@buffer,AppName,MB_OK);
end;
else DestroyWindow(h);
end
else if ((wp and $ffff) = ButtonID) and
((wP shr 16) = BN_CLICKED) then SendMessage(h,WM_COMMAND,IDM_GETTEXT,0);
end;
else WndProc := DefWindowProc(h,uMsg,wP,lP);
end;
end;
function WinMain(hI,hPI:HINST;CmdLine:LPSTR;cmdShow:longint) : longint;
var
wc : WndClassEx;
ms : msg;
h : HWnd;
begin
wc.cbSize := SizeOf(WndClassEx);
wc.style := CS_HREDRAW or CS_VREDRAW;
wc.lpfnWndProc := @WndProc;
wc.cbClsExtra := 0;
wc.cbWndExtra := 0;
wc.hInstance := hI;
wc.hbrBackground := COLOR_BTNFACE + 1;
wc.lpszMenuName := MenuName;
wc.lpszClassName := ClassName;
wc.hIcon := LoadIcon(0,IDI_APPLICATION);
wc.hIconSm := wc.hIcon;
wc.hCursor := LoadCursor(0,IDC_ARROW);
RegisterClassEx(wc);
h := CreateWindowEx(0,ClassName,AppName,
WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,
300,200,0,0,hI,0);
ShowWindow(h,CmdShow);
UpdateWindow(h);
while GetMessage(ms,0,0,0) do
begin
TranslateMessage(ms);
DispatchMessage(ms);
end;
WinMain := ms.wParam;
end;
begin
hInstance := GetModuleHandle(Nil);
CommandLine := GetCommandLine;
ExitProcess(WinMain(hInstance,0,CommandLine,SW_SHOWDEFAULT));
end.
|
Autorem
kursu jest
Iczelion. Kurs
programowania Windows znalazł się na serwerze I LO w Tarnowie za pisemną
zgodą autora. |
![]() | I Liceum Ogólnokształcące |
Pytania proszę przesyłać na adres email: i-lo@eduinf.waw.pl
W artykułach serwisu są używane cookies. Jeśli nie chcesz ich otrzymywać,
zablokuj je w swojej przeglądarce.
Informacje dodatkowe