📄 lion-tut-c27.htm
字号:
<html>
<head>
<link rel="stylesheet" href="../../asm.css">
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<title>Iczelion 的 Win32asm 教程</title>
</head>
<body bgcolor="#FFFFFF" background="../../images/back01.jpg">
<P align=center>第27课 工具提示控件</P>
<hr size=1>
<p> 我们将学习工具提示控件:它是什么如何创建和使用.下载<a href="files/tut27.zip">例子</a></p>
<h3>理论:</h3>
<p>工具提示是当鼠标在某特定区域上停留时显示的一个矩形窗口.工具提示窗口包含一些编程者想要显示的文本.在这点上,工具提示同状态栏的作用是一样的,所不同的是工具提示当单击或者远离指定区域的时候就会消逝,你可能熟悉与工具栏相关联的工具提示,那些"提示"是工具栏控件提供的便利.如果你想要在其它窗口、控件中显示工具提示的话,就不得不自己创建他们.</p>
<p>既然已经了解了什么是工具提示,就让我们来看看如何创建他们.大致步骤如下:</p>
<ol>
<li>用CreateWindowEx函数创建工具提示控件.
<li>定义一个工具提示控件将要监视鼠标移动的区域.
<li>将区域传递给工具提示控件
<li>将传递区域的鼠标消息转送给工具提示控件.(这步或许更早,具体依据转播消息的方法) </li>
</ol>
下面我们就来详细的讨论每一步.
<H4>工具提示控件的创建</H4>
工具提示控件是一种通用控件.同样,要在源代码某处调用<b>InitCommonControls</b>以便MASM能够将你的程序和comctl32.dll连接.
用CreateWindowEx创建工具提示控件,典型代码如下:
<BLOCKQUOTE><B>.data</B> <BR><B>TooltipClassName db "Tooltips_class32",0</B>
<BR><B>.code</B> <BR><B>.....</B> <BR><B>invoke InitCommonControls</B>
<BR><B>invoke CreateWindowEx, NULL, addr TooltipClassName, NULL,
TIS_ALWAYSTIP, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL</B></BLOCKQUOTE>
注意窗口风格:<b>TIS_ALWAYSTIP</b>指定了工具提示不管包含指定区域的窗口状态如何,当鼠标移过指定区域的时候,工具提示总是显示.简单的说就是,即使窗口处于非激活状态,鼠标移过工具提示指定区域的时候,工具提示也会出现.
<BR>
你不必在CreateWindowEx中包括<b>WS_POPUP</b> 和 <b>WS_EX_TOOLWINDOW</b>风格,因为工具提示处理过程会自动加上,你也不必指定工具提示窗口的坐标和宽高,控件会依据要显示的文字自动调节.四个参数,均使用<b>CW_USEDEFAULT</b>
,其余的参数都不太重要.
<H4>指定工具</H4>
<p>工具提示控件创建了但还没有显示,我们想要当鼠标指针在某个区域之上时显示工具提示窗口.现在需要指定这个区域.我们称这样的区域为"工具",“工具”就是工具提示控件监视鼠标指针是否移过的位于窗口客户区的一个方形区域.如果鼠标指针移过"工具",工具提示窗口就显示."工具"可覆盖整个客户区或者仅仅是它的一部分.因此我们把"工具"分成两种类型,一种是作为一个窗口,另一种则是某窗口客户区的一部分.两种各有所用.覆盖整个客户区的"工具"通常用于按钮、编辑控件等,你不必指定焦点域的坐标和大小:它被假定为窗口的整个客户区.仅覆盖窗口客户区一部分的"工具"在你想把窗口客户区分成几个部分但又不想使用子窗口时特别有用,但需要指定左上角的坐标和宽高.</p>
<p>使用如下的<B> TOOLINFO</B> 结构定义"工具":</p>
<BLOCKQUOTE><B>TOOLINFO STRUCT </B><BR><B>
cbSize
DWORD ? </B><BR><B>
uFlags
DWORD ? </B><BR><B>
hWnd
DWORD ? </B><BR><B>
uId
DWORD ? </B><BR><B>
rect
RECT <> </B><BR><B>
hInst
DWORD ? </B><BR><B>
lpszText
DWORD ? </B><BR><B>
lParam
LPARAM ? </B><BR><B>TOOLINFO ENDS </B></BLOCKQUOTE>
<CENTER>
<TABLE border=1>
<TBODY>
<TR align=middle bgColor=#666600>
<TD bgcolor="#CCCCCC">域名</TD>
<TD bgcolor="#CCCCCC">说明</TD>
</TR>
<TR>
<TD>cbSize</TD>
<TD>TOOLINFO结构的大小.必须填充, 如果这个区域不被正确填充Windows并不会报错,但你会得到不可预料的奇怪结果.</TD>
</TR>
<TR>
<TD height="229">uFlags</TD>
<TD height="229"> 指定焦点域的属性,可以是如下标志的联合:
<UL>
<LI><B>TTF_IDISHWND</B> "ID is hWnd".如果你指定了这个标志,就意味着你要使用覆盖整个客户区的"工具"
(上面第一种"工具"). 如果你使用了这个标志,你必须用你要使用的窗口句柄填充<b>uId</b>成员,如果你不指定这个成员,就意味着你要使用第二种"工具"、客户区窗口的一方形区域.在这种情况下,你就必须以方形区域的大小填充<b>rect</b>成员.
<LI><B>TTF_CENTERTIP </B> 通常工具提示窗口显示在鼠标的右下方,如果你指定了这个标志,不管鼠标的位置如何,工具提示总显示在焦点域总的中下方.
<LI><B>TTF_RTLREADING</B> .如果你的程序不是为阿拉伯或者希伯来语系统设计的,你完全可以不理它,它使得提示文本以从右至左的顺序显示,在其它系统中无效.
<LI><B>TTF_SUBCLASS</B> 如果你使用了这个标志,工具提示控件将子类化"工具"所在窗口以便截取发送给它的的鼠标消息,这个标志非常有用,否则你将不得不做更多的工作来向工具提示控件转发消息.</LI>
</UL>
</TD></TR>
<TR>
<TD>hWnd</TD>
<TD>包含"工具"的窗口句柄,如果你指定了<b>TTF_IDISHWND</b>标志,Windows将忽略该值,而使用<b>uId</b>成员的值作为窗口句柄.你需要填充这个域域如果:
<UL>
<LI>你不使用 <B>TTF_IDISHWND标志</B> (换句话说,你使用局部"工具")
<LI>你在 <b>lpszText</b> 成员中指定了<B>LPSTR_TEXTCALLBACK</B> .这个值告诉工具提示控件当需要显示提示窗口时,必须向包含"工具"的窗口查询应该显示什么.
这是一种实时的控件文本更新.如果你需要动态改变提示文本,你应当在 <b>lpszText</b>成员中指定<b>LPSTR_TEXTCALLBACK</b>值,控件就会向<b>hWnd</b>指定的窗口发送<b>TTN_NEEDTEXT</b>
消息. </LI>
</UL>
</TD></TR>
<TR>
<TD height="151">uId</TD>
<TD height="151">这个域的值可能有两种含义,依 <B>uFlags</B> 是否包含<B>TTF_IDISHWND</B>.
<UL>
<LI> 如果<b>TTF_IDISHWND</b>标志没有被指定就代表应用程序定义的"工具"ID,由于这意味着你使用仅覆盖客户区一部分的"工具",逻辑的推出一个客户区可能存在多个同样的焦点域(不存在交迭),Hwnd成员的一个窗口句柄就不够了,应用程序定义ID以区分他们因此而显得必要,只要唯一ID可以是任何值.
<LI> 如果<b>TTF_IDISHWND</b>标志被指定就表示整个客户区都作为焦点域的窗口句柄,你或许会奇怪为什么不用上面提到的<b>hWnd</b>成员的值来储存窗口句柄.答案是:<b>如果lpszText</b>指定为<b>LPSTR_TEXTCALLBACK</b>,Hwnd
可能已经被填充了.还有提供提示文本的窗口和包含"工具"的窗口可能不是同一个!(你可以设计一个提供两种服务的窗口程序,但太严格了,在这点上,微软为我们提供了更大的自由,欢呼吧!)</LI>
</UL>
</TD></TR>
<TR>
<TD height="54">rect</TD>
<TD height="54"> 指定"工具"大小的rect结构.这个结构定义了一个以hWnd指定窗口客户区左上角为基点的方形大小,简言之,如果你想指定客户区的一部分作为"工具"就得填充这个结构,如果你指定了<b>TTF_IDISHWND</b>标志
,控件就会忽略这个值.(你已经选择整个客户区作为"工具") </TD>
</TR>
<TR>
<TD height="52">hInst</TD>
<TD height="52">如果<b>lpszText</b>指定了字符串资源的标识,包含将作为工具提文本字符串资源的实例句柄.听起来有点费解,阅读一下<b>lpszText</b>的说明就可以明白这个域是干什么用的了.若lpszText不包含字符串资源标识,控件会忽略这个域.</TD>
</TR>
<TR>
<TD height="121">lpszText</TD>
<TD height="121">这个域可以有如下几个值:
<UL>
<LI>如果指定为<B>LPSTR_TEXTCALLBACK</B>, 工具提示控件就会向HWnd窗口发送<b>TTN_NEEDTEXT</b>消息以获得将要显示的字符串.提示文本的动态更新方法:每次显示提示窗口都改变提示文本.
<LI> 如果在这个域中指定字符串资源标识,当控件要在提示窗口中显示提示文本时,就搜索<b>hInst</b>成员标识的实例的字符串资源列表.由于字符串资源列表标识总是16位值,这个域的高字节将永远为0,这种方法在你想移植程序时非常有用,由于字符串资源以脚本形式定义,你不必修改源代码.只需要修改字符串列表提示文本就会相应改变,而不必担心引进bugs.
<LI>如果这个域的值不是<B>LPSTR_TEXTCALLBACK</B>并且高字节不为零, 控件截取这个值作为提示文本的指针,这是最简单的方法但也最不稳定.通过检查高字节区分字符串资源标识.</LI>
</UL>
</TD></TR></TBODY></TABLE>
</CENTER>
<P>
<P>总言之,你需要将<b>TOOLINFO</b>结构传递给工具提示控件之前填充填充好,它描述了你期望的"工具"属性.
<H4>向工具提示控件注册"工具"</H4>
填充完<B>TOOLINFO</B>结构后, 必须将其传递给控件 . 一个工具提示控件可以控制很多"工具",因此不必为一个窗口创建很多控件,为了注册"工具",向控件发送<b>TTM_ADDTOOL</b>消息
<b>wParam</b>不使用,<b>lParam</b>必须包含要注册的<b>TOOLINFO</b>结构的指针
<BLOCKQUOTE>.data? <BR>ti TOOLINFO <> <BR>....... <BR>.code <BR>.......
<BR><fill the TOOLINFO structure> <BR>....... <BR>invoke SendMessage,
hwndTooltip, TTM_ADDTOOL, NULL, addr ti</BLOCKQUOTE>
成功返回 <B>TRUE</B>,否则返回 <B>FALSE</B>.<BR>
发送 <B>TTM_DELTOOL</B>消息取消注册.
<H4>向工具提示控件转发鼠标消息</H4>
以上步骤完毕之后,控件知道了应当监视那一块区域和应该在提示窗口显示什么.唯一缺乏的就是激发机制. 想想看:"工具"指定的其它窗口的客户区的区域.控件如何截取发送向该窗口的消息呢?实际中需要截取消息以便了解鼠标停留了多长时间,当指定时间流逝以后,控件显示提示窗口.有两种方法:
一种需要包含"工具"窗口的合作,另一种则不需要.
<UL>
<LI>包含"工具"的窗口必须向控件发送 <B>TTM_RELAYEVENT</B> 以转发消息. <B>lParam</B>是指向要转发消息<B>MSG</B>的指针
控件仅处理如下鼠标消息 :
<UL>
<LI><B>WM_LBUTTONDOWN</B>
<LI><B>WM_MOUSEMOVE</B>
<LI><B>WM_LBUTTONUP</B>
<LI><B>WM_RBUTTONDOWN</B>
<LI><B>WM_MBUTTONDOWN</B>
<LI><B>WM_RBUTTONUP</B>
<LI><B>WM_MBUTTONUP</B> </LI>
</UL>
其它消息全被忽略,因此包含"工具"的窗口的处理过程必须包含像这样的选择:
<P><B>WndProc proc hWnd:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD</B>
<BR>
<B>.......</B> <BR>
<B> if uMsg==WM_CREATE</B> <BR>
<B> .............</B> <BR>
<B> elseif uMsg==WM_LBUTTONDOWN || uMsg==WM_MOUSEMOVE
|| uMsg==WM_LBUTTONUP || uMsg==WM_RBUTTONDOWN || uMsg==WM_MBUTTONDOWN ||
uMsg==WM_RBUTTONUP || uMsg==WM_MBUTTONUP</B> <BR>
<B> invoke SendMessage, hwndTooltip,
TTM_RELAYEVENT, NULL, addr msg</B> <BR>
<B> ..........</B> </P>
<LI>.你可以在<b>TOOLINFO</b>结构的<b>uFlags</b>成员指定 <b>TTF_SUBCLASS</b>标志。此标志告诉控件子类化包含"工具"的窗口以便无需窗口的协作便可捕获鼠标消息。由于除了控件自己处理截获的鼠标消息和指定<b>TTF_SUBCLASS</b>标志之外不用编写多余的代码,因此很易于使用。</LI>
</UL>
就是这些了,到这步为止,控件已经全功能了.还有几个你应当知道的相关消息.
<UL>
<LI><B>TTM_ACTIVATE.</B>如果你想动态的允许或者禁止工具提示控件,这个小消息就是为你而备.wParam值为<b>TRUE</b>,允许控件.若为<b>FALSE</b>,禁止控件.控件初始创建的时候无需发送消息激活他,便被自动设为允许状态.
<LI><B>TTM_GETTOOLINFO </B>and <B>TTM_SETTOOLINFO</B>. 如果你想在把TOOLINFO结构传递给控件之后获得或者改变其值,使用此消息.你需要用正确的uId
and hWnd值指定要改变的"工具".如果你只想改变rect成员的值,使用<b>TTM_NEWTOOLRECT</b> 消息,如果仅想改变提示文本,使用<b>TTM_UPDATETIPTEXT</b>消息.
<LI><B>TTM_SETDELAYTIME</B>. 使用此消息指定控件显示提示文本时的时间延迟.</LI>
</UL>
<H3>例子:</H3>
例子是一个有两个按钮的对话框,对话框的客户区分为4部分:左上、右上、左下、右下.每个区域都指定为有自己提示文本的"工具",两个按钮也有自己的提示文本.
<BLOCKQUOTE>
<p>.386 <BR>
.model flat,stdcall <BR>
option casemap:none <BR>
include \masm32\include\windows.inc <BR>
include \masm32\include\kernel32.inc <BR>
include \masm32\include\user32.inc <BR>
include \masm32\include\comctl32.inc <BR>
includelib \masm32\lib\comctl32.lib <BR>
includelib \masm32\lib\user32.lib <BR>
includelib \masm32\lib\kernel32.lib <BR>
DlgProc proto :DWORD,:DWORD,:DWORD,:DWORD <BR>
EnumChild proto :DWORD,:DWORD <BR>
SetDlgToolArea proto :DWORD,:DWORD,:DWORD,:DWORD,:DWORD <BR>
.const <BR>
IDD_MAINDIALOG equ 101 <BR>
.data <BR>
ToolTipsClassName db "Tooltips_class32",0 <BR>
MainDialogText1 db "This is the upper left area of the dialog",0 <BR>
MainDialogText2 db "This is the upper right area of the dialog",0 <BR>
MainDialogText3 db "This is the lower left area of the dialog",0 <BR>
MainDialogText4 db "This is the lower right area of the dialog",0 <BR>
.data? <BR>
hwndTool dd ? <BR>
hInstance dd ? <BR>
.code <BR>
start: <BR>
invoke GetModuleHandle,NULL <BR>
mov hInstance,eax <BR>
invoke DialogBoxParam,hInstance,IDD_MAINDIALOG,NULL,addr
DlgProc,NULL <BR>
invoke ExitProcess,eax</p>
<p> DlgProc proc hDlg:DWORD,uMsg:DWORD,wParam:DWORD,lParam:DWORD <BR>
LOCAL ti:TOOLINFO <BR>
LOCAL id:DWORD <BR>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -