Dodawanie menu, akceleratora, ikony i kursora do programu

Autor podstrony: Krzysztof Zajączkowski

Stronę tą wyświetlono już: 3620 razy

Nowy projekt myNotepad

Na tej stronie zacznę tworzyć pierwszy nieco ciekawszy projekt o nazwie myNotepad. Będzie to pusty projekt, który zasadniczo wykorzysta w znacznej mierze już wcześniej utworzony kod. Projekt nie zostanie do końca ukończony tutaj, ponieważ chcę się zasadniczo skupić na omówieniu następujących zagadnień:

Dodawanie pliku zasobów *rc i resource.h

W celu dodania do projektu pliku zasobów należy wcisnąć kombinację klawiszy Ctrl+Alt+L lub z menu View wybrać pozycję Solution explorer by po chwili ujrzeć okno z poniższego rysunku.

VS EE okno Solution Explorer
Rys. 1
Okno Solution Explorer

W oknie tym czym prędzej z menu kontekstowego należy wybrać pozycję Add->Resource aby program automatycznie dodał do projektu dwa pliki: *.rc oraz resource.h. W tym samym czasie powinno pojawić się okno Add Resource, w którym jest kilka możliwych pozycji do wyboru. Najpierw dodajmy ikonkę a więc należy kliknąć pole Icon i wcisnąć przycisk New.

Okno Add Resource programu VS EE
Rys. 2
Okno Add Resource programu VS EE

Po kliknięciu New powinien pojawić się widok z poniższego rysunku. Jak widać, VS EE udostępnia pewne podstawowe narzędzia do rysowania, ja jednakże nie będę niczego zmieniał i pozostawię domyślną ikonkę. Warto też zwrócić uwagę na fakt, że dostępne są od razu dwie wersje ikonki: pierwsza o rozmiarze 16x16 i druga o rozmiarze 32x32.

Widok okna projektu dla dodanej ikony
Rys. 3
Wisual Studio widok edytora ikonki programu

W podobny sposób należy dodać kursor programu, z tymże z pozycji rozwijanej okna Add Resource należy wybrać Cursor->IDC_POINTER i kliknąć New by oczom ukazał się widok z poniższego rysunku.

Widok okna edytora kursora
Rys. 4
Widok okna edytora kursora

W końcu upragnione menu dodać w podobny sposób należy. I tutaj prosiłbym o dodanie do niego takich samych pozycji jak w poniższym rysunku. Robi się to dość intuicyjnie, więc myślę że bez zbędnego opisywania każdy da sobie radę. Dodanie separatora (linii poziomej w menu) odbywa się poprzez wybranie z menu kontekstowego Insert separator.

Tworzenie menu
Rys. 5
Dodawanie pozycji do menu

Po dodaniu pozycji, proszę kliknąć prawym przyciskiem myszy na menu i wybrać z menu kontekstowego pozycję Edit IDs. Zmienić należy kolejne pozycje na: ID_PLIK_OTWORZ; ID_PLIK_ZAPISZ i ID_PLIK_ZAMKNIJ.

Pora na akceleratory, ale zanim to najpierw kilka słów wyjaśnienia co to są w ogóle akceleratory. Otóż są to skróty klawiaturowe, które będą obsługiwane przez program i mogą być związane np. z wywołaniem jakiejś pozycji w menu. Dodawanie akceleratora do pliku *.rc odbywa się w podobny sposób co poprzednie elementy a więc za pomocą okna Add Resource, gdzie tym razem oczywiście należy kliknąć pozycję Accelerator. Po kliknięciu przycisku New pojawi się okno edycji akceleratora. Tutaj również poproszę Was o dodanie pozycji takich samych jak na poniższym rysunku.

Widok okna edycji akceleratorów
Rys. 6
Widok okna z dodanymi akceleratorami

Kod źródłowy programu

Znacząca część kodu została powielona z poprzedniej strony, toteż bez zbytnich ceregieli w poniższym kodzie w komentarzach opisałem pokrótce jedynie te elementy, które uległy zmianie. W funkcji hwndProc okna głównego nie ma już komunikatu WM_PAINT, ponieważ w tym przypadku jest on całkowicie zbyteczny.

#include <windows.h> #include "resource.h" // konieczne jest załączenie pliku resource.h w celu uzyskania dostępu do identyfikatorów elementów zawartych w pliku *.rc #define ID_EDIT 3 // identyfikator kontrolki edit do przechowywania tekstu LRESULT CALLBACK hwndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam){ static RECT wndSize; static HWND hedit; // kontrolka edit do przechowywania tekstu switch(msg){ case WM_CREATE: { hedit = CreateWindow(L"EDIT", // okno klasy EDIT L"Ala ma kota\r\nA kot ma Alę\r\nAla go kocha\r\nA kot ją wcale", // tekst wyświetlany w oknie WS_CHILD // flaga oznaczająca że jest to okno potomne |WS_VISIBLE // flaga infomrująca, że okno ma być widoczne po utworzeniu |WS_HSCROLL|WS_VSCROLL // te flagi włączają paski skrolowania w poziomie i pionie |ES_AUTOHSCROLL|ES_AUTOVSCROLL // automatyczne przesuwanie podczas pisania w pionie i poziomie |ES_MULTILINE, // dopuszcza wprowadzanie wielu linii kodu 0,0,100,100, // współrzędne okna x, y oraz szerokość i wysokość (później zostaną zmienione w komunikacie WM_SIZE) hWnd, // uchwyt do okna rodzica (HMENU)ID_EDIT, // tutaj pewna niekonsekwencja, ale funkcja jako HMENU przyjmuje identyfikator okna potomnego (HINSTANCE)GetWindowLong(hWnd,GWL_HINSTANCE), // za pomocą tej funkcji pobieram uchwyt instancji HINSTANCE (ta funkcja w zleżności od użytej flagi może zwracać inne ciekawe rzeczy) NULL); NONCLIENTMETRICS ncm; // struktora dzięki której pozyskam informacje o czcionce systemowej ncm.cbSize=sizeof(ncm); // najpierw rozmiar struktury muszę opisać SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0); // tutaj pobieram informację między innymi o czciance używanej przez system HFONT font=CreateFontIndirect(&ncm.lfStatusFont); // a tutaj tworzę uchwyt do czcionki używanej przez system SendMessage(hedit, WM_SETFONT, (WPARAM) font, 0); // ustawiam czcionkę w kontrolce hedit } break; case WM_DESTROY: { PostQuitMessage(0); } break; case WM_COMMAND: { if(lParam){ // tutaj kontrolki powinno się obsługiwać }else{ switch(HIWORD(wParam)){ // gdy wyższe słowo wyciągnięte z parametru wParam case 0: // jest równe zero to komunikat pochodzi od menu { switch(LOWORD(wParam)){ // niższe słowo parametru wParam zawiera identyfikator klikniętej pozycji menu case ID_PLIK_OTWORZ: { MessageBox(hWnd, L"Jesze otwierania nie obsłużono",L"Informacja",MB_OK); // wyświetlam okno dialogowe informujące, że to jeszcze wymaga obsłużenia } break; case ID_PLIK_ZAMKNIJ: { PostQuitMessage(0); // kończę program } break; case ID_PLIK_ZAPISZ: { MessageBox(hWnd, L"Jesze zapisywania nie obsłużono",L"Informacja",MB_OK); // wyświetlam okno dialogowe informujące, że to jeszcze wymaga obsłużenia } break; } } break; case 1: // jest równe 1 to komunikat pochodzi od akceleratora { switch(LOWORD(wParam)){ // niższe słowo parametru wParam zawiera identyfikator akceleratora, który został wywołany case ID_PLIK_OTWORZ: { SendMessage(hWnd, WM_COMMAND, ID_PLIK_OTWORZ,NULL); // wysyłam komunikat o kliknięcia pozycji w menu } break; case ID_PLIK_ZAPISZ: { SendMessage(hWnd, WM_COMMAND, ID_PLIK_ZAPISZ,NULL); // wysyłam komunikat o kliknięcia pozycji w menu } break; } } break; } } } break; case WM_SIZE: { SetRect(&wndSize,0,0,LOWORD(lParam),HIWORD(lParam)); SetWindowPos(hedit, // uchwyt okna, którego położenie będzie zmieniane NULL, // ten parametr jest związany z kolejnością wyświetlania okien i w tym przypadku powinien przyjmować NULL 0,0,wndSize.right, wndSize.bottom, // x, y, szerokość, wysokość SWP_NOZORDER // bez zmiany położenia (jedno okno nad drugim) ); } break; } return DefWindowProc(hWnd, msg, wParam, lParam); } int WINAPI WinMain(HINSTANCE hInst, HINSTANCE , LPSTR str, int ){ WNDCLASS wnd; wnd.cbClsExtra = NULL; wnd.cbWndExtra = NULL; wnd.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH); // to będzie tło okna wnd.hCursor = LoadCursor(hInst, MAKEINTRESOURCE(IDC_POINTER)); // ładowanie własnego kursora wnd.hIcon = LoadIcon(hInst, MAKEINTRESOURCE(IDI_ICON1)); // ładowanie własnej ikonki wnd.hInstance = hInst; wnd.lpfnWndProc = hwndProc; wnd.lpszClassName = L"myNotepad"; wnd.lpszMenuName = NULL; wnd.style = CS_VREDRAW|CS_HREDRAW; if(!RegisterClass(&wnd)){ // jeżeli nie udało się zarejestrować klassy okna MessageBox(NULL,L"Wzięło się i nie zarejestrowało", L"Błąd", MB_OK); // to płacz, że coś się stało return 0; // i zwracaj zero } HWND hWnd = CreateWindow(L"myNotepad", // nazwa klasy okna L"myNotepad", // tekst belki tytułowej okna WS_OVERLAPPEDWINDOW, // styl okna (ten oznacza z belką tytułową, przyciskami minimalizacji, maksymalizacji i zwijania oraz z ramką CW_USEDEFAULT, // pozycja x (CS_USEDEFAULT oznacza domyślne) CW_USEDEFAULT, // pozycja y (CS_USEDEFAULT oznacza domyślne) CW_USEDEFAULT, // szerokość (CS_USEDEFAULT oznacza domyślne) CW_USEDEFAULT, // wysokość (CS_USEDEFAULT oznacza domyślne) NULL, // okno rodzica (tutaj nie będzie takiego więc NULL) LoadMenu(hInst, MAKEINTRESOURCE(IDR_MENU1)), // wskaźnik do menu (też nie będzie więc NULL) hInst, // uchwyt instancji programu NULL // wskaźnik do dodatkowych danych ); ShowWindow(hWnd, SW_NORMAL); // wyświetlania okna i jego tryb UpdateWindow(hWnd); // wymuszenie pierwszego odświerzenia okna MSG msg; // struktura komunikatów HACCEL hAccel = LoadAccelerators(hInst, MAKEINTRESOURCE(IDR_ACCELERATOR1)); // Ładowanie akceleratora z pliku zasobów .rc while(GetMessage(&msg, 0,0,0)){ // dopóki nie otrzymano komunikatu == 0 if(!TranslateAccelerator(hWnd, hAccel, &msg)){ // sprawdzam, czy nie otrzymałem komunikatu od akceleratora, jak nie to TranslateMessage(&msg); // tłumacz komunikat DispatchMessage(&msg); // rozpakuj komunikat } } return 0; }

Reasumując, program do tej pory ma menu, akcleleratory, własną ikonkę i własny kursor. Na następnej stronie omówię jak otwierać i zapisywać dane do pliku z wykorzystaniem okna dialogowego Open i Save.

Spis nowych makr i funkcji użytych w programie

Poniżej zamieszczam listę makr oraz funkcji, które nie były jeszcze używane na wcześniejszych stronach tego działu:

W projekcie zostały wykorzystane następujące struktury, których wcześniej nie używano:

Propozycje książek