Kontrolka typu toolbar

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

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

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:

Listing 2
  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. // ################## PROCEDURA OKNA GŁÓWNEGO #######################
  14. LRESULT CALLBACK WndParentProc(HWND hParent, UINT msg, WPARAM wParam, LPARAM lParam){
  15. switch(msg){
  16. case WM_SIZE:
  17. {
  18. SendMessage(toolbar, TB_AUTOSIZE, NULL, NULL); // okunikat wysyłany w celu automatycznego ustawienia wymiarów okna toolbar
  19. }
  20. break;
  21. case WM_COMMAND:
  22. {
  23. if((HWND)lParam == toolbar){
  24. switch(LOWORD(wParam)){
  25. case ID_LINE: // komunikat przychodzący od przycisku toolbar-a o identyfikatorze ID_LINE
  26. {
  27. MessageBox(hParent, L"Kliknięto przycisk rysowania linii",L"Info",MB_OK);
  28. }
  29. break;
  30. case ID_CIRCLE: // to samo co poprzednio, tylko dla ID_CIRCLE
  31. {
  32. MessageBox(hParent, L"Kliknięto przycisk rysowania okręgu",L"Info",MB_OK);
  33. }
  34. break;
  35. case ID_RECTANGLE: // to samo co poprzednio, tylko dla ID_RECTANGLE
  36. {
  37. MessageBox(hParent, L"Kliknięto przycisk rysowania prostokąta",L"Info",MB_OK);
  38. }
  39. break;
  40. case ID_SELECT: // to samo co poprzednio, tylko dla ID_SELECT
  41. {
  42. MessageBox(hParent, L"Kliknięto przycisk zaznaczania",L"Info",MB_OK);
  43. }
  44. break;
  45. }
  46. }
  47. }
  48. break;
  49. case WM_DESTROY:
  50. {
  51. PostQuitMessage(0);
  52. }
  53. break;
  54. }
  55. return DefWindowProc(hParent,msg, wParam, lParam);
  56. }
  57. int WINAPI WinMain(HINSTANCE hInst, HINSTANCE, LPSTR, int){
  58. // ####################### Operacje związane z rejestracją okna głównego ########################
  59. wchar_t parentWndClass[] = L"Okno główne programu Rysowanie";
  60. WNDCLASS wnd;
  61. wnd.cbClsExtra = NULL;
  62. wnd.cbWndExtra = NULL;
  63. wnd.hbrBackground = NULL;
  64. wnd.hCursor = LoadCursor(NULL, IDC_ARROW);
  65. wnd.hIcon = LoadIcon(NULL, IDC_ICON);
  66. wnd.hInstance = hInst;
  67. wnd.lpfnWndProc = WndParentProc;
  68. wnd.lpszClassName = parentWndClass;
  69. wnd.lpszMenuName = NULL;
  70. wnd.style = CS_VREDRAW|CS_HREDRAW;
  71. if(!RegisterClass(&wnd)){
  72. MessageBox(NULL, L"Nie zarejestrowano klasy okna", L"Błąd", MB_OK);
  73. return 0;
  74. }
  75. HWND parent_window = CreateWindow(parentWndClass, L"Rysowanie", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInst, NULL);
  76. // ################# Inicjalizowanie tablicy struktur niezbędnej do utworzenia toolbar-a #########################
  77. TBBUTTON bt[4]; // tablica struktur przycisków toolbar-a
  78. int i = 0; // indeks
  79. bt[i].dwData = 0; // brak dodatkowych danych
  80. bt[i].fsState = TBSTATE_ENABLED; // stan przycisku (w tym przypadku odblokowany)
  81. bt[i].fsStyle = TBSTYLE_CHECKGROUP; // styl przycisku (w tym przypadku grupowanie z innymi przyciskami)
  82. bt[i].iBitmap = i; // indeks wycinka bitmay
  83. bt[i].idCommand = ID_LINE; // identyfikator przycisku dla rysowania linii
  84. bt[i].iString = i; // indeks tekstu wyświetlanego pod przyciskiem
  85. i++;
  86. bt[i].dwData = 0;
  87. bt[i].fsState = TBSTATE_ENABLED;
  88. bt[i].fsStyle = TBSTYLE_CHECKGROUP;
  89. bt[i].iBitmap = i;
  90. bt[i].idCommand = ID_CIRCLE; // idnetyfikator przycisku dla rysowania okręgu
  91. bt[i].iString = i;
  92. i++;
  93. bt[i].dwData = 0;
  94. bt[i].fsState = TBSTATE_ENABLED;
  95. bt[i].fsStyle = TBSTYLE_CHECKGROUP;
  96. bt[i].iBitmap = i;
  97. bt[i].idCommand = ID_RECTANGLE; // identyfikator przycisku dla rysowania prostokąta
  98. bt[i].iString = i;
  99. i++;
  100. bt[i].dwData = 0;
  101. bt[i].fsState = TBSTATE_ENABLED;
  102. bt[i].fsStyle = TBSTYLE_CHECKGROUP;
  103. bt[i].iBitmap = i;
  104. bt[i].idCommand = ID_SELECT; // identyfikator przycisku dla zaznaczania obiektów
  105. bt[i].iString = i;
  106. toolbar = CreateToolbarEx( // tworzenie toolbar-a
  107. parent_window, // okno rodzica
  108. 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
  109. TB_ID, // identyfikator toolbar-a
  110. 4, // liczba fragmentów wycinanych z bitmapy
  111. hInst, // uchwyt instancji programu
  112. IDB_TB_BITMAP, // identyfikator bitmapy zawartej w zasobach programu
  113. bt, // tablica struktur opisujących przyciski
  114. 4, // rozmiar tablicy struktur opisujących przyciski
  115. 48, // szerokość przycisku
  116. 48, // wysokość przycisku
  117. 42, // szerokość bitmapy
  118. 42, // wysokość bitmapu
  119. sizeof(TBBUTTON) // rozmiar struktury TBBUTTON
  120. );
  121. SendMessage(toolbar, TB_ADDSTRING, 0, (LPARAM)L"OdcinekOkrągProstokątZaznaczanie"); // ustawianie tekstu pod obrazkami przycisków toolbar-u
  122. SendMessage(toolbar, TB_AUTOSIZE, NULL, NULL); // automatyczne dostosowanie rozmiaru okna do okna rodzica
  123. RECT r;
  124. POINT pt;
  125. SendMessage(toolbar,TB_GETRECT,ID_LINE,(LPARAM)&r); // pobieranie prostokąta przycisku o identyfikatorze ID_LINE
  126. // tutaj pozyskuję współrzędne środka przycisku
  127. pt.x = (r.right + r.left) / 2;
  128. pt.y = (r.bottom + r.top) / 2;
  129. // A tutaj wysyłam komunikaty WM_LBUTONDOWN i WM_LBUTTONUP w celu zaznaczenia przycisku o identyfikatorze ID_LINE
  130. SendMessage(toolbar,WM_LBUTTONDOWN,MK_LBUTTON,MAKELONG(pt.x,pt.y));
  131. SendMessage(toolbar,WM_LBUTTONUP,MK_LBUTTON,MAKELONG(pt.x,pt.y));
  132. ShowWindow(parent_window, SW_NORMAL);
  133. UpdateWindow(parent_window);
  134. MSG msg;
  135. while(GetMessage(&msg, 0,0,0)){
  136. TranslateMessage(&msg);
  137. DispatchMessage(&msg);
  138. }
  139. return 0;
  140. }

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

Podgląd programu Rysowanie
Rys. 2
Podgląd programu Rysowanie

Komentarze