Tworzenie własnego okna potomnego

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

Okna potomne

Czym są okna potomne już powinniście wiedzieć, ponieważ wszystkie kontrolki, jakie do tej pory były omawiane były tworzone jako okna potomne czy to okna głównego czy też okna dialogowego. Teraz do projektu Rysowanie dołączyć należy jedno dodatkowe specjalne okno potomne, które zostanie przez nas zarejestrowane, a następnie uchwyt do tego okna zostanie utworzony za pomocą dobrze znanej funkcji fu_cpp_winapi. Okno potomne musi przyjmować styl WS_CHILD oraz (gdy okno ma być widoczne) styl WS_VISIBLE, dodatkowo okno takie musie mieć uchwyt okna rodzica i w zasadzie to wszystko.

Do czego będzie służyć nasze okno potomne? W tym oknie będzie obsługiwane rysowanie wybranych elementów oraz ich edycja. Zacznę więc od omówienia samego kodu rejestrowania okna potomnego, który ma następującą postać:

Listing 1
  1. wnd.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
  2. wnd.hCursor = LoadCursor(NULL, IDC_CROSS);
  3. wnd.hIcon = LoadIcon(NULL, IDC_ICON);
  4. wnd.hInstance = hInst;
  5. wnd.lpfnWndProc = WndDrawingProc;
  6. wnd.lpszClassName = drawingWndClass;
  7. wnd.lpszMenuName = NULL;
  8. if(!RegisterClass(&wnd)){
  9. MessageBox(NULL, L"Nie zarejestrowano klasy okna rysowania", L"Błąd", MB_OK);
  10. return 0;
  11. }

Jak widać niektóre pola klasy zostały pominięte a to dlatego, że już wcześniej, przy rejestrowaniu klasy okna głównego te pola zostały poprawnie wypełnione. Utworzenie okna wygląda następująco:

Listing 2
  1. // #################### Tworzenie własnego okna potomnego ############################
  2. drawingWnd = CreateWindow(drawingWndClass, L"Rysowanie", WS_CHILD|WS_VISIBLE, 0, 0, 100, 100, parent_window, (HMENU)ID_DRAWING, hInst, NULL);

Procedura okna potomnego i zmiany w kodzie

Procedura okna potomnego powinna wyglądać następująco:

Listing 3
  1. // ################################### Procedura okna dokumentu #####################################
  2. LRESULT CALLBACK WndDrawingProc(HWND hWndDraw, UINT msg, WPARAM wParam, LPARAM lParam){
  3. static POINT mousepos; // współrzędne kursora w oknie
  4. switch(msg){
  5. case WM_PAINT:
  6. {
  7. PAINTSTRUCT ps;
  8. HDC hdc = BeginPaint(hWndDraw, &ps);
  9. EndPaint(hWndDraw, &ps);
  10. }
  11. break;
  12. case WM_MOUSEMOVE:
  13. {
  14. mousepos.x = LOWORD(lParam); // pobieranie współrzędnej x
  15. mousepos.y = HIWORD(lParam); // pobieranie współrzędnej y
  16. wchar_t buffor[100]; // bufor do konwersji na tekst
  17. wsprintf(buffor, L"X = %i",mousepos.x); // konwersja na tekst współrzędnej x
  18. SendMessage(statusbar, SB_SETTEXT, 0, (LPARAM)buffor); // ustawienie tekstu pierwszej pozycji paska kontrolki statusbar
  19. wsprintf(buffor, L"Y = %i",mousepos.y); // konwersja na tekst współrzędnej y
  20. SendMessage(statusbar, SB_SETTEXT, 1, (LPARAM)buffor); // ustawienie tekstu drugiej pozycji paska kontrolki statusbar
  21. }
  22. break;
  23. case WM_COMMAND:
  24. {
  25. if((HWND)lParam == toolbar){
  26. switch(LOWORD(wParam)){
  27. case ID_LINE: // komunikat przychodzący od przycisku toolbar-a o identyfikatorze ID_LINE
  28. {
  29. SendMessage(statusbar, SB_SETTEXT, 2, (LPARAM)L"Rysowanie linii"); // ustawienie tekstu w ostatnim polu statusbar-u
  30. }
  31. break;
  32. case ID_CIRCLE: // to samo co poprzednio, tylko dla ID_CIRCLE
  33. {
  34. SendMessage(statusbar, SB_SETTEXT, 2, (LPARAM)L"Rysowanie okręgu"); // ustawienie tekstu w ostatnim polu statusbar-u
  35. }
  36. break;
  37. case ID_RECTANGLE: // to samo co poprzednio, tylko dla ID_RECTANGLE
  38. {
  39. SendMessage(statusbar, SB_SETTEXT, 2, (LPARAM)L"Rysowanie prostokąta"); // ustawienie tekstu w ostatnim polu statusbar-u
  40. }
  41. break;
  42. case ID_SELECT: // to samo co poprzednio, tylko dla ID_SELECT
  43. {
  44. SendMessage(statusbar, SB_SETTEXT, 2, (LPARAM)L"Rysowanie edycja"); // ustawienie tekstu w ostatnim polu statusbar-u
  45. }
  46. break;
  47. }
  48. }
  49. }
  50. break;
  51. }
  52. return DefWindowProc(hWndDraw, msg, wParam, lParam);
  53. }

W komunikacie WM_MOUSEMOVE pobieram współrzędne kursora myszki w układzie związanym ściśle z oknem programu oraz ustawiam pola kontrolki statusbar, które mają wyświetlać współrzędne kursora w oknie rysowania.

Ciekawe jest też to, że cały kod z okna głównego dotyczący obsługi komunikatów przychodzących od okna głównego został przeniesiony właśnie do naszego okna potomnego! Natomiast teraz w oknie głównym w miejscu obsługi komunikatu WM_COMMAND wstawiłem następujący kod:

Listing 4
  1. case WM_COMMAND:
  2. {
  3. SendMessage(drawingWnd, msg, wParam, lParam); // przekazuję obsługę komunikatów WM_COMMAND do okna rysowania
  4. }
  5. break;

Również kod związany z obsługą komunikatu WM_SIZE uległ zmianie, bo przecież wymiary okna rysowania muszą się dynamicznie dostosowywać do wymiarów okna głównego oraz okien potomnych.

Listing 5
  1. static RECT parent_rect;
  2. switch(msg){
  3. case WM_SIZE:
  4. {
  5. SetRect(&parent_rect, 0, 0, LOWORD(lParam), HIWORD(lParam)); // pobieram rozmiar okna
  6. SendMessage(toolbar, TB_AUTOSIZE, NULL, NULL); // okunikat wysyłany w celu automatycznego ustawienia wymiarów okna toolbar
  7. SetWindowPos(statusbar, 0, 0, 0, 0, 0, SWP_NOZORDER); // automatyczne dostosowanie szerokości okna kontrolki statusbar-u do okna rodzica
  8. RECT sb_r;
  9. RECT tb_r;
  10. GetClientRect(toolbar, &tb_r); // pobieram wymiary okna kontrolki toolbar
  11. GetClientRect(statusbar, &sb_r); // pobieram wymiary okna kontrolki statusbar
  12. SetWindowPos(drawingWnd, NULL, 0, tb_r.bottom, parent_rect.right, parent_rect.bottom - sb_r.bottom - tb_r.bottom,SWP_NOZORDER); // ustawiam wymiary okna potomnego związango z obsługa rysowania
  13. }
  14. break;

Kod programu

Ostatecznie więc kod programu wygląda następująco:

Listing 6
  1. #include <windows.h>
  2. #include <commctrl.h>
  3. #include "resource.h"
  4. // ############## ID TOOLBAR-a ####################
  5. #define TB_ID 100
  6. // ############## ID PRZYCISKÓW TOOLBARD-a ##############
  7. #define ID_LINE 100
  8. #define ID_CIRCLE 101
  9. #define ID_RECTANGLE 102
  10. #define ID_SELECT 103
  11. // ###################### UCHWYT TOOLBAR-a ##########################
  12. HWND toolbar;
  13. // ###################### UCHWYT STATUSBAR-a ########################
  14. HWND statusbar;
  15. // ###################### ID STATUSBAR-a ############################
  16. #define SB_ID 101
  17. // ###################### ID OKNA RYSOWANIA #########################
  18. #define ID_DRAWING 102
  19. // ####################### UCHWYT OKNA RYSOWANIA ####################
  20. HWND drawingWnd;
  21. // ################## PROCEDURA OKNA GŁÓWNEGO #######################
  22. LRESULT CALLBACK WndParentProc(HWND hParent, UINT msg, WPARAM wParam, LPARAM lParam){
  23. static RECT parent_rect;
  24. switch(msg){
  25. case WM_SIZE:
  26. {
  27. SetRect(&parent_rect, 0, 0, LOWORD(lParam), HIWORD(lParam)); // pobieram rozmiar okna
  28. SendMessage(toolbar, TB_AUTOSIZE, NULL, NULL); // okunikat wysyłany w celu automatycznego ustawienia wymiarów okna toolbar
  29. SetWindowPos(statusbar, 0, 0, 0, 0, 0, SWP_NOZORDER); // automatyczne dostosowanie szerokości okna kontrolki statusbar-u do okna rodzica
  30. RECT sb_r;
  31. RECT tb_r;
  32. GetClientRect(toolbar, &tb_r); // pobieram wymiary okna kontrolki toolbar
  33. GetClientRect(statusbar, &sb_r); // pobieram wymiary okna kontrolki statusbar
  34. SetWindowPos(drawingWnd, NULL, 0, tb_r.bottom, parent_rect.right, parent_rect.bottom - sb_r.bottom - tb_r.bottom,SWP_NOZORDER); // ustawiam wymiary okna potomnego związango z obsługa rysowania
  35. }
  36. break;
  37. case WM_COMMAND:
  38. {
  39. SendMessage(drawingWnd, msg, wParam, lParam); // przekazuję obsługę komunikatów WM_COMMAND do okna rysowania
  40. }
  41. break;
  42. case WM_DESTROY:
  43. {
  44. PostQuitMessage(0);
  45. }
  46. break;
  47. }
  48. return DefWindowProc(hParent,msg, wParam, lParam);
  49. }
  50. // ################################### Procedura okna dokumentu #####################################
  51. LRESULT CALLBACK WndDrawingProc(HWND hWndDraw, UINT msg, WPARAM wParam, LPARAM lParam){
  52. static POINT mousepos; // współrzędne kursora w oknie
  53. switch(msg){
  54. case WM_PAINT:
  55. {
  56. PAINTSTRUCT ps;
  57. HDC hdc = BeginPaint(hWndDraw, &ps);
  58. EndPaint(hWndDraw, &ps);
  59. }
  60. break;
  61. case WM_MOUSEMOVE:
  62. {
  63. mousepos.x = LOWORD(lParam); // pobieranie współrzędnej x
  64. mousepos.y = HIWORD(lParam); // pobieranie współrzędnej y
  65. wchar_t buffor[100]; // bufor do konwersji na tekst
  66. wsprintf(buffor, L"X = %i",mousepos.x); // konwersja na tekst współrzędnej x
  67. SendMessage(statusbar, SB_SETTEXT, 0, (LPARAM)buffor); // ustawienie tekstu pierwszej pozycji paska kontrolki statusbar
  68. wsprintf(buffor, L"Y = %i",mousepos.y); // konwersja na tekst współrzędnej y
  69. SendMessage(statusbar, SB_SETTEXT, 1, (LPARAM)buffor); // ustawienie tekstu drugiej pozycji paska kontrolki statusbar
  70. }
  71. break;
  72. case WM_COMMAND:
  73. {
  74. if((HWND)lParam == toolbar){
  75. switch(LOWORD(wParam)){
  76. case ID_LINE: // komunikat przychodzący od przycisku toolbar-a o identyfikatorze ID_LINE
  77. {
  78. SendMessage(statusbar, SB_SETTEXT, 2, (LPARAM)L"Rysowanie linii"); // ustawienie tekstu w ostatnim polu statusbar-u
  79. }
  80. break;
  81. case ID_CIRCLE: // to samo co poprzednio, tylko dla ID_CIRCLE
  82. {
  83. SendMessage(statusbar, SB_SETTEXT, 2, (LPARAM)L"Rysowanie okręgu"); // ustawienie tekstu w ostatnim polu statusbar-u
  84. }
  85. break;
  86. case ID_RECTANGLE: // to samo co poprzednio, tylko dla ID_RECTANGLE
  87. {
  88. SendMessage(statusbar, SB_SETTEXT, 2, (LPARAM)L"Rysowanie prostokąta"); // ustawienie tekstu w ostatnim polu statusbar-u
  89. }
  90. break;
  91. case ID_SELECT: // to samo co poprzednio, tylko dla ID_SELECT
  92. {
  93. SendMessage(statusbar, SB_SETTEXT, 2, (LPARAM)L"Rysowanie edycja"); // ustawienie tekstu w ostatnim polu statusbar-u
  94. }
  95. break;
  96. }
  97. }
  98. }
  99. break;
  100. }
  101. return DefWindowProc(hWndDraw, msg, wParam, lParam);
  102. }
  103. int WINAPI WinMain(HINSTANCE hInst, HINSTANCE, LPSTR, int){
  104. // ####################### Operacje związane z rejestracją okna głównego ########################
  105. wchar_t parentWndClass[] = L"Okno główne programu Rysowanie";
  106. wchar_t drawingWndClass[] = L"Okno rysowania";
  107. WNDCLASS wnd;
  108. wnd.cbClsExtra = NULL;
  109. wnd.cbWndExtra = NULL;
  110. wnd.hbrBackground = NULL;
  111. wnd.hCursor = LoadCursor(NULL, IDC_ARROW);
  112. wnd.hIcon = LoadIcon(NULL, IDC_ICON);
  113. wnd.hInstance = hInst;
  114. wnd.lpfnWndProc = WndParentProc;
  115. wnd.lpszClassName = parentWndClass;
  116. wnd.lpszMenuName = NULL;
  117. wnd.style = CS_VREDRAW|CS_HREDRAW;
  118. if(!RegisterClass(&wnd)){
  119. MessageBox(NULL, L"Nie zarejestrowano klasy okna", L"Błąd", MB_OK);
  120. return 0;
  121. }
  122. wnd.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
  123. wnd.hCursor = LoadCursor(NULL, IDC_CROSS);
  124. wnd.hIcon = LoadIcon(NULL, IDC_ICON);
  125. wnd.hInstance = hInst;
  126. wnd.lpfnWndProc = WndDrawingProc;
  127. wnd.lpszClassName = drawingWndClass;
  128. wnd.lpszMenuName = NULL;
  129. if(!RegisterClass(&wnd)){
  130. MessageBox(NULL, L"Nie zarejestrowano klasy okna rysowania", L"Błąd", MB_OK);
  131. return 0;
  132. }
  133. HWND parent_window = CreateWindow(parentWndClass, L"Rysowanie", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInst, NULL);
  134. // #################### Tworzenie własnego okna potomnego ############################
  135. drawingWnd = CreateWindow(drawingWndClass, L"Rysowanie", WS_CHILD|WS_VISIBLE, 0, 0, 100, 100, parent_window, (HMENU)ID_DRAWING, hInst, NULL);
  136. // ################# Inicjalizowanie tablicy struktur niezbędnej do utworzenia toolbar-a #########################
  137. TBBUTTON bt[4]; // tablica struktur przycisków toolbar-a
  138. int i = 0; // indeks
  139. bt[i].dwData = 0; // brak dodatkowych danych
  140. bt[i].fsState = TBSTATE_ENABLED; // stan przycisku (w tym przypadku odblokowany)
  141. bt[i].fsStyle = TBSTYLE_CHECKGROUP; // styl przycisku (w tym przypadku grupowanie z innymi przyciskami)
  142. bt[i].iBitmap = i; // indeks wycinka bitmay
  143. bt[i].idCommand = ID_LINE; // identyfikator przycisku dla rysowania linii
  144. bt[i].iString = i; // indeks tekstu wyświetlanego pod przyciskiem
  145. i++;
  146. bt[i].dwData = 0;
  147. bt[i].fsState = TBSTATE_ENABLED;
  148. bt[i].fsStyle = TBSTYLE_CHECKGROUP;
  149. bt[i].iBitmap = i;
  150. bt[i].idCommand = ID_CIRCLE; // idnetyfikator przycisku dla rysowania okręgu
  151. bt[i].iString = i;
  152. i++;
  153. bt[i].dwData = 0;
  154. bt[i].fsState = TBSTATE_ENABLED;
  155. bt[i].fsStyle = TBSTYLE_CHECKGROUP;
  156. bt[i].iBitmap = i;
  157. bt[i].idCommand = ID_RECTANGLE; // identyfikator przycisku dla rysowania prostokąta
  158. bt[i].iString = i;
  159. i++;
  160. bt[i].dwData = 0;
  161. bt[i].fsState = TBSTATE_ENABLED;
  162. bt[i].fsStyle = TBSTYLE_CHECKGROUP;
  163. bt[i].iBitmap = i;
  164. bt[i].idCommand = ID_SELECT; // identyfikator przycisku dla zaznaczania obiektów
  165. bt[i].iString = i;
  166. toolbar = CreateToolbarEx( // tworzenie toolbar-a
  167. parent_window, // okno rodzica
  168. WS_CHILD|WS_VISIBLE|TBSTYLE_FLAT|TBSTYLE_WRAPABLE, // style: okno potomne; widoczny; płaskie przyciski; przyciski zwijane, gdy nie mogą się zmieścić w oknie programu
  169. TB_ID, // identyfikator toolbar-a
  170. 4, // liczba fragmentów wycinanych z bitmapy
  171. hInst, // uchwyt instancji programu
  172. IDB_TB_BITMAP, // identyfikator bitmapy zawartej w zasobach programu
  173. bt, // tablica struktur opisujących przyciski
  174. 4, // rozmiar tablicy struktur opisujących przyciski
  175. 48, // szerokość przycisku
  176. 48, // wysokość przycisku
  177. 42, // szerokość bitmapy
  178. 42, // wysokość bitmapu
  179. sizeof(TBBUTTON) // rozmiar struktury TBBUTTON
  180. );
  181. SendMessage(toolbar, TB_ADDSTRING, 0, (LPARAM)L"OdcinekOkrągProstokątZaznaczanie"); // ustawianie tekstu pod obrazkami przycisków toolbar-u
  182. SendMessage(toolbar, TB_AUTOSIZE, NULL, NULL); // automatyczne dostosowanie rozmiaru okna do okna rodzica
  183. // ############################# TWORZENIE STATUS BARU ###############################
  184. statusbar = CreateWindowEx(0, L"msctls_statusbar32",L"",WS_CHILD|WS_VISIBLE, 0, 0, 100, 100, parent_window, (HMENU)SB_ID ,hInst, 0); // pozyskiwanie uchwytu okna
  185. int parts[3]={120,260,-1}; // szerokość poszczególnych pól statusbar-u
  186. SendMessage(statusbar, SB_SETPARTS, 3, (LPARAM)parts); // ustawianie liczby pól oraz ich szerokości
  187. SendMessage(statusbar, SB_SETTEXT, 0, (LPARAM)L"X= "); // ustawienie tekstu w polu pierwszym
  188. SendMessage(statusbar, SB_SETTEXT, 1, (LPARAM)L"Y= "); // ustawienie tekstu w polu drugim
  189. // ####################################################################################
  190. RECT r;
  191. POINT pt;
  192. SendMessage(toolbar,TB_GETRECT,ID_LINE,(LPARAM)&r); // pobieranie prostokąta przycisku o identyfikatorze ID_LINE
  193. // tutaj pozyskuję współrzędne środka przycisku
  194. pt.x = (r.right + r.left) / 2;
  195. pt.y = (r.bottom + r.top) / 2;
  196. // A tutaj wysyłam komunikaty WM_LBUTONDOWN i WM_LBUTTONUP w celu zaznaczenia przycisku o identyfikatorze ID_LINE
  197. SendMessage(toolbar,WM_LBUTTONDOWN,MK_LBUTTON,MAKELONG(pt.x,pt.y));
  198. SendMessage(toolbar,WM_LBUTTONUP,MK_LBUTTON,MAKELONG(pt.x,pt.y));
  199. ShowWindow(parent_window, SW_NORMAL);
  200. UpdateWindow(parent_window);
  201. MSG msg;
  202. while(GetMessage(&msg, 0,0,0)){
  203. TranslateMessage(&msg);
  204. DispatchMessage(&msg);
  205. }
  206. return 0;
  207. }

W powyższym kodzie nie ma ani linijki, która byłaby odpowiedzialna za rysowanie naszych obiektów. Do tej pory utworzony został jedynie interfejs programu (i to w okrojonej formie). Na następnej podstronie już napiszemy trochę kodu, który będzie umożliwiał rysowania nowych obiektów w oknie rysowania.

Widok okna programu Rysowanie
Rys. 1
Nowy widok okna programu Rysowanie wzbogacony o okno potomne.

Komentarze