Wyjście Spis treści Poprzedni Następny
Autor:
©Iczelion |
©2008 mgr
Jerzy Wałaszek |
|
Na tej lekcji nauczymy się, czym są typowe kontrolki (common controls) oraz jak się ich używa. Potraktuj tę lekcję jedynie jako szybkie i zgrubne wprowadzenie do tematu.
Pobierz plik z przykładem {z tego archiwum}.
System Windows 95 został wyposażony w kilka rozszerzeń interfejsu użytkownika w stosunku do systemu Windows 3.1x. Rozszerzenia te wzbogacają graficzny interfejs użytkownika (GUI). Kilka z nich było szeroko używane jeszcze przed nadejściem systemu Windows 95, np. pasek stanu, paski narzędziowe, itp. Programiści musieli je kodować sami. Teraz firma Microsoft dołączyła je do swojego systemu Windows 9x/NT/2000/XP. Poznamy je na tej lekcji.
Oto zestawienie nowych kontrolek:
Ponieważ kontrolek tych jest tak dużo, to ładowanie ich wszystkich do pamięci i rejestrowanie byłoby marnotrawstwem zasobów. Wszystkie opisane powyżej kontrolki z wyjątkiem Rich edit są przechowywane w bibliotece comctl32.dll, którą aplikacje mogą załadować, gdy chcą używać typowych kontrolek. Kontrolka tekstu formatowanego (Rich edit) posiada swoją własną bibliotekę o nazwie richedXX.dll, ponieważ jest bardzo skomplikowana (to w sumie kompletny edytor tekstu) i stąd dużo większa od jej współplemieńców.
Aby upewnić się, iż biblioteka comctl32.dll została poprawnie załadowana, powinieneś na początku swojego programu wywołać funkcję InitCommonControls. Obecnie funkcja ta nic nie robi. Zawiera tylko pojedynczy rozkaz ret. Jedynym jej celem jest umieszczenie odwołania do comctl32.dll w sekcji importu, aby program ładujący systemu Windows załadował tę bibliotekę z naszym programem. Funkcja ta nie musi być nawet uruchamiana - wystarczy, że występuje w twoim programie. Czarną robotę wykonuje funkcja wejściowa biblioteki DLL, która rejestruje wszystkie klasy typowych kontrolek przy załadowaniu biblioteki do pamięci. Typowe kontrolki tworzy się na podstawie tych klas w identyczny sposób jak kontrolki okien dialogowych, takie jak pola edycji, pola list itp.
Zupełnie osobną sprawą jest edytor tekstu formatowanego Rich edit. Jeśli chcesz go zastosować, musisz skorzystać z wywołania LoadLibrary, a później z FreeLibrary przy usuwaniu kontrolki z pamięci.
A teraz o tworzeniu typowych kontrolek. Możesz skorzystać z edytora zasobów do umieszczenia ich w oknach dialogowych lub możesz utworzyć je samodzielnie. Prawie wszystkie typowe kontrolki są tworzone przez wywołanie funkcji CreateWindowEx lub CreateWindow i przekazanie im nazwy klasy kontrolki jako jeden z parametrów. Niektóre typowe kontrolki posiadają specyficzne funkcje tworzące je, jednakże funkcje te nie są czymś nowym a jedynie zastępują wywołanie CreateWindowEx w celu ułatwienia tworzenia kontrolek. Poniżej przedstawiam istniejące, specyficzne funkcje tworzenia typowych kontrolek:
Aby utworzyć typowe kontrolki, musisz znać ich nazwy klas. Zebrałem je w poniższej tabelce:
Nazwa klasy | Typowa kontrolka | |
---|---|---|
ToolbarWindow32 | Pasek narzędziowy | Toolbar |
tooltips_class32 | Podpowiedź | Tooltip |
msctls_statusbar32 | Pasek stanu | Status bar |
SysTreeView32 | Widok drzewa | Tree view |
SysListView32 | Widok listy | List view |
SysAnimate32 | Animacja | Animation |
SysHeader32 | Nagłówek | Header |
msctls_hotkey32 | Klawisz skrótu | Hot-key |
msctls_progress32 | Pasek postępu | Progress bar |
RICHEDIT | Edytor tekstu formatowanego | Rich edit |
msctls_updown32 | Przycisk góra/dół | Up-down |
SysTabControl32 | Zakładka | Tab |
Kontrolki arkuszy własności, stron własności i list obrazowych posiadają swoje własne specyficzne funkcje tworzące. Kontrolka listy przewijanej jest przyspieszoną wersją pola listy, zatem nie posiada własnej klasy. Powyższe nazwy klas zostały zweryfikowane przez analizę skryptu zasobów wygenerowanego przez edytor zasobów Visual C++. Różnią się one od nazw klas wymienionych przez pliki pomocy dla API Win32 firmy Borland oraz podręcznik Programming Windows 95 Charlesa Petzolda. Podana lista jest tę właściwą.
Typowe kontrolki mogą stosować ogólne style okien, takie jak WS_CHILD itp. Posiadają one również swoje własne, specyficzne style w rodzaju TVS_XXXXX dla kontrolki widoku drzewa, LVS_xxxx dla kontrolki widoku listy, itd. Najlepszym przyjacielem w tym względzie będzie dla ciebie plik pomocy dla biblioteki API Win32.
Teraz gdy już wiemy, jak tworzyć typowe kontrolki, możemy przejść do metod komunikowania się pomiędzy nimi a ich oknem nadrzędnym. W przeciwieństwie do kontrolek okna dialogowego typowe kontrolki nie komunikują się z procesem nadrzędnym poprzez wiadomości WM_COMMAND. Zamiast nich wysyłają wiadomości WM_NOTIFY do swojego okna nadrzędnego, gdy dzieje się z nimi coś interesującego. Okno nadrzędne może kontrolować swoje elementy potomne wysyłając do nich wiadomości. Dla tych nowych kontrolek zdefiniowano wiele nowych typów wiadomości. Więcej szczegółów na ten temat znajdziesz w pomocy dla biblioteki API Win32.
W poniższym przykładzie przetestujmy kontrolkę paska postępu (progress bar) oraz kontrolkę paska stanu (status bar).
Plik COMCTL.ASM.
.386 .MODEL FLAT, STDCALL OPTION CASEMAP:NONE INCLUDE \masm32\include\windows.inc INCLUDE \masm32\include\user32.inc INCLUDE \masm32\include\kernel32.inc INCLUDE \masm32\include\comctl32.inc INCLUDELIB \masm32\lib\comctl32.lib INCLUDELIB \masm32\lib\user32.lib INCLUDELIB \masm32\lib\kernel32.lib WinMain PROTO :DWORD, :DWORD, :DWORD, :DWORD .CONST IDC_PROGRESS EQU 1 IDC_STATUS EQU 2 IDC_TIMER EQU 3 .DATA ClassName DB "CommonControlWinClass", 0 AppName DB "Demonstracja typowych kontrolek", 0 ProgressClass DB "msctls_progress32", 0 Message DB "Skończone!", 0 TimerID DD 0 .DATA? hInstance HINSTANCE ? hwndProgress DD ? hwndStatus DD ? CurrentStep DD ? .CODE start: INVOKE GetModuleHandle, NULL mov hInstance, eax INVOKE WinMain, hInstance, NULL, NULL, SW_SHOWDEFAULT INVOKE ExitProcess, eax INVOKE InitCommonControls 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_APPWORKSPACE mov wc.lpszMenuName, NULL 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_OVERLAPPED + WS_CAPTION + \ WS_SYSMENU + WS_MINIMIZEBOX + \ WS_MAXIMIZEBOX + WS_VISIBLE,\ CW_USEDEFAULT, CW_USEDEFAULT,\ 400, 200, NULL, NULL, hInst, NULL mov hwnd, eax .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_CREATE INVOKE CreateWindowEx, NULL,\ ADDR ProgressClass, NULL, \ WS_CHILD + WS_VISIBLE,\ 50, 70, 300, 20,\ hWnd, IDC_PROGRESS, hInstance, NULL mov hwndProgress, eax mov eax, 1000 mov CurrentStep, eax shl eax, 16 INVOKE SendMessage, hwndProgress, PBM_SETRANGE, 0, eax INVOKE SendMessage, hwndProgress, PBM_SETSTEP, 10, 0 INVOKE CreateStatusWindow, WS_CHILD + WS_VISIBLE,\ NULL, hWnd, IDC_STATUS mov hwndStatus, eax INVOKE SetTimer, hWnd, IDC_TIMER, 100, NULL mov TimerID, eax .ELSEIF uMsg==WM_DESTROY INVOKE PostQuitMessage, NULL .IF TimerID!=0 INVOKE KillTimer, hWnd, TimerID .ENDIF .ELSEIF uMsg==WM_TIMER INVOKE SendMessage, hwndProgress, PBM_STEPIT, 0, 0 sub CurrentStep, 10 .IF CurrentStep==0 INVOKE KillTimer, hWnd, TimerID mov TimerID, 0 INVOKE SendMessage, hwndStatus,\ SB_SETTEXT, 0, ADDR Message INVOKE MessageBox, hWnd,\ ADDR Message, ADDR AppName,\ MB_OK + MB_ICONINFORMATION INVOKE SendMessage, hwndStatus, SB_SETTEXT, 0, 0 INVOKE SendMessage, hwndProgress, PBM_SETPOS, 0, 0 .ENDIF .ELSE INVOKE DefWindowProc, hWnd, uMsg, wParam, lParam ret .ENDIF xor eax, eax ret WndProc ENDP END start
INVOKE WinMain, hInstance, NULL, NULL, SW_SHOWDEFAULT INVOKE ExitProcess, eax INVOKE InitCommonControls
Rozmyślnie umieściłem wywołanie funkcji InitCommonControls za ExitProcess, aby zademonstrować, iż jest ona tutaj jedynie dla wstawienia odwołania do biblioteki comctl32.dll w sekcji importu. Jak widać typowe kontrolki działają nawet, gdy InitCommonControls nie zostanie wykonana.
.IF uMsg==WM_CREATE INVOKE CreateWindowEx, NULL,\ ADDR ProgressClass, NULL, \ WS_CHILD + WS_VISIBLE,\ 50, 70, 300, 20,\ hWnd, IDC_PROGRESS, hInstance, NULL mov hwndProgress, eax
Właśnie tutaj tworzymy typowe kontrolki. Zauważ, iż wywołanie CreateWindowEx zawiera hWnd jako uchwyt okna nadrzędnego. Określa również numer identyfikacyjny ID dla kontrolki. Jednakże z uwagi na otrzymanie uchwytu okna kontrolki, numer ten nie będzie używany. Wszystkie kontrolki okna potomnego muszą mieć styl WS_CHILD.
mov eax, 1000 mov CurrentStep, eax shl eax, 16 INVOKE SendMessage, hwndProgress, PBM_SETRANGE, 0, eax INVOKE SendMessage, hwndProgress, PBM_SETSTEP, 10, 0
Po utworzeniu paska postępu możemy ustawić jego zakres. Standardowo zakres ten wynosi od 0 do 100. Jeśli cię to nie zadowala, możesz ustalić swój własny przy pomocy wiadomości PBM_SETRANGE. lParam tej wiadomości zawiera zakres, górna granica znajduje się w starszym słowie, a dolna w młodszym. Przy pomocy wiadomości PBM_SETSTEP możemy określić wielkość kroku. Nasz przykład ustala krok na 10, co oznacza, iż przy każdym wysłaniu wiadomości PBM_STEPIT do paska postępu jego wskaźnik przesunie się o 10. Możesz również określać własne położenie za pomocą wiadomości PBM_SETPOS, które dają ci ściślejszą kontrolę nad paskiem postępu.
INVOKE CreateStatusWindow, WS_CHILD + WS_VISIBLE,\ NULL, hWnd, IDC_STATUS mov hwndStatus, eax INVOKE SetTimer, hWnd, IDC_TIMER, 100, NULL mov TimerID, eax
W następnej kolejności tworzymy pasek stanu wywołując CreateStatusWindow. Wywołanie to można z łatwością zrozumieć, zatem nie skomentuje go. Po utworzeniu paska stanu tworzymy licznik czasu. W naszym przykładzie będziemy uaktualniali pasek postępu w regularnych odstępach czasu co 100 milisekund, zatem musimy utworzyć kontrolkę licznika czasu (timer control). Poniżej przedstawiam prototyp funkcji SetTimer.
SetTimer PROTO hWnd: DWORD,\ TimerID: DWORD,\ TimeInterval: DWORD,\ lpTimerProc: DWORD
Jeśli wywołanie się powiedzie, to dostaniemy wartość TimerID. Jeśli nie, to zostanie zwrócona wartość 0. Dlatego identyfikator licznika czasu nie może mieć wartości zero.
.ELSEIF uMsg==WM_TIMER INVOKE SendMessage, hwndProgress, PBM_STEPIT, 0, 0 sub CurrentStep, 10 .IF CurrentStep==0 INVOKE KillTimer, hWnd, TimerID mov TimerID, 0 INVOKE SendMessage, hwndStatus,SB_SETTEXT, 0, ADDR Message INVOKE MessageBox, hWnd,ADDR Message, ADDR AppName,MB_OK + MB_ICONINFORMATION INVOKE SendMessage, hwndStatus, SB_SETTEXT, 0, 0 INVOKE SendMessage, hwndProgress, PBM_SETPOS, 0, 0 .ENDIF
Gdy upłynie zadany okres czasu, licznik wysyła wiadomość WM_TIMER. W tym miejscu umieszczasz kod, który w takim przypadku ma zostać wykonany. W tym przykładzie uaktualniamy pasek postępu, a następnie sprawdzamy, czy osiągnięta została jego górna granica. Jeśli tak, usuwamy licznik czasu, a następnie wyświetlamy tekst na pasku stanu za pomocą wiadomości SB_SETTEXT. Zostaje wyświetlone okno wiadomości, a gdy użytkownik kliknie przycisk OK, usuwamy tekst z paska stanu i czyścimy pasek przesuwu.
Ta sama aplikacja w Pascalu:
{******************************** ** I Liceum Ogólnokształcące ** ** w Tarnowie ** ** mgr Jerzy Wałaszek ** ********************************} program ComCtl; uses Windows; const IDC_PROGRESS = 1; IDC_STATUS = 2; IDC_TIMER = 3; ClassName = 'CommonControlWinClass'; AppName = 'Demonstracja typowych kontrolek'; ProgressClass = 'msctls_progress32'; Message = 'Skończone!'; var TimerID : longint; hInstance : HINST; hwndProgress : longword; hwndStatus : longword; CurrentStep : longword; function WndProc(hWnd:HWND;uMsg:UINT;wParam:WPARAM;lParam:LPARAM) : longint; begin Result := 0; case uMsg of WM_CREATE: begin hwndProgress := CreateWindowEx(0,ProgressClass,0,WS_CHILD + WS_VISIBLE, 50,70,300,20,hWnd,IDC_PROGRESS,hInstance,0); CurrentStep := 1000; SendMessage(hwndProgress,PBM_SETRANGE,0,1000 shl 16); SendMessage(hwndProgress,PBM_SETSTEP,10,0); hwndStatus := CreateStatusWindow(WS_CHILD+WS_VISIBLE,0,hWnd,IDC_STATUS); TimerID := SetTimer(hWnd,IDC_TIMER,100,nil); end; WM_DESTROY: begin PostQuitMessage(0); if TimerID <> 0 then KillTimer(hWnd,TimerID); end; WM_TIMER: begin SendMessage(hwndProgress,PBM_STEPIT,0,0); CurrentStep := CurrentStep - 10; if CurrentStep = 0 then begin KillTimer(hWnd,TimerID); TimerID := 0; SendMessage(hwndStatus,SB_SETTEXT,0,longint(@Message[1])); MessageBox(hWnd,Message,AppName,MB_OK + MB_ICONINFORMATION); SendMessage(hwndStatus,SB_SETTEXT,0,0); SendMessage(hwndProgress,PBM_SETPOS,0,0); end; end; else Result := DefWindowProc(hWnd,uMsg,wParam,lParam); end; end; function WinMain(hInst,hPrevInst:HINST;CmdLine:LPSTR;CmdShow:DWORD) : longint; var wc : WNDCLASSEX; msg : MSG; hwnd : HANDLE; begin with wc do begin cbSize := sizeof(WNDCLASSEX); style := CS_HREDRAW or CS_VREDRAW; lpfnWndProc := @WndProc; cbClsExtra := 0; cbWndExtra := 0; hInstance := hInst; hbrBackground := COLOR_APPWORKSPACE; lpszMenuName := 0; lpszClassName := ClassName; hIcon := LoadIcon(0,IDI_APPLICATION); hIconSm := hIcon; hCursor := LoadCursor(0,IDC_ARROW); end; RegisterClassEx(wc); hwnd := CreateWindowEx(WS_EX_CLIENTEDGE,ClassName,AppName, WS_OVERLAPPED + WS_CAPTION + WS_SYSMENU + WS_MINIMIZEBOX + WS_MAXIMIZEBOX + WS_VISIBLE,CW_USEDEFAULT,CW_USEDEFAULT, 400,200,0,0,hInst,0); while GetMessage(msg,0,0,0) do begin TranslateMessage(msg); DispatchMessage(msg); end; Result := msg.wParam; end; begin InitCommonControls; hInstance := GetModuleHandle(0); ExitProcess(WinMain(hInstance,0,0,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