Wyjście Spis treści Poprzedni Następny
Autor:
©Iczelion |
©2008 mgr Jerzy Wałaszek I LO w Tarnowie |
|
Na tej lekcji poznamy kontrolkę podpowiedzi (tooltip control). Co to jest, jak to utworzyć i zastosować.
Załaduj {ten przykład}
Podpowiedź jest małym, prostokątnym oknem wyświetlanym, gdy kursor myszki unosi się ponad określonym obszarem. Okno podpowiedzi zawiera pewien tekst, który programista życzy sobie wyświetlić dla użytkownika. W tym sensie podpowiedź odgrywa taka sama rolę, co wiersz stanu, lecz znika ona, gdy użytkownik kliknie lub przesunie wskaźnik myszki poza wyznaczony obszar. Prawdopodobnie znasz podpowiedzi powiązane z przyciskami paska narzędziowego. Te "podpowiedzi" są udogodnieniami udostępnianymi przez kontrolkę paska narzędziowego (tolbar control). Jeśli chcesz mieć podpowiedzi dla innych okien / kontrolek, musisz utworzyć własną kontrolkę podpowiedzi.
Teraz gdy już wiesz, czym jest podpowiedź, przejdźmy do sposobów jej tworzenia i używania. Poniżej wymieniam kolejne kroki:
Zbadamy teraz szczegółowo każdy z wymienionych powyżej kroków.
Kontrolka podpowiedzi jest kontrolką wspólną. Skoro tak, to musisz wywołać gdzieś w swoim kodzie źródłowym funkcję InitCommonControls, aby MASM niejawnie skonsolidował twój program z biblioteką comctl32.dll. Kontrolkę podpowiedzi tworzysz za pomocą CreateWindowEx. Typowy scenariusz jest następujący:
.DATA TooltipClassName DB "Tooltips_class32", 0 .CODE ... INVOKE InitCommonControls INVOKE CreateWindowEx, NULL, ADDR TooltipClassName,\ NULL, TIS_ALWAYSTIP,\ CW_USEDEFAULT, CW_USEDEFAULT,\ CW_USEDEFAULT, CW_USEDEFAULT,\ NULL, NULL, hInstance, NULL
Zwróć uwagę na styl okna TIS_ALWAYSTIP, który określa, iż podpowiedź będzie pokazywana, gdy wskaźnik myszki jest ponad wyznaczonym obszarem bez względu na stan okna zawierającego ten obszar. Mówiąc prosto, jeśli zastosujesz ten znacznik, to gdy wskaźnik myszki unosi się ponad obszarem zarejestrowanym dla kontrolki podpowiedzi, okno podpowiedzi pojawi się, nawet jeśli okno leżące pod wskaźnikiem myszki jest nieaktywne.
W wywołaniu CreateWindowEx nie musisz dołączać styli WS_POPUP i WS_EX_TOOLWINDOW, ponieważ procedura okna dla kontrolki podpowiedzi dodaje je automatycznie. Również nie musisz określać współrzędnych, wysokości i szerokości okna podpowiedzi: kontrolka podpowiedzi ustawi je automatycznie do rozmiaru wyświetlanego tekstu, zatem w czterech parametrach przekazujemy wartość CW_USEDEFAULT. Pozostałe parametry nie są godne uwagi.
Kontrolka podpowiedzi jest utworzona, lecz nie pojawia się natychmiast. Chcemy pokazać okno podpowiedzi, gdy wskaźnik myszki unosi się ponad jakimś obszarem. Teraz nadszedł czas określenia tego obszaru. W naszym żargonie obszar ten nosi nazwę "narzędzia" (tool). Narzędzie jest prostokątem na obszarze roboczym okna, dla którego kontrolka podpowiedzi będzie monitorowała pozycję wskaźnika myszki. Jeśli wskaźnik myszki unosi się ponad narzędziem, to pojawia się okno podpowiedzi dla tego narzędzia (tooltip window). Prostokąt może pokrywać cały obszar roboczy lub tylko jego część. Zatem możemy rozdzielić narzędzie na dwa rodzaje: narzędzie implementowane jako okno i narzędzie implementowane jako prostokąt w obszarze roboczym okna. Oba rodzaje posiadają swoje własne zastosowania. Narzędzie pokrywające cały obszar roboczy okna jest najczęściej używane z kontrolkami takimi jak przyciski, kontrolki edycyjne itp. Nie musisz określać współrzędnych ani rozmiarów tego narzędzia: zakłada się, iż pokrywa cały obszar roboczy okna. Narzędzie implementowane jako prostokąt w obszarze roboczym jest użyteczne, gdy chcesz podzielić ten obszar roboczy okna na kilka regionów bez stosowania okien potomnych. Przy tym rodzaju narzędzia musisz określić współrzędne jego lewego górnego narożnika oraz szerokość i wysokość. Dane te umieszczasz w strukturze TOOLINFO o następującej definicji:
TOOLINFO STRUCT cbSize DWORD ? uFlags DWORD ? hWnd DWORD ? uId DWORD ? rect RECT <> hInst DWORD ? lpszText DWORD ? lParam LPARAM ? TOOLINFO ENDS
Podsumowując, musisz wypełnić strukturę TOOLINFO przed wysłaniem jej do kontrolki podpowiedzi. Struktura ta opisuje charakterystykę pożądanego narzędzia.
Po wypełnieniu struktury TOOLINFO musisz wysłać ją do kontrolki podpowiedzi. Kontrolka ta może obsługiwać wiele narzędzi, zatem zwykle nie potrzeba tworzyć więcej niż jednej kontrolki dla jednego okna. Rejestrowanie narzędzia w kontrolce podpowiedzi polega na wysłaniu do niej wiadomości TTM_ADDTOOL. Parametr wParam nie jest używany, a lParam musi zawierać adres struktury TOOLINFO do zarejestrowania.
.DATA? ... ti TOOLINFO <> ... .CODE ... <wypełnij strukturę TOOLINFO> ... INVOKE SendMessage, hwndTooltip, TTM_ADDTOOL, NULL, ADDR ti ...
Funkcja SendMessage dla tej wiadomości zwróci TRUE, jeśli narzędzie zostało poprawnie zarejestrowane w kontrolce podpowiedzi, a FALSE w przypadku przeciwnym.
Narzędzie możesz wyrejestrować wysyłając wiadomość TTM_DELTOOL do kontrolki podpowiedzi.
Gdy powyższy etap zostanie zakończony, kontrolka podpowiedzi zna już obszar monitorowania wiadomości myszki oraz tekst do wyświetlenia w oknie podpowiedzi. Brakuje jedynie elementu wyzwalającego te działania. Przemyśl to: obszar określony przez narzędzie znajduje się w obszarze roboczym innego okna. Jak kontrolka podpowiedzi może przechwycić wiadomości myszki przeznaczone dla tego okna? Musi to zrobić, aby zmierzyć czas przebywania wskaźnika myszki ponad jakimś punktem narzędzia, a gdy zadany czas upłynie, kontrolka podpowiedzi ma wyświetlić okno z tekstem podpowiedzi. Istnieją dwie metody osiągnięcia tego celu, jedna wymagająca współpracy z oknem zawierającym narzędzie, a druga nie korzystająca z tej współpracy ze strony okna.
Wszystkie inne wiadomości są ignorowane. Stąd w procedurze okna zawierającego narzędzie musi istnieć mechanizm przełączający w stylu:
WndProc PROC hWnd:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD ... .IF uMsg==WM_CREATE ... .ELSEIF uMsg==WM_LBUTTONDOWN || uMsg==WM_MOUSEMOVE ||\ uMsg==WM_LBUTTONUP || uMsg==WM_RBUTTONDOWN ||\ uMsg==WM_MBUTTONDOWN || uMsg==WM_RBUTTONUP ||\ uMsg==WM_MBUTTONUP INVOKE SendMessage, hwndTooltip, TTM_RELAYEVENT, NULL, ADDR msg ...
To jest to. Na tym etapie twoja kontrolka podpowiedzi jest w pełni funkcjonalna. Istnieje kilka użytecznych wiadomości związanych z podpowiedziami, które powinieneś znać.
Prezentowany przykład programu wyświetla proste okno dialogowe z dwoma przyciskami. Obszar roboczy okna dialogowego podzielony został na cztery obszary: lewy górny, lewy dolny, prawy górny i prawy dolny. Każdy obszar określony jest jako narzędzie z własnym tekstem podpowiedzi. Dwa przyciski również posiadają swoje własne teksty podpowiedzi.
.386 .MODEL FLAT, STDCALL OPTION CASEMAP:NONE INCLUDE \masm32\include\windows.inc INCLUDE \masm32\include\kernel32.inc INCLUDE \masm32\include\user32.inc INCLUDE \masm32\include\comctl32.inc INCLUDELIB \masm32\lib\comctl32.lib INCLUDELIB \masm32\lib\user32.lib INCLUDELIB \masm32\lib\kernel32.lib DlgProc PROTO :DWORD, :DWORD, :DWORD, :DWORD EnumChild PROTO :DWORD, :DWORD SetDlgToolArea PROTO :DWORD, :DWORD, :DWORD, :DWORD, :DWORD .CONST IDD_MAINDIALOG EQU 101 IDC_EXIT EQU 1001 .DATA ToolTipsClassName DB "Tooltips_class32", 0 MainDialogText1 DB "Lewy górny obszar okna dialogowego", 0 MainDialogText2 DB "Prawy górny obszar okna dialogowego", 0 MainDialogText3 DB "Lewy dolny obszar okna dialogowego", 0 MainDialogText4 DB "Prawy dolny obszar okna dialogowego", 0 .DATA? hwndTool DD ? hInstance DD ? .CODE start: INVOKE GetModuleHandle, NULL mov hInstance, eax INVOKE DialogBoxParam, hInstance, IDD_MAINDIALOG, NULL, ADDR DlgProc, NULL INVOKE ExitProcess, eax DlgProc PROC hDlg:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD LOCAL ti : TOOLINFO LOCAL id : DWORD LOCAL rect : RECT .IF uMsg==WM_INITDIALOG INVOKE InitCommonControls INVOKE CreateWindowEx, NULL, ADDR ToolTipsClassName, NULL,\ TTS_ALWAYSTIP, CW_USEDEFAULT,\ CW_USEDEFAULT, CW_USEDEFAULT,\ CW_USEDEFAULT, NULL, NULL,\ hInstance, NULL mov hwndTool, eax mov id, 0 mov ti.cbSize, SIZEOF TOOLINFO mov ti.uFlags, TTF_SUBCLASS push hDlg pop ti.hWnd INVOKE GetWindowRect, hDlg, ADDR rect INVOKE SetDlgToolArea, hDlg, ADDR ti, ADDR MainDialogText1, id, ADDR rect inc d INVOKE SetDlgToolArea, hDlg, ADDR ti, ADDR MainDialogText2, id, ADDR rect inc id INVOKE SetDlgToolArea, hDlg, ADDR ti, ADDR MainDialogText3, id, ADDR rect inc id INVOKE SetDlgToolArea, hDlg, ADDR ti, ADDR MainDialogText4, id, ADDR rect INVOKE EnumChildWindows, hDlg, ADDR EnumChild, ADDR ti .ELSEIF uMsg==WM_CLOSE INVOKE EndDialog, hDlg, NULL .ELSEIF uMsg==WM_COMMAND .IF (lParam!=0) && \ (WORD PTR wParam + 2 == BN_CLICKED) && \ (WORD PTR wParam==IDC_EXIT) INVOKE EndDialog, hDlg, NULL .ENDIF .ELSE mov eax, FALSE ret .ENDIF mov eax, TRUE ret DlgProc ENDP EnumChild PROC USES edi hwndChild:DWORD, lParam:DWORD LOCAL buffer[256]:BYTE mov edi, lParam ASSUME edi: PTR TOOLINFO push hwndChild pop [edi].uId or [edi].uFlags, TTF_IDISHWND INVOKE GetWindowText, hwndChild, ADDR buffer, 255 lea eax, buffer mov [edi].lpszText, eax INVOKE SendMessage, hwndTool, TTM_ADDTOOL, NULL, edi ASSUME edi: NOTHING ret EnumChild ENDP SetDlgToolArea PROC USES edi esi hDlg: DWORD,\ lpti: DWORD,\ lpText: DWORD,\ id: DWORD,\ lprect: DWORD mov edi, lpti mov esi, lprect ASSUME esi: PTR RECT ASSUME edi: PTR TOOLINFO .IF id==0 mov [edi].rect.left, 0 mov [edi].rect.top, 0 mov eax, [esi].right sub eax, [esi].left shr eax, 1 mov [edi].rect.right, eax mov eax, [esi].bottom sub eax, [esi].top shr eax, 1 mov [edi].rect.bottom, eax .ELSEIF id==1 mov eax, [esi].right sub eax, [esi].left shr eax, 1 inc eax mov [edi].rect.left, eax mov [edi].rect.top, 0 mov eax, [esi].right sub eax, [esi].left mov [edi].rect.right, eax mov eax, [esi].bottom sub eax, [esi].top mov [edi].rect.bottom, eax .ELSEIF id==2 mov [edi].rect.left, 0 mov eax, [esi].bottom sub eax, [esi].top shr eax, 1 inc eax mov [edi].rect.top, eax mov eax, [esi].right sub eax, [esi].left shr eax, 1 mov [edi].rect.right, eax mov eax, [esi].bottom sub eax, [esi].top mov [edi].rect.bottom, eax .ELSE mov eax, [esi].right sub eax, [esi].left shr eax, 1 inc eax mov [edi].rect.left, eax mov eax, [esi].bottom sub eax, [esi].top shr eax, 1 inc eax mov [edi].rect.top, eax mov eax, [esi].right sub eax, [esi].left mov [edi].rect.right, eax mov eax, [esi].bottom sub eax, [esi].top mov [edi].rect.bottom, eax .ENDIF push lpText pop [edi].lpszText INVOKE SendMessage, hwndTool, TTM_ADDTOOL, NULL, lpti ASSUME edi: NOTHING ASSUME esi: NOTHING ret SetDlgToolArea ENDP END start
Po utworzeniu głównego okna dialogowego tworzymy kontrolkę podpowiedzi za pomocą CreateWindowEx.
.IF uMsg==WM_INITDIALOG INVOKE InitCommonControls INVOKE CreateWindowEx, NULL, ADDR ToolTipsClassName, NULL,\ TTS_ALWAYSTIP, CW_USEDEFAULT,\ CW_USEDEFAULT, CW_USEDEFAULT,\ CW_USEDEFAULT, NULL, NULL,\ hInstance, NULL mov hwndTool, eax
Po wykonaniu tego zadania przechodzimy do definiowania czterech narzędzi dla każdego z narożników okna dialogowego.
mov id, 0 ;używane jako numer ID narzędzia mov ti.cbSize, SIZEOF TOOLINFO mov ti.uFlags, TTF_SUBCLASS ;nakazujemy kontrolce przejąć ;procedurę okna dialogowego push hDlg pop ti.hWnd ;uchwyt okna z narzędziem INVOKE GetWindowRect, hDlg, ADDR rect ;wymiary obszaru roboczego INVOKE SetDlgToolArea, hDlg, ADDR ti, ADDR MainDialogText1, id, ADDR rect
Inicjujemy pola struktury TOOLINFO. Zwróć uwagę, iż chcemy podzielić obszar roboczy na cztery narzędzie, zatem musimy znać jego rozmiary. Dlatego właśnie wywołujemy GetWindowRect. Nie chcemy samodzielnie przekazywać wiadomości myszki do kontrolki podpowiedzi, zatem określamy znacznik TTF_SUBCLASS.
SetDlgToolArea jest funkcją obliczającą granice prostokąta dla każdego narzędzia i rejestrującą to narzędzie w kontrolce podpowiedzi. Nie będę zagłębiał się w szczegóły tych obliczeń, wystarczy, gdy powiem, iż dzieli ona obszar roboczy na cztery równe ćwiartki. Następnie wysyła wiadomość TTM_ADDTOOL do kontrolki podpowiedzi przekazując adres struktury TOOLINFO w parametrze lParam.
INVOKE SendMessage, hwndTool, TTM_ADDTOOL, NULL, lpti
Po zarejestrowaniu wszystkich czterech narzędzi przechodzimy do przycisków w oknie dialogowym. Możemy obsłużyć każdy przycisk za pomocą jego numeru ID, lecz nie jest to zbyt wygodne. Zamiast tego użyjemy wywołania API EnumChildWindows do przeglądnięcia wszystkich kontrolek w oknie dialogowym i zarejestrowania ich w kontrolce podpowiedzi. Funkcja EnumChildWindows ma następującą składnię:
EnumChildWindows PROTO hWnd:DWORD, lpEnumFunc:DWORD, lParam:DWORD
Funkcja EnumChildProc ma następującą definicję:
EnumChildProc PROTO hwndChild:DWORD, lParam:DWORD
W naszym przykładzie wywołujemy funkcję EnumChildWindows następująco:
INVOKE EnumChildWindows, hDlg, ADDR EnumChild, ADDR ti
W parametrze lParam przekazujemy adres struktury TOOLINFO, ponieważ w funkcji EnumChild będziemy rejestrować każdą kontrolkę potomną w kontrolce podpowiedzi. Jeśli nie skorzystamy z takiej metody, to strukturę ti musimy zadeklarować jako globalną, co może wprowadzić błędy.
Gdy wywołujemy EnumChildWindows, system Windows będzie przeglądał kolejno kontrolki potomne na naszym oknie dialogowym i dla każdej z nich wywoływał funkcję EnumChild. Zatem skoro nasze okno dialogowe wyposażone jest w dwa przyciski, funkcja EnumChild zostanie wywołana dwukrotnie.
Funkcja EnumChild wypełnia istotne pola struktury TOOLINFO, a następnie rejestruje to narzędzie w kontrolce podpowiedzi.
EnumChild PROC USES edi hwndChild:DWORD, lParam:DWORD LOCAL buffer[256]:BYTE mov edi, lParam ASSUME edi: PTR TOOLINFO push hwndChild pop [edi].uId ;jako narzędzie stosujemy cały obszar kontrolki or [edi].uFlags, TTF_IDISHWND INVOKE GetWindowText, hwndChild, ADDR buffer, 255 lea eax, buffer ;jako podpowiedzi używamy tytułu okna mov [edi].lpszText, eax INVOKE SendMessage, hwndTool, TTM_ADDTOOL, NULL, edi ASSUME edi: NOTHING ret EnumChild ENDP
Zwróć uwagę, iż w tym przypadku stosujemy inny rodzaj narzędzia, które pokrywa cały obszar roboczy okna. Dlatego musimy wypełnić pole uId uchwytem okna zawierającego to narzędzie. Również musimy określić znacznik TTF_IDISHWND w polu uFlags.
Autorem
kursu jest
Iczelion. Kurs
programowania Windows znalazł się na serwerze I LO w Tarnowie za pisemną
zgodą autora. |
I Liceum Ogólnokształcące |
Pytania proszę przesyłać na adres email: i-lo@eduinf.waw.pl
W artykułach serwisu są używane cookies. Jeśli nie chcesz ich otrzymywać,
zablokuj je w swojej przeglądarce.
Informacje dodatkowe