![]() |
Wyjście Spis treści Poprzedni Następny
Autor:
©Iczelion |
©2008 mgr
Jerzy Wałaszek |
W tym rozdziale nauczymy się dołączać do naszego okna listę poleceń menu..
Załaduj pliki {przykładu 1} i {przykładu 2}
Lista poleceń menu jest jednym z najważniejszych komponentów w naszym oknie. Lista ta przedstawia usługi oferowane użytkownikowi przez program. Użytkownik nie musi studiować instrukcji dołączonej do programu, aby móc go używać, może on przeczytać uważnie menu, aby wyrobić sobie pogląd co do możliwości określonego programu i natychmiast zacząć z nim zabawę. Ponieważ lista poleceń menu jest narzędziem umożliwiającym użytkownikowi szybki start z programem, powinieneś stosować się do standardów. Ujmując zwięźle pierwsze dwa elementy menu powinny być elementami Plik i Edycja, a ostatni powinien być elementem Pomoc. Własne elementy możesz umieszczać pomiędzy elementami Edycja i Pomoc. Jeśli element menu wywołuje okienko dialogowe, to do nazwę powinieneś zakończyć trzema kropkami (...).
Lista poleceń menu jest typem zasobu. Istnieje kilka typów zasobów, takich jak okno dialogowe, tablica łańcuchów tekstowych, ikona, mapa bitowa, lista menu itp. Zasoby są opisane w oddzielnym pliku zwanym plikiem zasobów, który posiada rozszerzenie .RC. Zasoby łączysz z kodem źródłowym podczas etapu konsolidacji. Końcowy produkt jest plikiem wykonywalnym posiadającym zarówno instrukcje jak i zasoby.
Skrypty zasobów możesz tworzyć przy pomocy dowolnego edytora tekstu. Składają się one ze zdań opisujących wygląd oraz inne atrybuty zasobów wykorzystywanych w określonym programie. Jednakże tworzenie takich skryptów w procesorze tekstu jest czynnością uciążliwą. Lepszą alternatywą będzie zapewne użycie edytora zasobów, który w sposób wizualny pozwala ci z łatwością projektować zasoby. Edytory zasobów zwykle są dołączane do pakietów kompilatorów takich jak Visual C++, Borland C++, itp. (od tłumacza - polecam pakiet RadASM, który zawiera dobry, zintegrowany edytor zasobów).
Zasób menu opisuje się w sposób następujący:
MojeMenu MENU { [tutaj lista menu] }
Programiści C/C++
mogą zauważyć, iż bardzo to przypomina deklarację struktury.
MojeMenu jest nazwą menu, za którą występuje słowo
kluczowe MENU oraz lista menu w nawiasach klamrowych.
Zamiast nich możesz stosować słowa BEGIN
i END, jeśli tak sobie życzysz. Taka składnia
będzie bardziej strawna dla programujących w języku Pascal.
Lista menu może składać się albo z poleceń MENUITEM albo z POPUP.
Polecenie MENUITEM definiuje opcję menu, która przy wyborze nie rozwija nowej listy poleceń. Składnia tego polecenia jest następująca:
MENUITEM "&tekst", ID [,opcje]
Rozpoczyna się od słowa kluczowego MENUITEM, za którym następuje tekst używany do wyświetlenia danej opcji menu. Zwróć uwagę na znak "&". Powoduje on podkreślenie na ekranie literki, która następuje bezpośrednio za nim. Za tekstem umieszcza się numer identyfikacyjny ID elementu menu. ID jest numerem, który będzie użyty do identyfikacji elementu menu w wiadomości wysyłanej do procedury okna, gdy ten element menu zostanie wybrany przez użytkownika. Dlatego elementy menu nie mogą mieć powtarzających się numerów ID.
Opcje można pominąć. Dostępne są następujące opcje:
Możesz użyć jedną z podanych wyżej opcji lub połączyć kilka operatorem OR. Uważaj, razem nie można jedynie łączyć INACTIVE oraz GRAYED.
Polecenie POPUP posiada następującą składnię:
POPUP "&tekst" [,opcje] { [lista menu] }
Polecenie POPUP definiuje element menu, który przy wybraniu rozwija listę elementów menu w małym oknie. Lista ta może się składać z poleceń POPUP lub MENUITEM. Istnieje specjalne polecenie MENUITEM SEPARATOR, które rysuje poziomą linię w oknie z poleceniami menu.
Następnym krokiem po zakończeniu pracy nad skryptem zasobów jest odwołanie się do niego w twoim programie źródłowym. Możesz tego dokonać w dwóch różnych miejscach swojego programu.
.DATA MenuName DB "FirstMenu",0 ... .CODE ... mov wc.lpszMenuName, OFFSET MenuName ...
.DATA MenuName DB "FirstMenu",0 hMenu HMENU ? ... .CODE ... INVOKE LoadMenu, hInst, OFFSET MenuName mov hMenu, eax INVOKE CreateWindowEx, NULL, OFFSET ClsName,\ OFFSET Caption, WS_OVERLAPPEDWINDOW,\ CW_USEDEFAULT, CW_USEDEFAULT,\ CW_USEDEFAULT, CW_USEDEFAULT,\ NULL,\ hMenu,\ hInst,\ NULL ...
Zatem możesz zapytać, czym różnią się te dwa sposoby?
Jeśli odwołujesz się do menu w strukturze WNDCLASSEX, to dane menu stanie się "standardowym" menu dla klasy okna. Każde okno tej klasy będzie wyposażone w to samo menu.
Jeśli chcesz, aby każde okno utworzone na podstawie tej samej klasy posiadało różne zestawy poleceń menu, to musisz wybrać drugi sposób. W tym przypadku każde okno utworzone przez funkcję CreateWindowEx z ustawionym parametrem uchwytu menu będzie posiadało menu przykrywające standardowe, określone w strukturze WNDCLASSEX.
Następnie zbadamy, w jaki sposób menu powiadamia procedurę okna o wyborze opcji dokonanym przez użytkownika.
Gdy użytkownik wybierze element menu, procedura okna otrzyma wiadomość WM_COMMAND. Młodsze słowo wParam zawiera numer ID wybranego przez użytkownika elementu menu.
Teraz mamy wystarczająco dużo wiadomości do utworzenia i używania menu. Do dzieła.
Pierwszy przykład pokazuje sposób utworzenia i użycia menu przez określenie nazwy menu w klasie okna.
.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 Test_string DB "Wybrałeś element Test z menu",0 Hello_string DB "Witaj, mój przyjacielu",0 Goodbye_string DB "Do zobaczenia, cześć",0 .DATA? hInstance HINSTANCE ? CommandLine LPSTR ? .CONST IDM_TEST EQU 1 IDM_HELLO EQU 2 IDM_GOODBYE 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_WINDOW+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, NULL, ADDR ClassName, ADDR AppName,\ WS_OVERLAPPEDWINDOW, CW_USEDEFAULT,\ CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 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 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_COMMAND mov eax, wParam .IF ax==IDM_TEST INVOKE MessageBox, NULL, ADDR Test_string, OFFSET AppName, MB_OK .ELSEIF ax==IDM_HELLO INVOKE MessageBox, NULL, ADDR Hello_string, OFFSET AppName, MB_OK .ELSEIF ax==IDM_GOODBYE INVOKE MessageBox, NULL, ADDR Goodbye_string, OFFSET AppName, MB_OK .ELSE INVOKE DestroyWindow, hWnd .ENDIF .ELSE INVOKE DefWindowProc, hWnd, uMsg, wParam, lParam ret .ENDIF xor eax, eax ret WndProc ENDP END start
Zawartość pliku zasobów MENU.RC jest następująca:
#define IDM_TEST 1 #define IDM_HELLO 2 #define IDM_GOODBYE 3 #define IDM_EXIT 4 FirstMenu MENU { POPUP "&Lista" { MENUITEM "&Przywitaj się", IDM_HELLO MENUITEM "P&ożegnaj się", IDM_GOODBYE MENUITEM SEPARATOR MENUITEM "&Koniec", IDM_EXIT } MENUITEM "&Test", IDM_TEST }
Przeanalizujmy najpierw plik zasobu:
#define IDM_TEST 1 /* równe IDM_TEST EQU 1 w pliku ASM */ #define IDM_HELLO 2 #define IDM_GOODBYE 3 #define IDM_EXIT 4
Powyższe wiersze definiują numery ID elementów menu używane przez nasz
skrypt.
Możesz przypisywać dowolne wartości numerom ID pod warunkiem, iż w danym menu różnią się one od siebie.
FirstMenu MENU
Zdefiniuj swoje menu przy pomocy słowa kluczowego MENU.
POPUP "&Lista" { MENUITEM "&Przywitaj się", IDM_HELLO MENUITEM "P&ożegnaj się", IDM_GOODBYE MENUITEM SEPARATOR MENUITEM "&Koniec", IDM_EXIT }
Zadeklaruj menu rozwijane z czterema elementami, jeden z nich to
separator.
MENUITEM "&Test", IDM_TEST
Zadeklaruj nowy element w menu głównym.
W następnej kolejności zbadamy kod źródłowy programu.
MenuName DB "FirstMenu",0 Test_string DB "Wybrałeś element Test z menu",0 Hello_string DB "Witaj, mój przyjacielu",0 Goodbye_string DB "Do zobaczenia, cześć",0
MenuName jest nazwą menu w pliku zasobów. Zwróć uwagę, iż w takim pliku
można zdefiniować kilka list poleceń menu, zatem musisz określić, które
z nich chcesz użyć. Pozostałe trzy wiersze definiują łańcuchy tekstowe,
które będą wyświetlane przez okienka wiadomości wywoływane, gdy
użytkownik dokona wyboru odpowiedniej opcji z menu.
IDM_TEST EQU 1 ;numery ID poleceń menu IDM_HELLO EQU 2 IDM_GOODBYE EQU 3 IDM_EXIT EQU 4
Zdefiniuj numery ID elementów menu do użytku w procedurze okna. Wartości
te MUSZĄ być identyczne z wartościami w pliku zasobów.
.ELSEIF uMsg==WM_COMMAND mov eax, wParam .IF ax==IDM_TEST INVOKE MessageBox, NULL, ADDR Test_string, OFFSET AppName, MB_OK .ELSEIF ax==IDM_HELLO INVOKE MessageBox, NULL, ADDR Hello_string, OFFSET AppName, MB_OK .ELSEIF ax==IDM_GOODBYE INVOKE MessageBox, NULL, ADDR Goodbye_string, OFFSET AppName, MB_OK .ELSE INVOKE DestroyWindow, hWnd .ENDIF
W procedurze okna przetwarzamy wiadomości WM_COMMAND.
Gdy użytkownik wybiera element menu, do procedury okna zostaje wysłana
wiadomość WM_COMMAND i w młodszym słowie
parametru wParam system Windows
umieszcza numer ID wybranego elementu menu. Zatem
po pobraniu do rejestru eax wartości
wParam porównujemy jego młodsze słowo ax z
poprzednio zdefiniowanymi numerami ID elementów
menu i podejmujemy odpowiednie działania. W pierwszych trzech
przypadkach gdy użytkownik wybierze element menu Test,
Przywitaj się lub
Pożegnaj się, po prostu wyświetlamy krótki tekst w
okienku wiadomości.
Jeśli użytkownik wybierze element menu Koniec, wywołujemy DestroyWindow z uchwytem naszego okna jako parametr, co spowoduje zamknięcie naszego okna.
Jak widać określenie nazwy menu w klasie okna jest nieskomplikowane. Jednakże możesz również zastosować alternatywną metodę załadowania menu do swojego okna. Nie pokażę tutaj całego kodu źródłowego tego przykładu, który w większości jest taki sam. Plik zasobów jest identyczny z opisanym powyżej w obu metodach. Zmiany w kodzie pokazuję poniżej:
.DATA? hInstance HINSTANCE ? CommandLine LPSTR ? hMenu HMENU ?
Zdefiniuj zmienną typu HMENU do przechowywania uchwytu
naszej listy poleceń menu.
INVOKE LoadMenu, hInst, OFFSET MenuName mov hMenu, eax INVOKE CreateWindowEx, NULL, ADDR ClassName, ADDR AppName,\ WS_OVERLAPPEDWINDOW, CW_USEDEFAULT,\ CW_USEDEFAULT, CW_USEDEFAULT,\ CW_USEDEFAULT, NULL, hMenu,\ hInst, NULL
Przed wywołaniem CreateWindowEx wywołujemy
funkcję LoadMenu z uchwytem do egzemplarza
naszego programu oraz wskazaniem nazwy menu. Funkcja
LoadMenu zwraca uchwyt do naszego menu w pliku zasobów, który z
kolei przekazujemy do CreateWindowEx.
Ta sama aplikacja w Pascalu:
Aplikacja wykorzystuje identyczny plik zasobów, co program asemblerowy. W przypadku DevPascala plik ten należy umieścić w katalogu projektu pod nazwą rsrc.rc.
{******************************** ** I Liceum Ogólnokształcące ** ** w Tarnowie ** ** mgr Jerzy Wałaszek ** ********************************} program Menu; uses Windows; const ClassName = 'SimpleWinClass'; AppName = 'Nasze Pierwsze Okno'; MenuName = 'FirstMenu'; Test_string = 'Wybrałeś element Test z menu'; Hello_string = 'Witaj, mój przyjacielu'; Goodbye_string = 'Do zobaczenia, cześć'; IDM_TEST = 1; IDM_HELLO = 2; IDM_GOODBYE = 3; IDM_EXIT = 4; function WndProc(h:HWND;uMsg:UINT;wP:WPARAM;lP:LPARAM) : longint; begin case uMsg of WM_DESTROY : begin PostQuitMessage(0); WndProc := 0; end; WM_COMMAND : begin case (wP and $ffff) of IDM_TEST : MessageBox(0,Test_string,AppName,MB_OK); IDM_HELLO : MessageBox(0,Hello_string,AppName,MB_OK); IDM_GOODBYE : MessageBox(0,Goodbye_string,AppName,MB_OK); else DestroyWindow(h); end; 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_WINDOW + 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, CW_USEDEFAULT,CW_USEDEFAULT,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 ExitProcess(WinMain(GetModuleHandle(0),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