Win32汇编--使用资源--对话框--在对话框中使用子窗口控件(2)

Win32汇编--使用资源--对话框--在对话框中使用子窗口控件(2)

 

2、子窗口控件的通用使用方法

       由于子窗口控件实际上就是窗口,大部分窗口函数对它们都是适用的,如可以用EnableWindow在灰化和允许状态之间切换,可以用ShowWindow在显示和隐藏之间切换,可以用GetWindowTextSetWindowText来改变上面的文字,也可以用MoveWindow来改变大小和移动位置等。在Control.asm中用“显示图片”复选框切换图片框的隐藏和显示,用的就是ShowWindow函数,处理“允许更换图片”复选框时切换“更换图片”按钮的状态,用的是EnableWindow函数。

 

       除了可以用对子窗口控件使用窗口的通用函数外,还可以使用针对它们的专用函数。下面介绍一些常用的函数。

 

       在资源脚本文件中定义的是控件的ID,当这些子窗口控件被创建以后同样会有一个窗口句柄,但既然它们不是由我们由自己创建的,那么怎么知道它们的窗口句柄呢?有一个函数可以从ID中获取子窗口句柄:

       invoke GetDlgItem, hDlg, dwIDDlgItem

       mov    hDlgItem, eax

       函数的输入参数是对话框句柄和ID值,返回值是子窗口句柄;反过来,有两种方法可以从子窗口句柄获取ID

       1invoke GetDlgCtrolID, hWndCtrl ;输入子窗口句柄,返回值是控件ID

       2invoke GetWindowLong, hWndCtrl, GWL_ID

 

       当需要向控件发送消息的时候,当然可以先用GetDlgItem获取子窗口句柄再用SendMessage函数,但有一个函数更为简便:

       invoke SendDlgItemMessage, hDlg, dwIDDlgItem, Msg, wParam, lParam

 

       这个函数可以直接向控件发送消息,只需要在参数中指定对话框句柄和子窗口ID(注意:并没有PostDlgItemMessage这样的函数!)。

 

       如果要想知道在一个控件上按下了Tab键或Shift+Tab键会跳到哪一个控件上去,也就是说下一个或上一个Tab停留位在哪里,可以使用GetNextDlgTabItem函数:

       invoke GetNextDlgTabItem, hDlg, hCtl, bPrevious

       .if eax

        mov hWinNext, eax

.endif

 

其中的bPrevious参数指定了搜索的方向;与之相似,使用GetNextDlgGroupItem函数可以返回下一个分组的位置:

invoke GetNextDlgGroupItem, hDlg, hCtl, bPrevious

.if eax

   mov hWinNext, eax

.endif

 


3
、使用单选钮和复选框

       单选钮是互斥的选择钮,同一组的多个单选钮只能有一个被选中,单选钮的外形是一个圆形的标记加上文本,圆形中有黑点表示被选中。复选框不是互斥的,多个复选框的状态不会互相影响,复选框的外形是一个方框加上文本,方框中可以用有无对钩来表示是否被选中。

 

       单选钮和复选框控件都是基于Button类的,只不过它们的窗口风格分别是BS_RADIOBUTTONBS_CHECKBOX。既然它们是特殊的“按钮”,所以和它们有关的函数都带有“Button”一词,查看一个单选钮或复选框是否被选中可以用下面的函数来检测:

       invoke IsDlgButtonChecked, hDlg, nIDButton

       函数的返回值可能是BST_CHECKED(选中状态),BST_INDETERMINATE3态复选框的灰化状态)或BST_UNCHECKED(未选中状态)。也可以用向子窗口控件发送BM_GETCHECK消息的方法来检测,返回值和上面的函数是一样的。

 

       如果想设置单选钮或复选框的状态,可以使用下面的语句:

       invoke CheckDlgButton, hDlg, nIDButton, uCheck

       参数uCheckBST_CHECKEDBST_INDETERMINATEBST_UNCHECKED来表示需要设置的状态,含义同上。向控件发送BM_SETCHECK消息也可以取得同样的效果,这时消息的wParam中放置需要设置的状态。

 

       复选框是不互斥的,所以可以随意设置状态。而对于BS_RADIOBUTTON风格的单选钮来说,并不是把某个按钮设置为选中状态以后,同组的其他按钮就会自动变成非选中状态,所以用CheckDlgButton函数选中了一个单选钮以后,如果不是手动把同组的其他按钮全部改为非选中状态(逐个地调用CheckDlgButton),就会看到同时有两个单选钮是选中的。但把同组的所有单选钮逐个地设置显得有点麻烦,所以针对单选钮有一个专用函数:

       invoke CheckRadioButton, hDlg, nIDFirstButton, nIDLastButton, nIDCheckButton

       这个函数把IDnIDFirstButtonnIDLastButton之间的单选钮全部设置为非选中状态,只有nIDCheckButton是选中状态,当然在使用中要注意将这一批ID定义为连续的数值。

       如果还嫌CheckRadioButton有点麻烦,还有一种最简单的办法——使用自动单选钮,同组的AUTORADIOBUTTON会随着用户选中一个而自动清除其他单选钮的状态,所以在程序中只需要在初始化的时候预设一次,其他时间就可以不必关心设置问题了,以后唯一用到的就是调用IsDlgButtonChecked检查状态了。

 

4、使用静态控件

       静态控件是基于Static类的子窗口控件,之所以叫“静态”控件,是因为它是“安静”的——它们不向对话框发送WM_COMMAND消息,所以静态控件的ID一般是没有用处的,定义时常常将它们定义为-1,如果需要在程序中改变属性,那么也可以为静态控件指定一个唯一的ID

 

       资源脚本文件中可以使用缩写的基于Static类的有LTEXTCTEXTRTEXT(文本框)和ICON(图标框),除了这些常用的类型之外,Static类还可以用CONTROL语句通过指定不同的窗口风格派生出不同用途的控件来。

 

       下面说明静态控件的一些用法。

       对于文本框,文本长度超过边界的时候默认是自动换行的,但如果同时指定SS_SIMPLE风格的话,就不会自动换行。读者可以在程序中用SetWindowText或发送WM_SETTEXT消息来动态改变显示的文本,同样,也可以用GetWindowText或发送WM_GETTEXT消息来获取其中的文本。

 

       静态控件可以用来构筑简单的线条和图形,如果指定SS_BLACKFRAMESS_GRAYFRAMESS_WHITEFRAME风格,那么静态控件显示为填充的矩形,填充颜色分别是黑色、灰色或白色;而指定SS_BLACKRECTSS_GRAYRECTSS_WHITERECT风格的话,则显示为非填充的矩形框,边线颜色是黑色、灰色或白色。

 

       静态控件也可以用来做立体感的线条或边框,指定SS_ETCHEDHORZ风格显示为横线,指定SS_ETCHEDVERT风格显示为竖线,指定SS_ETCHEDFRAME风格则显示为立体的矩形框,视觉上的效果似于没有文字的GROUPBOX

 

       静态控件还有一个用途是做图形显示,当图形是图标的时候,用ICON语句就可能定义了,其默认的风格是SS_ICON,如果想使用位图,那么可以指定SS_BITMAP风格,例子程序中的图片框就是这样定义的。

CONTROL IDB_1, IDC_BMP, “Static”, SS_BITMAP | WS_CHILD | WS_VISIBLE, 5, 5, 40, 95

       在这里,“文字”部分指定位图资源的ID,前面已经把Picture1.bmp的资源ID定义为IDB_1IDC_BMP是图片框自己的ID,如果不需要在程序中改变图片的话,那么这里可以定义为-1

 

       在程序中可以通过向控件发送STM_SETIMAGE消息来设置新的图片,消息的wParam指定图片的格式,取值可以是IMAGE_BITMAPIMAGE_CURSORIMAGE_ICON,分别对应新图片的格式,lParam是图片的句柄,如果是位图,lParam就是用LoadBitmap装入的位图句柄,同样,图片类型是光标和图标的时候,这里就是用LoadCursorLoadIcon装入的句柄。

 

       在例子程序中,用来改变图片框图片的语句是:

       invoke SendDlgItemMessage, hWnd, IDC_BMP, STM_SETIMAGE, IMAGE_BITMAP, eax

       eax中是位图句柄,IDC_BMP是图片框的IDwParamIMAGE_BITMAP表示要设置的图片类型是位图。

 

5、使用文本编辑控件

       文本编辑控件是基于Edit类的控件,可以用缩写EDITTEXT定义,读者可以在文本编辑控件中输入并编辑文本。每当用户在文本编辑控件中输入一个字符的时候,控件就会向对话框过程发送一个WM_COMMAND消息,所以在例子程序中,当在自定义文字的编辑框中每输入一个字,标题栏文字就会马上改变。

 

       要获取编辑框中的文本有多种方法,可以用GetWindowText,也可以用发送WM_GETTEXT消息的办法,要设置文本,同样可以用SetWindowText或发送WM_SETTEXT,但最简便的办法还是使用下面的函数:

       invoke GetDlgItemText, hDlg, nIDDlgItem, lpString, nMaxCount      ;取文本

       invoke SetDlgItemText, hDlg, nIDDlgItem, lpString                         ;设置文本

       lpString是放置字符的缓冲区地址,用GetDlgItemText函数来获取文本的时候,要用nMaxCount参数指定缓冲区的最大长度,以免获取的文本长度超过缓冲区长度引起溢出,设置的时候若使用SetDlgItemText函数时就不需要这个参数。

 

       在实际使用中,经常要在文本编辑控件中输入输出数值型参数,将文本转换为数值比较麻烦,把数值转换为文本也要经过一个wsprintf调用,为了简化操作,Windows提供了两个函数来处理这个问题:

       invoke SetDlgItemInt, hDlg, nIDDlgItem, uValue, bSigned                ;设置控件中的数值

       invoke GetDlgItemInt, hDlg, nIDDlgItem, lpTranslated, bSigned              ;取控件中的数值

       SetDlgItemInt函数将uValue参数先转换成字符串格式,然后设置到文本编辑控件中,bSigned参数指定了uValue的格式,如果是TRUE的话,表示uValue是有符号数;是FALSE的话,表示uValue是无符号数。

       GetDlgItemInt函数则将对话框中的文本转换成数值型返回,同样,用bSigned指定转换的方式,TRUE表示按照符号数格式转换,这时函数会检测文本的第一个字符是不是负号;FALSE则按照无符号数转换。参数lpTranslated是指向一个dword型变量的指针,GetDlgItemInt会在这个变量中返回BOOL类型值表示函数是否调用成功,成功则返回TRUE,有这样一个参数的原因是函数的返回值用来返回转换后的数值了,以至于没有地方可以表示函数是否执行成功。当然,lpTranslated参数也可以输入NULL,这样,当函数返回0的时候就无法知道是文本框是“ 0 还是文本不符合格式造成转换失败。

 

       SetDlgItemIntGetDlgItemInt函数不仅适用于文本编辑控件,所有对其上面的文本可以修改的控件都可以使用它们。

 

       使用文本编辑控件的时候,文本的长度也是个需要注意的问题。如果控件的宽度定义得过窄,当字符填充到最右边的时候,编辑框就不允许继续输入了,为了继续输入并让文本自动卷动,可以指定WS_HSCROLL风格;反之,定义WS_HSCROLL风格后输入文本的长度不受限制又不好,那么可以用向控件发送EM_LIMITTEXT消息的方式来设定最大长度。

 

       下面的例子将IDC_EDIT的输入最大长度定为10个字符:

       invoke SendDlgItemMessage, hDlg, IDC_EDIT, EM_LIMITTEXT, 10, NULL

 

       另外,有时候可能需要把编辑框设置为只读的(和灰化不同,灰化的编辑框中文本无法进行任何操作,包括卷动操作,而只读的仅仅是不能修改),要把初始状态定义为只读的,只需在定义语句中加上ES_READONLY风格,在程序中需要动态改变只读状态可以发送EM_SETREADONLY消息,下面的第一句把编辑框设为只读,第二句把编辑框改回到可写状态:

       invoke SendDlgItemMessage, hDlg, IDC_EDIT, EM_SETREADONLY, TRUE, NULL ;只读

       invoke SendDlgItemMessage, hDlg, IDC_EDIT, EM_SETREADONLY, FALSE, NULL;可写

 

       文本编辑框在默认状态下是单行的,也可以通过加上EM_MULTILINE风格变成多行的,这时可以同时加上WS_VSCROLL风格显示一个垂直方向的滚动条。

 

6、使用滚动条

       滚动条有水平和垂直两种,默认的SCROLLBAR语句定义的是水平的滚动条,它的默认风格是SBS_HORZ,例子程序中用下面的语句定义了一个水平滚动条:

       SCROLLBAR IDC_SCROLL, 6, 118, 125, 10

       如果要定义垂直的滚动条,那么要指明SBS_VERT风格:

       SCROLLBAR IDC_SCROLL, x, y, 宽度, 高度, SBS_VERT

 

       和其他子窗口控件发送WM_COMMAND消息不同,水平滚动条向对话框窗口发送WM_HSCROLL消息,而垂直滚动条则发送WM_VSCROLL消息,所以针对两种方式的滚动条要分别处理不同的消息。

 

       WM_xSCROLL消息的参数如下所示:

       wParam的低16 = nScrollCode             ;动作码

       wParam的高16 = nPos                       ;滚动条当前位置

       lParam = hwndScrollBar                                   ;滚动条控件的窗口句柄

 

       其中nScrollCode代表了滚动条的当前动作,定义值及其含义如下:

       SB_BOTTOM                      滚动条移到了最下/右边。

       SB_ENDSCROLL                用户停止了滚动动作。

       SB_THUMBPOSITION        滚动条被拖动到某处。

       SB_THUMBTRACK             滚动条在拖动中。

       SB_TOP                              滚动条移到了最上/左边。

       SB_LINELEFT                    滚动条左移了一格(对于水平滚动条)。

       SB_LINERIGHT                  滚动条右移了一格(对于水平滚动条)。

       SB_PAGELEFT                    滚动条左移了一页(对于水平滚动条)。

       SB_PAGERIGHT                 滚动条右移了一页(对于水平滚动条)。

       SB_LINEDOWN                  滚动条下移了一格(对于垂直滚动条)。

       SB_LINEUP                        滚动条上移了一格(对于垂直滚动条)。

       SB_PAGEDOWN                 滚动条下移了一页(对于垂直滚动条)。

       SB_PAGEUP                        滚动条上移了一页(对于垂直滚动条)。

 

       nPos的值只有当动作码是SB_THUMBPOSITIONSB_THUMBTRACK时才有效,其他的时候为0

 

       第一眼看到SB_xxx动作码的时候,读者可能会以为水平滚动条和垂直滚动条的动作码是不相同的——水平滚动条是SB_xxxLEFTSB_xxxRIGHT,而垂直滚动条是SB_xxxUPSB_xxxDOWN,但在Windows.inc中查看一下就可以发现,SB_xxxLEFTSB_xxxUP在数值上是相同的,SB_xxxRIGHTSB_xxxDOWN也是如此,所以不同定义方法只是为了直观起见而已。

 

       以水平滚动条为例,处理滚动条消息的代码一般是如下结构:

       .elseif eax == WM_HSCROLL          ;窗口的消息处理分支,eaxwMsg

              mov eax, lParam

           .if    eax == hWnd滚动条1

                 mov eax, wParam

                 .if ax == SB_LINELEFT

                     dec 位置变量

                 .elseif ax == SB_LINERIGHT

                     inc   位置变量

                 .elseif ax == SB_PAGELEFT

                     sub   位置变量,页长

                 .elseif ax == SB_PAGERIGHT

                     add   位置变量,页长

                 .elseif ax == SB_THUMBPOSITION || ax == SB_THUMBTRACK

                     mov   eax, wParam

                     shr    eax, 16

                     mov   位置变量,eax

                 .endif

       .elseif eax == hWnd滚动条2

           ;处理滚动条2的代码,同上面的结构

.endif

 

在例子程序Control.asm中只定义了一个滚动条,所有的消息肯定都是它发出的,所以去掉了判断lParam是哪个滚动条的步骤直接处理wParam中的动作码。

 

在用户按动滚动条后,滚动条不会自己移动位置,它只是将用户的动作以WM_xSCROLL消息的形式反馈给程序,真正要移动它还是要靠程序来设置,所以代码中要根据不同的动作首先计算新的位置,并判断新的位置是否越界,例子程序中的这些代码判断新的位置是否超出0~100的范围,如果是,则校正到0~100之间:

cmp dwPos, 0

jge @F

mov dwPos, 0

@@:

cmp dwPos, 100

jle @F

mov dwPos,100

 

在介绍MASM语句的时候提到过,.if dwPos > 0语句只可以用来比较无符号数,所以在这里使用cmp指令自己测试分支而不是使用.if伪指令。

不计算好新位置的时候要将位置设置回去,用户才会看到滚动条移动了,方法是向滚动发送SBM_SETPOS消息:

invoke SendDlgItemMessage, hWnd, IDC_SCROLL, SBM_SETPOS, dwPos, TRUE

最后一个参数为TRUE表示设置后重新绘画滚动条。

在初始化的时候,要给滚动条发送SBM_SETRANGE消息来设定滚动范围:

invoke SendDlgItemMessage, hWnd, IDC_SCROLL, SBM_SETRANGE, 最小值, 最大值

 

如果需要获取滚动条的信息,可以尝试发送下面两个消息:SBM_GETPOS可以获取滚动条的当前位置,也就是上一次用SBM_SETPOS设置的值;SBM_GETRANGE可以获取滚动的范围,也就是用SBM_SETRANGE设置的值。

 

7、使用组合框

       顾名思义,组合框是一个“组合”起来的东西,它由一个可供选择的列表和一个可供输入的edit类组合而成。组合框让用户既可以自己输入文本,也可以选择列表中的某一项当做输入。用不同的风格定义可以产生3种类型的组合框。

       CBS_SIMPLE风格的组合框,它的上面可以输入文本,下面的列表可供选择预设文本;

       CBS_DROPDOWN风格的组合框,上面同样可以输入文本,但下面的列表是下拉式的,平时处于收起状态,点击编辑框右边的三角形才会拉下来;

       CBS_DROPDOWNLIST风格的组合框,它仅是一个下拉的选择框,上面的框中不允许输入文字。

 

       组合框中还有几种常用的、可以附加的风格:

       CBS_AUTOHSCROLL          输入过长的文本时输入框自动卷动。

       CBS_LOWERCASE              自动将所有的文本转换成小写。

       CBS_SORT                         自动将插入的文本项排序。

       CBS_UPPERCASE        自动将所有的文本转换成大写。

 

       组合框中列表框部分的文字添加、项目的选择等操作都是通过发送消息来完成的,主要的消息如下表所示:

组合框的消息

消息

wParam

lParam

说明

CB_ADDSTRING

0

字符串地址

把一个字符串添加到列表中

CB_INSERTSTRING

位置索引

字符串地址

把一个字符串插入到列表中

CB_FINDSTRING

开始查找的位置索引

查看的字符串

在列表中查找以lParam字符串开头的项,找到则返回位置索引,未找到则返回CB_ERP

CB_FINDSTRINGEXACT

位置索引

查找的字符串

精确查找字符串

CB_DELETESTRING

位置索引

0

删除一个列表项

CB_RESETCONTENT

0

0

删除所有的列表项

CB_GETLBTEXT

位置索引

缓冲区地址

获取指定列表项的字符串,缓冲区必须足够大

CB_GETLBTEXTLEN

位置索引

0

获取指定列表项的字符串长度

CB_GETCOUNT

0

0

获取列表项的总项数

CB_SETCURSEL

位置索引

0

选中一个列表项,并将列表项中的文字拷贝到编辑控件中

CB_SELECTSTRING

开始查找的位置索引

字符串地址

查找以lParam指定的字符串开始的列表项,如果找到则选中它并将字符串拷贝到编辑控件中

CB_GETCURSEL

0

0

获取当前选中的位置索引,没有选中的项目则返回CB_ERR

CB_SHOWDROPDOWN

状态

0

打开(状态为TRUE)或收起(状态为FALSE)下拉列表

CB_GETDROPPEDSTATE

0

0

检测列表的当前下拉状态,返回TRUE表示拉下,FALSE表示收起

 

       当用户在组合框中进行选择操作时,Windows向对话框过程发送WM_COMMAND消息,消息中wParam参数的低16位是组合框ID,高16位是通知码,用来表示用户的操作,通知码的定义如下表所示。

用户操作组合框后的通知码

通知码

说明

CBN_SELCHANGE

用户将要选择一个项目(鼠标移动到了这个项目上)

CBN_CLOSEUP

下拉列表关闭(可能是选择完成也可以是取消选择)

CBN_SELENDOK

用户完成选择项目

CBN_SELENDCANCEL

用户取消选择(鼠标移动到了某个项目上,但并没有按下而是点击了其他控件,或按动了Esc键)

CBN_DBLCLK

CBS_SIMPLE的组合框中双击了一个列表项

CBN_DROPDOWN

用户打开了下拉框(按动了编辑框的下拉按钮)

 

       如果想在用户选择了一个项目后做相应的动作,最好的办法就是处理CBN_SELENDOK通知码,因为这才意味着用户真正完成了一个选择动作,例子程序中就是这样处理的:

.elseif ax == IDC_TITLETEXT         ;WM_COMMAND消息中

       shr eax,16

       .if ax == CBN_SELENDOK

           invoke SendDlgItemMessage, hWnd, IDC_TITLETEXT, CB_GETCURSEL, 0, 0

                 ;根据返回的eax值做相应动作

       .endif

 

       以上的操作都是针对下拉列表部分的,另外也有很多消息是针对组合框中的编辑控件的,对组合框的窗口句柄发送WM_GETTEXTWM_SETTEXT,操作的对象就是组合框的编辑控件;如果要限制控件中文本的最大输入长度,可以发送CB_LIMITTEXT的消息,这时候wParam参数指定最大数量;当用户在编辑框中编辑文本的时候,Windows在用户输入之后、字符显示之前会发送CBN_EDITUPDATE通知码;当字符在编辑框中显示以后,又会发送CBN_EDITCHANGE通知码。所以在处理WM_COMMAND消息时通过处理这两个通知码可以检测到用户的输入操作。

 

       组合框是子窗口控件中比较复杂的一种,这里仅介绍了常用的消息和通知码。

 

你可能感兴趣的:(Win32汇编--使用资源--对话框--在对话框中使用子窗口控件(2))