Dodawanie menu, akceleratora, ikony i kursora do programu

Stronę tą wyświetlono już: 331 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 oraz resource.h;
  • dodanie własnej ikony aplikacji do pliku *.rc;
  • dodanie własnego kursora strzałki do pliku *.rc;
  • dodanie własnego menu do pliku *.rc;
  • dodanie akceleratora do pliku *.rc;
  • załączanie pliku resource.h do projektu oraz zastosowanie wszystkich elementów dodanych do zasobów w projekcie;
  • dodanie okna potomnego (kontrolki) EDIT i obsłużenie prawidłowego jej pozycjonowania w oknie (komunikat WM_SIZE).

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.

Listing 1
  1. #include <windows.h>
  2. #include "resource.h" // konieczne jest załączenie pliku resource.h w celu uzyskania dostępu do identyfikatorów elementów zawartych w pliku *.rc
  3. #define ID_EDIT 3 // identyfikator kontrolki edit do przechowywania tekstu
  4. LRESULT CALLBACK hwndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam){
  5. static RECT wndSize;
  6. static HWND hedit; // kontrolka edit do przechowywania tekstu
  7. switch(msg){
  8. case WM_CREATE:
  9. {
  10. hedit = CreateWindow(L"EDIT", // okno klasy EDIT
  11. L"Ala ma kota\r\nA kot ma Alę\r\nAla go kocha\r\nA kot ją wcale", // tekst wyświetlany w oknie
  12. WS_CHILD // flaga oznaczająca że jest to okno potomne
  13. |WS_VISIBLE // flaga infomrująca, że okno ma być widoczne po utworzeniu
  14. |WS_HSCROLL|WS_VSCROLL // te flagi włączają paski skrolowania w poziomie i pionie
  15. |ES_AUTOHSCROLL|ES_AUTOVSCROLL // automatyczne przesuwanie podczas pisania w pionie i poziomie
  16. |ES_MULTILINE, // dopuszcza wprowadzanie wielu linii kodu
  17. 0,0,100,100, // współrzędne okna x, y oraz szerokość i wysokość (później zostaną zmienione w komunikacie WM_SIZE)
  18. hWnd, // uchwyt do okna rodzica
  19. (HMENU)ID_EDIT, // tutaj pewna niekonsekwencja, ale funkcja jako HMENU przyjmuje identyfikator okna potomnego
  20. (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)
  21. NULL);
  22. NONCLIENTMETRICS ncm; // struktora dzięki której pozyskam informacje o czcionce systemowej
  23. ncm.cbSize=sizeof(ncm); // najpierw rozmiar struktury muszę opisać
  24. SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0); // tutaj pobieram informację między innymi o czciance używanej przez system
  25. HFONT font=CreateFontIndirect(&ncm.lfStatusFont); // a tutaj tworzę uchwyt do czcionki używanej przez system
  26. SendMessage(hedit, WM_SETFONT, (WPARAM) font, 0); // ustawiam czcionkę w kontrolce hedit
  27. }
  28. break;
  29. case WM_DESTROY:
  30. {
  31. PostQuitMessage(0);
  32. }
  33. break;
  34. case WM_COMMAND:
  35. {
  36. if(lParam){
  37. // tutaj kontrolki powinno się obsługiwać
  38. }else{
  39. switch(HIWORD(wParam)){ // gdy wyższe słowo wyciągnięte z parametru wParam
  40. case 0: // jest równe zero to komunikat pochodzi od menu
  41. {
  42. switch(LOWORD(wParam)){ // niższe słowo parametru wParam zawiera identyfikator klikniętej pozycji menu
  43. case ID_PLIK_OTWORZ:
  44. {
  45. 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
  46. }
  47. break;
  48. case ID_PLIK_ZAMKNIJ:
  49. {
  50. PostQuitMessage(0); // kończę program
  51. }
  52. break;
  53. case ID_PLIK_ZAPISZ:
  54. {
  55. 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
  56. }
  57. break;
  58. }
  59. }
  60. break;
  61. case 1: // jest równe 1 to komunikat pochodzi od akceleratora
  62. {
  63. switch(LOWORD(wParam)){ // niższe słowo parametru wParam zawiera identyfikator akceleratora, który został wywołany
  64. case ID_PLIK_OTWORZ:
  65. {
  66. SendMessage(hWnd, WM_COMMAND, ID_PLIK_OTWORZ,NULL); // wysyłam komunikat o kliknięcia pozycji w menu
  67. }
  68. break;
  69. case ID_PLIK_ZAPISZ:
  70. {
  71. SendMessage(hWnd, WM_COMMAND, ID_PLIK_ZAPISZ,NULL); // wysyłam komunikat o kliknięcia pozycji w menu
  72. }
  73. break;
  74. }
  75. }
  76. break;
  77. }
  78. }
  79. }
  80. break;
  81. case WM_SIZE:
  82. {
  83. SetRect(&wndSize,0,0,LOWORD(lParam),HIWORD(lParam));
  84. SetWindowPos(hedit, // uchwyt okna, którego położenie będzie zmieniane
  85. NULL, // ten parametr jest związany z kolejnością wyświetlania okien i w tym przypadku powinien przyjmować NULL
  86. 0,0,wndSize.right, wndSize.bottom, // x, y, szerokość, wysokość
  87. SWP_NOZORDER // bez zmiany położenia (jedno okno nad drugim)
  88. );
  89. }
  90. break;
  91. }
  92. return DefWindowProc(hWnd, msg, wParam, lParam);
  93. }
  94. int WINAPI WinMain(HINSTANCE hInst, HINSTANCE , LPSTR str, int ){
  95. WNDCLASS wnd;
  96. wnd.cbClsExtra = NULL;
  97. wnd.cbWndExtra = NULL;
  98. wnd.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH); // to będzie tło okna
  99. wnd.hCursor = LoadCursor(hInst, MAKEINTRESOURCE(IDC_POINTER)); // ładowanie własnego kursora
  100. wnd.hIcon = LoadIcon(hInst, MAKEINTRESOURCE(IDI_ICON1)); // ładowanie własnej ikonki
  101. wnd.hInstance = hInst;
  102. wnd.lpfnWndProc = hwndProc;
  103. wnd.lpszClassName = L"myNotepad";
  104. wnd.lpszMenuName = NULL;
  105. wnd.style = CS_VREDRAW|CS_HREDRAW;
  106. if(!RegisterClass(&wnd)){ // jeżeli nie udało się zarejestrować klassy okna
  107. MessageBox(NULL,L"Wzięło się i nie zarejestrowało", L"Błąd", MB_OK); // to płacz, że coś się stało
  108. return 0; // i zwracaj zero
  109. }
  110. HWND hWnd = CreateWindow(L"myNotepad", // nazwa klasy okna
  111. L"myNotepad", // tekst belki tytułowej okna
  112. WS_OVERLAPPEDWINDOW, // styl okna (ten oznacza z belką tytułową, przyciskami minimalizacji, maksymalizacji i zwijania oraz z ramką
  113. CW_USEDEFAULT, // pozycja x (CS_USEDEFAULT oznacza domyślne)
  114. CW_USEDEFAULT, // pozycja y (CS_USEDEFAULT oznacza domyślne)
  115. CW_USEDEFAULT, // szerokość (CS_USEDEFAULT oznacza domyślne)
  116. CW_USEDEFAULT, // wysokość (CS_USEDEFAULT oznacza domyślne)
  117. NULL, // okno rodzica (tutaj nie będzie takiego więc NULL)
  118. LoadMenu(hInst, MAKEINTRESOURCE(IDR_MENU1)), // wskaźnik do menu (też nie będzie więc NULL)
  119. hInst, // uchwyt instancji programu
  120. NULL // wskaźnik do dodatkowych danych
  121. );
  122. ShowWindow(hWnd, SW_NORMAL); // wyświetlania okna i jego tryb
  123. UpdateWindow(hWnd); // wymuszenie pierwszego odświerzenia okna
  124. MSG msg; // struktura komunikatów
  125. HACCEL hAccel = LoadAccelerators(hInst, MAKEINTRESOURCE(IDR_ACCELERATOR1)); // Ładowanie akceleratora z pliku zasobów .rc
  126. while(GetMessage(&msg, 0,0,0)){ // dopóki nie otrzymano komunikatu == 0
  127. if(!TranslateAccelerator(hWnd, hAccel, &msg)){ // sprawdzam, czy nie otrzymałem komunikatu od akceleratora, jak nie to
  128. TranslateMessage(&msg); // tłumacz komunikat
  129. DispatchMessage(&msg); // rozpakuj komunikat
  130. }
  131. }
  132. return 0;
  133. }

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:

  • MAKEINTRESOURCE - makro zamieniające identyfikator elementu zawartego w pliku *.rc do postaci kompatybilnej z funkcjami ładującymi te zasoby;
  • LoadAccelerators - funkcja umożliwiająca ładowanie akceleratora z pliku zasobów *.rc;
  • LoadCursor - funkcja umożliwiająca ładowanie kursora z zasobów pliku *.rc;
  • LoadIcon - funkcja umożliwiająca ładowanie ikony z zasobów pliku *.rc;
  • LoadMenu - funkcja użyta do załadowania menu z zasobów pliku *.rc;
  • TranslateAccelerator - funkcja przetwarzająca komunikaty pochodzące od akceleratora okna;
  • SystemParametersInfo - funkcja umożliwiająca uzyskiwanie informacji o ustawieniach systemu;
  • CreateFontIndirect - funkcja umożliwiająca utworzenie uchwytu czcionki HFONT;
  • SendMessage - funkcja umożliwiająca wysyłanie komunikatu do danego okna;
  • SetWindowPos - funkcja umożliwiająca zmianę pozycji i rozmiaru okna.

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

  • NONCLIENTMETRICS - struktura powiązana z funkcją SystemParametersInfo

Komentarze