Tworzenie własnego okna potomnego

Autor podstrony: Krzysztof Zajączkowski

Stronę tą wyświetlono już: 2189 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ć:

wnd.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); wnd.hCursor = LoadCursor(NULL, IDC_CROSS); wnd.hIcon = LoadIcon(NULL, IDC_ICON); wnd.hInstance = hInst; wnd.lpfnWndProc = WndDrawingProc; wnd.lpszClassName = drawingWndClass; wnd.lpszMenuName = NULL; if(!RegisterClass(&wnd)){ MessageBox(NULL, L"Nie zarejestrowano klasy okna rysowania", L"Błąd", MB_OK); return 0; }

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:

// #################### Tworzenie własnego okna potomnego ############################ 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:

// ################################### Procedura okna dokumentu ##################################### LRESULT CALLBACK WndDrawingProc(HWND hWndDraw, UINT msg, WPARAM wParam, LPARAM lParam){ static POINT mousepos; // współrzędne kursora w oknie switch(msg){ case WM_PAINT: { PAINTSTRUCT ps; HDC hdc = BeginPaint(hWndDraw, &ps); EndPaint(hWndDraw, &ps); } break; case WM_MOUSEMOVE: { mousepos.x = LOWORD(lParam); // pobieranie współrzędnej x mousepos.y = HIWORD(lParam); // pobieranie współrzędnej y wchar_t buffor[100]; // bufor do konwersji na tekst wsprintf(buffor, L"X = %i",mousepos.x); // konwersja na tekst współrzędnej x SendMessage(statusbar, SB_SETTEXT, 0, (LPARAM)buffor); // ustawienie tekstu pierwszej pozycji paska kontrolki statusbar wsprintf(buffor, L"Y = %i",mousepos.y); // konwersja na tekst współrzędnej y SendMessage(statusbar, SB_SETTEXT, 1, (LPARAM)buffor); // ustawienie tekstu drugiej pozycji paska kontrolki statusbar } break; case WM_COMMAND: { if((HWND)lParam == toolbar){ switch(LOWORD(wParam)){ case ID_LINE: // komunikat przychodzący od przycisku toolbar-a o identyfikatorze ID_LINE { SendMessage(statusbar, SB_SETTEXT, 2, (LPARAM)L"Rysowanie linii"); // ustawienie tekstu w ostatnim polu statusbar-u } break; case ID_CIRCLE: // to samo co poprzednio, tylko dla ID_CIRCLE { SendMessage(statusbar, SB_SETTEXT, 2, (LPARAM)L"Rysowanie okręgu"); // ustawienie tekstu w ostatnim polu statusbar-u } break; case ID_RECTANGLE: // to samo co poprzednio, tylko dla ID_RECTANGLE { SendMessage(statusbar, SB_SETTEXT, 2, (LPARAM)L"Rysowanie prostokąta"); // ustawienie tekstu w ostatnim polu statusbar-u } break; case ID_SELECT: // to samo co poprzednio, tylko dla ID_SELECT { SendMessage(statusbar, SB_SETTEXT, 2, (LPARAM)L"Rysowanie edycja"); // ustawienie tekstu w ostatnim polu statusbar-u } break; } } } break; } return DefWindowProc(hWndDraw, msg, wParam, lParam); }

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:

case WM_COMMAND: { SendMessage(drawingWnd, msg, wParam, lParam); // przekazuję obsługę komunikatów WM_COMMAND do okna rysowania } 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.

static RECT parent_rect; switch(msg){ case WM_SIZE: { SetRect(&parent_rect, 0, 0, LOWORD(lParam), HIWORD(lParam)); // pobieram rozmiar okna SendMessage(toolbar, TB_AUTOSIZE, NULL, NULL); // okunikat wysyłany w celu automatycznego ustawienia wymiarów okna toolbar SetWindowPos(statusbar, 0, 0, 0, 0, 0, SWP_NOZORDER); // automatyczne dostosowanie szerokości okna kontrolki statusbar-u do okna rodzica RECT sb_r; RECT tb_r; GetClientRect(toolbar, &tb_r); // pobieram wymiary okna kontrolki toolbar GetClientRect(statusbar, &sb_r); // pobieram wymiary okna kontrolki statusbar 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 } break;

Kod programu

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

#include <windows.h> #include <commctrl.h> #include "resource.h" // ############## ID TOOLBAR-a #################### #define TB_ID 100 // ############## ID PRZYCISKÓW TOOLBARD-a ############## #define ID_LINE 100 #define ID_CIRCLE 101 #define ID_RECTANGLE 102 #define ID_SELECT 103 // ###################### UCHWYT TOOLBAR-a ########################## HWND toolbar; // ###################### UCHWYT STATUSBAR-a ######################## HWND statusbar; // ###################### ID STATUSBAR-a ############################ #define SB_ID 101 // ###################### ID OKNA RYSOWANIA ######################### #define ID_DRAWING 102 // ####################### UCHWYT OKNA RYSOWANIA #################### HWND drawingWnd; // ################## PROCEDURA OKNA GŁÓWNEGO ####################### LRESULT CALLBACK WndParentProc(HWND hParent, UINT msg, WPARAM wParam, LPARAM lParam){ static RECT parent_rect; switch(msg){ case WM_SIZE: { SetRect(&parent_rect, 0, 0, LOWORD(lParam), HIWORD(lParam)); // pobieram rozmiar okna SendMessage(toolbar, TB_AUTOSIZE, NULL, NULL); // okunikat wysyłany w celu automatycznego ustawienia wymiarów okna toolbar SetWindowPos(statusbar, 0, 0, 0, 0, 0, SWP_NOZORDER); // automatyczne dostosowanie szerokości okna kontrolki statusbar-u do okna rodzica RECT sb_r; RECT tb_r; GetClientRect(toolbar, &tb_r); // pobieram wymiary okna kontrolki toolbar GetClientRect(statusbar, &sb_r); // pobieram wymiary okna kontrolki statusbar 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 } break; case WM_COMMAND: { SendMessage(drawingWnd, msg, wParam, lParam); // przekazuję obsługę komunikatów WM_COMMAND do okna rysowania } break; case WM_DESTROY: { PostQuitMessage(0); } break; } return DefWindowProc(hParent,msg, wParam, lParam); } // ################################### Procedura okna dokumentu ##################################### LRESULT CALLBACK WndDrawingProc(HWND hWndDraw, UINT msg, WPARAM wParam, LPARAM lParam){ static POINT mousepos; // współrzędne kursora w oknie switch(msg){ case WM_PAINT: { PAINTSTRUCT ps; HDC hdc = BeginPaint(hWndDraw, &ps); EndPaint(hWndDraw, &ps); } break; case WM_MOUSEMOVE: { mousepos.x = LOWORD(lParam); // pobieranie współrzędnej x mousepos.y = HIWORD(lParam); // pobieranie współrzędnej y wchar_t buffor[100]; // bufor do konwersji na tekst wsprintf(buffor, L"X = %i",mousepos.x); // konwersja na tekst współrzędnej x SendMessage(statusbar, SB_SETTEXT, 0, (LPARAM)buffor); // ustawienie tekstu pierwszej pozycji paska kontrolki statusbar wsprintf(buffor, L"Y = %i",mousepos.y); // konwersja na tekst współrzędnej y SendMessage(statusbar, SB_SETTEXT, 1, (LPARAM)buffor); // ustawienie tekstu drugiej pozycji paska kontrolki statusbar } break; case WM_COMMAND: { if((HWND)lParam == toolbar){ switch(LOWORD(wParam)){ case ID_LINE: // komunikat przychodzący od przycisku toolbar-a o identyfikatorze ID_LINE { SendMessage(statusbar, SB_SETTEXT, 2, (LPARAM)L"Rysowanie linii"); // ustawienie tekstu w ostatnim polu statusbar-u } break; case ID_CIRCLE: // to samo co poprzednio, tylko dla ID_CIRCLE { SendMessage(statusbar, SB_SETTEXT, 2, (LPARAM)L"Rysowanie okręgu"); // ustawienie tekstu w ostatnim polu statusbar-u } break; case ID_RECTANGLE: // to samo co poprzednio, tylko dla ID_RECTANGLE { SendMessage(statusbar, SB_SETTEXT, 2, (LPARAM)L"Rysowanie prostokąta"); // ustawienie tekstu w ostatnim polu statusbar-u } break; case ID_SELECT: // to samo co poprzednio, tylko dla ID_SELECT { SendMessage(statusbar, SB_SETTEXT, 2, (LPARAM)L"Rysowanie edycja"); // ustawienie tekstu w ostatnim polu statusbar-u } break; } } } break; } return DefWindowProc(hWndDraw, msg, wParam, lParam); } int WINAPI WinMain(HINSTANCE hInst, HINSTANCE, LPSTR, int){ // ####################### Operacje związane z rejestracją okna głównego ######################## wchar_t parentWndClass[] = L"Okno główne programu Rysowanie"; wchar_t drawingWndClass[] = L"Okno rysowania"; WNDCLASS wnd; wnd.cbClsExtra = NULL; wnd.cbWndExtra = NULL; wnd.hbrBackground = NULL; wnd.hCursor = LoadCursor(NULL, IDC_ARROW); wnd.hIcon = LoadIcon(NULL, IDC_ICON); wnd.hInstance = hInst; wnd.lpfnWndProc = WndParentProc; wnd.lpszClassName = parentWndClass; wnd.lpszMenuName = NULL; wnd.style = CS_VREDRAW|CS_HREDRAW; if(!RegisterClass(&wnd)){ MessageBox(NULL, L"Nie zarejestrowano klasy okna", L"Błąd", MB_OK); return 0; } wnd.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); wnd.hCursor = LoadCursor(NULL, IDC_CROSS); wnd.hIcon = LoadIcon(NULL, IDC_ICON); wnd.hInstance = hInst; wnd.lpfnWndProc = WndDrawingProc; wnd.lpszClassName = drawingWndClass; wnd.lpszMenuName = NULL; if(!RegisterClass(&wnd)){ MessageBox(NULL, L"Nie zarejestrowano klasy okna rysowania", L"Błąd", MB_OK); return 0; } HWND parent_window = CreateWindow(parentWndClass, L"Rysowanie", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInst, NULL); // #################### Tworzenie własnego okna potomnego ############################ drawingWnd = CreateWindow(drawingWndClass, L"Rysowanie", WS_CHILD|WS_VISIBLE, 0, 0, 100, 100, parent_window, (HMENU)ID_DRAWING, hInst, NULL); // ################# Inicjalizowanie tablicy struktur niezbędnej do utworzenia toolbar-a ######################### TBBUTTON bt[4]; // tablica struktur przycisków toolbar-a int i = 0; // indeks bt[i].dwData = 0; // brak dodatkowych danych bt[i].fsState = TBSTATE_ENABLED; // stan przycisku (w tym przypadku odblokowany) bt[i].fsStyle = TBSTYLE_CHECKGROUP; // styl przycisku (w tym przypadku grupowanie z innymi przyciskami) bt[i].iBitmap = i; // indeks wycinka bitmay bt[i].idCommand = ID_LINE; // identyfikator przycisku dla rysowania linii bt[i].iString = i; // indeks tekstu wyświetlanego pod przyciskiem i++; bt[i].dwData = 0; bt[i].fsState = TBSTATE_ENABLED; bt[i].fsStyle = TBSTYLE_CHECKGROUP; bt[i].iBitmap = i; bt[i].idCommand = ID_CIRCLE; // idnetyfikator przycisku dla rysowania okręgu bt[i].iString = i; i++; bt[i].dwData = 0; bt[i].fsState = TBSTATE_ENABLED; bt[i].fsStyle = TBSTYLE_CHECKGROUP; bt[i].iBitmap = i; bt[i].idCommand = ID_RECTANGLE; // identyfikator przycisku dla rysowania prostokąta bt[i].iString = i; i++; bt[i].dwData = 0; bt[i].fsState = TBSTATE_ENABLED; bt[i].fsStyle = TBSTYLE_CHECKGROUP; bt[i].iBitmap = i; bt[i].idCommand = ID_SELECT; // identyfikator przycisku dla zaznaczania obiektów bt[i].iString = i; toolbar = CreateToolbarEx( // tworzenie toolbar-a parent_window, // okno rodzica 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 TB_ID, // identyfikator toolbar-a 4, // liczba fragmentów wycinanych z bitmapy hInst, // uchwyt instancji programu IDB_TB_BITMAP, // identyfikator bitmapy zawartej w zasobach programu bt, // tablica struktur opisujących przyciski 4, // rozmiar tablicy struktur opisujących przyciski 48, // szerokość przycisku 48, // wysokość przycisku 42, // szerokość bitmapy 42, // wysokość bitmapu sizeof(TBBUTTON) // rozmiar struktury TBBUTTON ); SendMessage(toolbar, TB_ADDSTRING, 0, (LPARAM)L"OdcinekOkrągProstokątZaznaczanie"); // ustawianie tekstu pod obrazkami przycisków toolbar-u SendMessage(toolbar, TB_AUTOSIZE, NULL, NULL); // automatyczne dostosowanie rozmiaru okna do okna rodzica // ############################# TWORZENIE STATUS BARU ############################### 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 int parts[3]={120,260,-1}; // szerokość poszczególnych pól statusbar-u SendMessage(statusbar, SB_SETPARTS, 3, (LPARAM)parts); // ustawianie liczby pól oraz ich szerokości SendMessage(statusbar, SB_SETTEXT, 0, (LPARAM)L"X= "); // ustawienie tekstu w polu pierwszym SendMessage(statusbar, SB_SETTEXT, 1, (LPARAM)L"Y= "); // ustawienie tekstu w polu drugim // #################################################################################### RECT r; POINT pt; SendMessage(toolbar,TB_GETRECT,ID_LINE,(LPARAM)&r); // pobieranie prostokąta przycisku o identyfikatorze ID_LINE // tutaj pozyskuję współrzędne środka przycisku pt.x = (r.right + r.left) / 2; pt.y = (r.bottom + r.top) / 2; // A tutaj wysyłam komunikaty WM_LBUTONDOWN i WM_LBUTTONUP w celu zaznaczenia przycisku o identyfikatorze ID_LINE SendMessage(toolbar,WM_LBUTTONDOWN,MK_LBUTTON,MAKELONG(pt.x,pt.y)); SendMessage(toolbar,WM_LBUTTONUP,MK_LBUTTON,MAKELONG(pt.x,pt.y)); ShowWindow(parent_window, SW_NORMAL); UpdateWindow(parent_window); MSG msg; while(GetMessage(&msg, 0,0,0)){ TranslateMessage(&msg); DispatchMessage(&msg); } return 0; }

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.
Propozycje książek