Autor podstrony: Krzysztof Zajączkowski

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

Wstęp

Kontrolka typu toolbar jest jedną z bardziej przydatnych kontrolek i służy ona do wyświetlania grup przycisków, które mogą być z sobą powiązane. Na tej stronie więc poświęcę nieco uwagi tworzeniu własnej kontrolki tego typu. W tym celu utworzyć należy nowy czysty projekt o nazwie Rysowanie, a to dlatego, że tworzyć będziemy program rysujący różne elementy w obszarze okna programu. Podstawą stworzenia takiego programu będzie kontrolka typu toolbar, która umożliwi przełączanie się pomiędzy trybami rysowania.

Toolbar - oznacza w tłumaczeniu na nasz język: pasek narzędziowy, tak więc ta kontrolka będzie wyświetlała różne narzędzia związane z realizacją zadań programu.

Dodawanie bitmapy do zasobów programu

Zanim przystąpię do pisania samego programu, konieczne jest najpierw dodanie do projektu pliku zasobów .rc wraz z bitmapą, której właściwości powinny być następujące:

  • szerokość - 192 px;
  • wysokość - 48 px;
  • głębia kolorów - 8 bitowa;

Bitmapa ta powinna wyglądać mniej więcej tak jak na poniższym rysunku, i zostanie ona użyta przy tworzeniu paska narzędziowego.

Bitmapa pliku zasobów dla toolbar-a
Rys. 1
Bitmapa pliku zasobów dla toolbar-a

Wszystkie właściwości bitmapy można zmienić w oknie Properties, które można wyświetlić za pomocą kombinacji klawiszy ctrl+enter lub z menu Viwe→Others Windows→Properties Window. W tym samym oknie należy zmienić wartość pola ID z domyślnej wartości na IDB_TB_BITMAP, ale najpierw konieczne jest zaznaczenie elementu dodanej bitmapy na liście zasobów znajdującej się w oknie Resource View - Rysowanie, które z kolei można wyświetlić wciskając kombinację klawiszy ctrl+shift+e lub wybierając z menu View→Resource View.

Struktura TBBUTTON

Struktura TBBUTTON służy do opisywania styli i właściwości poszczególnych przycisków okna kontrolki toolbar. Z tego względu warto się przyjrzeć co niektórym polom tej struktury:

  • iBitmap - indeks wycinka bitmapy, który zostanie przypisany do danego przycisku. Pierwszym elementem indeksowania jest zero;
  • idCommand - identyfikator przycisku, który zostanie wysłany w komunikacie WM_COMMAND;
  • fsState - określa stan przycisku, ten element może mieć jedną z następujących kombinacji:
    • TBSTATE_CHECKED - przycisk ma styl TBSTYLE_CHECK i wyświetla się jako już kliknięty;
    • TBSTATE_ELLIPSES - przycina wyświetlany tekst i wyświetla trzy kropek;
    • TBSTATE_ENABLED - odblokowany przycisk (przycisk bez tego stylu jest wyszarzony i nieaktywny;
    • TBSTATE_HIDDEN - przycisk jest niewidoczny i nie może otrzymywać komunikatów od użytkownika;
    • TBSTATE_INDETERMINATE - przycisk jest wyszarzony;
    • TBSTATE_PRESSED - przycisk jest wciśnięty;
    • TBSTATE_WRAP - oznacza, że następne przyciski będą wstawiane w nowej linii, ta flaga musi być użyta wraz z flagą TBSTATE_ENABLED;
  • fsStyle - określa styl przycisku, oto zbiór dostępnych flag:
    • BTNS_AUTOSIZE - toolbar nie będzie wykorzystywał standardowej szerokości dla przycisku, zamiast tego długość ta będzie obliczana na podstawie szerokości obrazka oraz tekstu przypisanego do przycisku;
    • BTNS_BUTTON tworzy standardowy przycisk. Odpowiednikiem tego stylu jest TBSTYLE_BUTTON;
    • BTNS_CHECK - tworzy przycisk podwójnego stanu: wciśniętego lub nie wciśniętego za każdym razem, gdy użytkownik kliknie kontrolkę. Przycisk ma inny kolor tła gdy jest w stanie wciśnięcia. Odpowiednikiem tego stylu jest TBSTYLE_CHECK;
    • BTNS_CHECKGROUP - tworzy przycisk, który pozostaje wciśnięty dopóki następny przycisk z tej samej grupy nie zostanie naciśnięty;
    • BTNS_DROPDOWN - tworzy rozwijany przycisk, który może wyświetlać listę, gdy przycisk jest kliknięty. Zamiast komunikatu WM_COMMAND używanego dla normalnych przycisków, przycisk rozwijany wysyła notyfikację TBN_DROPDOWN. Aplikacja może wtedy otrzymać listę opcji. Jeżeli toolbar ma styl TBNSTYLE_EX_DRAWDDARROWS, przycisk rozwijany będzie miał ikonkę wyświetlaną z lewej jego strony. Gdy strzałka zostanie kliknięta to wysyłany jest komunikat TBN_DROPDOWN, natomiast, gdy przycisk zostanie kliknięty, to wysyłany jest komunikat WM_COMMAND;
    • BTNS_GROUP - gdy jest użyty wraz z stylem BTNS_CHECK, tworzy przycisk, który pozostaje wciśnięty dopóki inny przycisk w grupie nie zostanie wciśnięty;
    • BTNS_NOPREFIX - tekst przycisku nie będzie wyświetlał prefiksów związanych z akceleratorami;
    • BTNS_SEP - wstawia separator pomiędzy różnymi grupami przycisków;
    • BTNS_SHOWTEXT - określa, że tekst przycisku powinien zostać wyświetlony. Każdy przycisk może mieć przypisany tekst, ale tylko te, które mają ustawiony ten styl będą go wyświetlały. Gdy styl ten nie jest ustawiony a przyciski mają przypisany tekst, wtedy tekst będzie się pojawiał dopiero po najechaniu kursorem myszki na dany przycisk;
    • BTNS_WHOLEDROPDOWN - przycisk będzie miał strzałkę rozwijania, lecz nie jako oddzielną część;
  • dwData - dodatkowe dane zdefiniowane w programie;
  • iString - indeks tekstu, który jest przypisany do przycisku;

Funkcja CreateToolbarEx

Oto nagłówek funkcji, która tworzy uchwyt okna kontrolki toolbar:

HWND CreateToolbarEx( HWND hwnd, // uchwyt rodzica DWORD ws, // styl okna UINT wID, // identyfikator kontrolki int nBitmaps, // id bitmapy zawartej w pliku zasobów .rc HINSTANCE hBMInst, // uchwyt instancji programu UINT_PTR wBMID, // liczba przycisków LPCTBBUTTON lpButtons, // wskaźnik do tablicy struktur opisujących przyciski int iNumButtons, // liczba elementów tablicy struktur opisujących przyciski int dxButton, // szerokość przycisku int dyButton, // wysokość przycisku int dxBitmap, // szerokość bitmapy int dyBitmap, // wysokość bitmapy UINT uStructSize // rozmiar struktury TBBUTTON );

Zasadniczo wszystkie parametry funkcji CreateToolbarEx zostały omówione w komentarzach nagłówka funkcji, jednak warto rozpisać się nieco na temat parametru ws, który określa style okna. Style te mogą przybierać następujące wartości:

  • TBSTYLE_ALTDRAG - umożliwia użytkownikowi zmianę położenia przycisków toolbar-a poprzez kliknięcie i przeciągnięcie przycisku z przytrzymanym klawiszem alt. Jeżeli ten styl nie został zastosowany, użytkownik musi wcisnąć i przytrzymać klawisz shift gdy przycisk jest przeciągany. Aby ten styl zadziałał i aby przeciąganie było możliwe trzeba użyć stylu CCS_ADJUSTABLE;
  • TBSTYLE_CUSTOMERASE - generuje notyfikację NM_CUSTOMDRAW gdy toolbar przetwarza komunikat WM_ERASEBKGND;
  • TBSTYLE_FLAT - przyciski toolbar-a są rysowane bez trójwymiarowej ramki;
  • TBSTYLE_LIST - przyciski toolbar-a są rysowane bez trójwymiarowej ramki, natomiast tekst dołączony do przycisków jest rysowany po prawej stronie bitmapy przycisku;
  • TBSTYLE_REGISTERDROP - generuje notyfikację TBN_GETOBJECT przy żądaniu upuszczenia obiektu gdy kursor przechodzi ponad oknem toolbar;
  • TBSTYLE_TOOLTIPS - tworzy kontrolki toolbar-u wyświetlając informację o przeznaczeniu danego przycisku;
  • TBSTYLE_TRANSPARENT - tworzy toolbar z przezroczystym tłem. Tło przycisków pozostaje niezmienione (nie jest przezroczyste);
  • TBSTYLE_WRAPABLE - tworzy toolbar, który może mieć wiele linii. Kontrolka ta utworzona z tym stylem zwija przyciski i wyświetla je w nowej linii, gdy te nie mieszczą się w oknie. Styl ten nie może być użyty z stylem CCS_VERT;

Istnieje jeszcze oddzielna grupa stylów, które mogą również być użyte w parametrze ws funkcji CreateToolbarEx:

  • CCS_ADJUSTABLE - odblokowuje ustawienie dla kontrolki toolbar możliwości przeciągania przycisków do nowej pozycji lub ich usuwanie z listy toolbar-a;
  • CCS_BOTTOM - ustawia kontrolkę wyrównując ją do dolnej krawędzi okna rodzica z wyrównaniem do lewej jego strony oraz ustawia szerokość na równi z tymże oknem. Kontrolka typu statusbar ma ten styl ustawiony jako domyślny;
  • CCS_LEFT - ustawia kontrolkę tak, że jest ona wyświetlana poziomo z wyrównaniem do lewej krawędzi okna rodzica;
  • CCS_NODIVIDER - wyłącza rysowanie krawędzi kontrolki, która jest rysowana na jej górnej krawędzi;
  • CCS_NOMOVEX - kontrolka może przemieszczać się i zmieniać rozmiar w poziomie, lecz nie w pionie w odpowiedzi na komunikat WM_SIZE. Jeżeli został użyty styl CCS_NORESIZE ten styl nie ma zastosowania;
  • CCS_NOMOVEY - kontrolka może przemieszczać się i zmieniać rozmiar w pionie, lecz nie w poziomie w odpowiedzi na komunikat WM_SIZE. Jeżeli został użyty styl CCS_NORESIZE ten styl nie ma zastosowania;
  • CCS_NOPARENTALIGN - zapobiega przed automatycznym przemieszczaniem kontrolki do górnej lub dolnej krawędzi okna rodzica. Zamiast tego kontrolka utrzymuje położenie dostosowując jedynie swój rozmiar do rozmiaru okna rodzica. Jeżeli styl CCS_TOP lub CCS_BOTTOM jest również użyty, wtedy szerokość jest ustawiana na domyślną wartość, ale pozycja i szerokość się nie zmieniają;
  • CCS_NORESIZE - zapobiega przed zmianą pozycji oraz szerokości i wysokości okna kontrolki;
  • CCS_RIGHT - wyrównuje poziomo do prawej krawędzi okna;
  • CCS_TOP - wyrównuje do górnej krawędzi okna, kontrolka typu toolbar ma ten styl ustawiony domyślnie;
  • CCS_VERT - tworzy kontrolkę wyświetlaną poziomo;

Kod programu

Przed rozpoczęciem wpisywania kodu należy kliknąć w menu Projekt→Rysowanie Properties lub wcisnąć kombinację klawiszy alt+F7 aby pojawiło się okno Rysowanie Property Page. W oknie tym w liście rozwijanej po lewej stronie należy kliknąć pozycję Configuration Properties→Linker→Input i w kontrolce po prawej stronie w polu Additional Dependencies wpisać nazwę pliku comctl32.lib. Skoro już wiadomo co i jak, teraz pozostało już napisanie jedynie kodu programu:

#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; // ################## PROCEDURA OKNA GŁÓWNEGO ####################### LRESULT CALLBACK WndParentProc(HWND hParent, UINT msg, WPARAM wParam, LPARAM lParam){ switch(msg){ case WM_SIZE: { SendMessage(toolbar, TB_AUTOSIZE, NULL, NULL); // okunikat wysyłany w celu automatycznego ustawienia wymiarów okna toolbar } 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 { MessageBox(hParent, L"Kliknięto przycisk rysowania linii",L"Info",MB_OK); } break; case ID_CIRCLE: // to samo co poprzednio, tylko dla ID_CIRCLE { MessageBox(hParent, L"Kliknięto przycisk rysowania okręgu",L"Info",MB_OK); } break; case ID_RECTANGLE: // to samo co poprzednio, tylko dla ID_RECTANGLE { MessageBox(hParent, L"Kliknięto przycisk rysowania prostokąta",L"Info",MB_OK); } break; case ID_SELECT: // to samo co poprzednio, tylko dla ID_SELECT { MessageBox(hParent, L"Kliknięto przycisk zaznaczania",L"Info",MB_OK); } break; } } } break; case WM_DESTROY: { PostQuitMessage(0); } break; } return DefWindowProc(hParent,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"; 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; } HWND parent_window = CreateWindow(parentWndClass, L"Rysowanie", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, 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 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; }

Na koniec mały podgląd programu zamieszczam poniżej.

Podgląd programu Rysowanie
Rys. 2
Podgląd programu Rysowanie
Layout wykonany przez autora strony, wszelkie prawa zastrzeżone. Jakiekolwiek użycie części lub całości grafik znajdujących się na tej stronie bez pisemnej zgody jej autora surowo zabronione.