![]() |
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 ENDSPodsumowują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