6.1 传统控件 在上一课的表5.1已经列出了Windows的传统控件及其对应的控件类。在这些控件中,读者应该重点掌握命令按钮、选择框、单选按钮、编辑框、列表框和组合框。 .1.1 传统控件的控件通知消息 控件通过向父窗口发送控件通知消息来表明发生了某种事件.例如,当用户在按钮上单击鼠标时,按钮控件会向父窗口发送BN_CLICKED消息.传统控件的通知消息实际上是通过WM_COMMAND消息发给父窗口的(滚动条除外),在该消息的wParam中含有通知消息码(如BN_CLICKED)和控件的ID,在lParam中则包含了控件的句柄. 利用ClassWizard可以很容易地为控件通知消息加入消息映射和消息处理函数,这在上一章中已经演示过了.传统控件的消息映射宏是ON_XXXX,其中XXXX表示通知消息码,如BN_CLICKED.ON_XXXX消息映射如下所示,该宏有两个参数,一个是控件的ID,一个是消息处理函数名. ON_XXXX(nID, memberFxn) 消息处理函数的声明应该有如下形式: afx_msg void memberFxn( ); 例如,某按钮的BN_CLICKED消息的消息映射及其处理函数的声明如下所示 ON_BN_CLICKED(IDC_ADD,OnAdd) afx_msg void OnAdd( ); 有时,为了处理方便,需要把多个ID连续的控件发出的相同消息映射到同一个处理函数上.这就要用到ON_CONTROL_RANGE宏.ON_CONTROL_RANGE消息映射宏的第一个参数是控件消息码,第二和第三个参数分别指明了一组连续的控件ID中的头一个和最后一个ID,最后一个参数是消息处理函数名。例如,要处理一组单选按钮发出的BN_CLICKED消息,相应的消息映射如下所示:
函数OnRadioClicked的声明如下,该函数比上面的OnAdd多了一个参数nID以说明发送通知消息的控件ID. afx_msg void OnRadioClicked(UINT nID); ClassWizard不支持ON_CONTROL_RANGE宏,所以需要手工建立消息映射和消息处理函数.
6.1.2 静态控件 静态控件包括静态正文(Static Text)和图片控件(Picture)。静态正文控件用来显示正文。图片控件可以显示位图、图标、方框和图元文件,在图片控件中显示图片的好处是不必操心图片的重绘问题。静态控件不能接收用户的输入。在上一章中,读者已经用过静态正文和组框控件。图片控件的例子可以在AppWizard创建的IDD_ABOUTBOX对话框模板中找到,在该模板中有一个图片控件用来显示图标。 静态控件的主要起说明和装饰作用。MFC的CStatic类封装了静态控件。CStatic类的成员函数Create负责创建静态控件,该函数的声明为
参数lpszText指定了控件显示的正文。dwStyle指定了静态控件的风格,表6.1显示了静态控件的各种风格,dwStyle可将这些风格组合起来。rect是一个对RECT或CRect结构的引用,用来说明控件的位置和尺寸。pParentWnd指向父窗口,该参数不能为NULL。nID则说明了控件的ID。如果创建成功,该函数返回TRUE,否则返回FALSE. 表6.1 静态控件的风格
除了上表中的风格外,一般还要为控件指定WS_CHILD和WS_VISIBLE窗口风格。一个典型的静态正文控件的风格为WS_CHILD|WS_VISIBLE|SS_LEFT。 对于用对话框模板编辑器创建的静态控件,可以在控件的属性对话框中指定表6.1中列出的控件风格。例如,可以在静态正文控件的属性对话框中选择Simple,这相当于指定了SS_SIMPLE风格。 Cstatic类主要的成员函数在表6.2中列出。可以利用CWnd类的成员函数GetWindowText,SetWindowText和GetWindowTextLength等函数来查询和设置静态控件中显示的正文. 表6.2 CStatic类的主要成员函数
静态控件较简单,故这里就不举例说明了。 6.1.3 按钮控件 按钮是指可以响应鼠标点击的小矩形子窗口。按钮控件包括命令按钮(Pushbutton)、检查框(Check Box)、单选按钮(Radio Button)、组框(Group Box)和自绘式按钮(Owner-draw Button)。命令按钮的作用是对用户的鼠标单击作出反应并触发相应的事件,在按钮中既可以显示正文,也可以显示位图。选择框控件可作为一种选择标记,可以有选中、不选中和不确定三种状态。单选按钮控件一般都是成组出现的,具有互斥的性质,即同组单选按钮中只能有一个是被选中的。组框用来将相关的一些控件聚成一组.自绘式按钮是指由程序而不是系统负责重绘的按钮。 按钮主要是指命令按钮、选择框和单选按钮。后二者实际上是一种特殊的按钮,它们有选择和未选择状态。当一个选择框处于选择状态时,在小方框内会出现一个“√”,当单选按钮处于选择状态时,会在圆圈中显示一个黑色实心圆。此外,检查框还有一种不确定状态,这时检查框呈灰色显示,不能接受用户的输入,以表明控件是无效的或无意义的。 按钮控件会向父窗口发出如表6.3所示的控件通知消息。 表6.3 按钮控件的通知消息
FC的CButton类封装了按钮控件。CButton类的成员函数Create负责创建按钮控件,该函数的声明为
参数lpszCaption指定了按钮显示的正文。dwStyle指定了按钮的风格,如表6.4所示,dwStyle可以是这些风格的组合。rect说明了按钮的位置和尺寸。pParentWnd指向父窗口,该参数不能为NULL。nID是按钮的ID。如果创建成功,该函数返回TRUE,否则返回FALSE.
表6.4 按钮的风格
除了上表中的风格外,一般还要为控件指定WS_CHILD、WS_VISIBLE和WS_TABSTOP窗口风格,WS_TABSTOP使控件具有Tabstop属性。创建一个普通按钮应指定的风格为WS_CHILD|WS_VISIBLE|WS_TABSTOP。创建一个普通检查框应指定风格WS_CHILD|WS_VISIBLE|WS_TABSTOP| BS_AUTOCHECKBOX。创建组中第一个单选按钮应指定风格WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_GROUP| BS_AUTORADIOBUTTON,组中其它单选按钮应指定风格则不应该包括WS_TABSTOP和WS_GROUP。 对于用对话框模板编辑器创建的按钮控件,可以在控件的属性对话框中指定表6.4中列出的控件风格。例如,在命令按钮的属性对话框中选择Default button,相当于指定了BS_DEFPUSHBUTTON。 CButton类的主要的成员函数有: UINT GetState( ) const; 0x0003。用来获取检查框或单选按钮的状态。0表示未选中,1表示被选中,2表示不确定状态(仅用于检查框)。 0x0004。用来判断按钮是否是高亮度显示的。非零值意味着按钮是高亮度显示的。当用户点击了按钮并按主鼠标左键时,按钮会呈高亮度显示。 0x0008。非零值表示按钮拥有输入焦点。 void SetState( BOOL bHighlight ); int GetCheck( ) const; void SetCheck( int nCheck ); UINT GetButtonStyle( ) const; void SetButtonStyle( UINT nStyle, BOOL bRedraw = TRUE ); HBITMAP SetBitmap( HBITMAP hBitmap ); HBITMAP GetBitmap( ) const; HICON SetIcon( HICON hIcon ); HICON GetIcon( ) const; HCURSOR SetCursor( HCURSOR hCursor ); HCURSOR GetCursor( ); 另外,可以使用下列的一些与按钮控件有关的CWnd成员函数来设置或查询按钮的状态。用这些函数的好处在于不必构建按钮控件对象,只要知道按钮的ID,就可以直接设置或查询按钮。 void CheckDlgButton( int nIDButton, UINT nCheck ); void CheckRadioButton( int nIDFirstButton, int nIDLastButton, int nIDCheckButton ); int GetCheckedRadioButton( int nIDFirstButton, int nIDLastButton ); UINT IsDlgButtonChecked( int nIDButton ) const; 可以调用CWnd成员函数GetWindowText,GetWindowTextLength和SetWindowText来查询或设置按钮中显示的正文. MFC还提供了CButton的派生类CBitmapButton。利用该类可以创建一个拥有四幅位图的命令按钮,按钮在不同状态时会显示不同的位图,这样可以使界面显得生动活泼。如果读者对CBitmapButton感兴趣,可以参看VC5.0随盘提供的MFC例子CTRLTEST。 在上一章的Register例子中已演示了各种按钮控件的使用,故这里就不再举例了。
6.1.4 编辑框控件 编辑框(Edit Box)控件实际上是一个简易的正文编辑器,用户可以在编辑框中输入并编辑正文。编辑框既可以是单行的,也可以是多行的,多行编辑框是从零开始编行号的.在一个多行编辑框中,除了最后一行外,每一行的结尾处都有一对回车换行符(用"\r\n"表示).这对回车换行符是正文换行的标志,在屏幕上是不可见的. 编辑框控件会向父窗口发出如表6.5所示的控件通知消息。 表6.5
MFC的CEdit类封装了编辑框控件。CEdit类的成员函数Create负责创建按钮控件,该函数的声明为
参数dwStyle指定了编辑框控件风格,如表6.6所示,dwStyle可以是这些风格的组合。rect指定了编辑框的位置和尺寸。pParentWnd指定了父窗口,不能为NULL。编辑框的ID由nID指定。如果创建成功,该函数返回TRUE,否则返回FALSE. 表6.6 编辑框控件的风格
除了上表中的风格外,一般还要为控件指定WS_CHILD、WS_VISIBLE、WS_TABSTOP和WS_BORDER窗口风格,WS_BORDER使控件带边框。创建一个普通的单行编辑框应指定风格为WS_CHILD|WS_VISIBLE|WS_TABSTOP |WS_BORDER|ES_LEFT|ES_AUTOHSCROLL,这将创建一个带边框、左对齐正文、可水平滚动的单行编辑器。要创建一个普通多行编辑框,还要附加ES_MULTILINE|ES_WANTRETURN|ES_AUTOVSCROLL |WS_HSCROLL| WS_VSCROLL风格,这将创建一个可水平和垂直滚动的,带有水平和垂直滚动条的多行编辑器。 对于用对话框模板编辑器创建的编辑框控件,可以在控件的属性对话框中指定表6.6中列出的控件风格。例如,在属性对话框中选择Multi-line项,相当与指定了ES_MULTILINE风格。 编辑框支持剪贴板操作。CEdit类提供了一些与剪贴板有关的成员函数,如表6.7所示。 表6.7 与剪切板有关的CEdit成员函数
可以用下列CEdit或CWnd类的成员函数来查询编辑框。在学习下面的函数时,读者会经常遇到术语字符索引.字符的字符索引是指从编辑框的开头字符开始的字符编号,它是从零开始编号的.也就是说,字符索引实际上是指当把整个编辑正文看作一个字符串数组时,该字符所在的数组元素的下标.
int GetWindowText( LPTSTR lpszStringBuf, int nMaxCount ) const; int GetWindowTextLength( ) const; DWORD GetSel( ) const; int LineFromChar( int nIndex = –1 ) const; int LineIndex( int nLine = –1 ) const; int GetLineCount( ) const; int LineLength( int nLine = –1 ) const; int GetLine( int nIndex, LPTSTR lpszBuffer ) const; 下列CWnd或CEdit类的成员函数可用来修改编辑框控件。 void SetWindowText( LPCTSTR lpszString ); void SetSel( DWORD dwSelection, BOOL bNoScroll = FALSE ); void ReplaceSel( LPCTSTR lpszNewText, BOOL bCanUndo = FALSE ); 在调用上述函数时,如果涉及的是一个多行编辑框,那么除了LineLength和GetLine函数外,都要把回车和换行符考虑在内.例如,假设在编辑框中有如下几行正文: abcd efg ij 那么字母"e"的字符索引是6而不是4,因为"abcd"后面还有一对回车换行符.调用LineLength(7)会返回第二行的长度3.调用LineIndex(2)会得到11.调用LineFromChar(8)会返回1.如果没有选择任何正文,并且插入符在字母"e"上,那么调用GetSel返回值的低位和高位字都是6. 通过分析上述函数,我们可以总结出一些查询和设置编辑框的方法. 调用CWnd的成员函数GetWindowText和SetWindowText可以查询和设置编辑框的整个正文,在上一章的Register程序中,我们就使用过这两个函数. 如果想对多行编辑框逐行查询,那么应该先调用GetLineCount获得总行数,然后再调用GetLine来获取每一行的正文.下面一段代码演示了如何对多行编辑框进行逐行查询. char buf[40]; int total=MyEdit.GetLineCount(); int i,length; for(i=0;i<total;i++) { length=MyEdit.GetLine(i,buf,39); buf[length]=0; //加字符串结束符 . . . . . . } 可以利用LineIndex和LineFromChar来在字符索引和字符的行列坐标之间相互转换.下列代码演示了在已知字符索引的情况下,如何获得对应的行列坐标: 对于选择正文的查询和设置,应该利用函数GetSel、SetSel和ReplaceSel. 可以利用GetSel和SetSel来查询和设置插入符的位置.SetSel可以使编辑框滚动到插入符的新位置. 要获取插入符的行列坐标,可用下面的代码实现: MyEdit.SetSel(-1,0); //取消正文的选择 可以利用ReplaceSel函数在 插入符处插入正文,典型的代码如下所示: 可以利用ReplaceSel清除编辑框中的正文,典型的代码如下所示: 在后面的小节中,读者将会看到使用编辑框的例子. .1.5 滚动条控件 滚动条(Scroll Bar)主要用来从某一预定义值范围内快速有效地进行选择.滚动条分垂直滚动条和水平滚动条两种.在滚动条内有一个滚动框,用来表示当前的值.用鼠标单击滚动条,可以使滚动框移动一页或一行,也可以直接拖动滚动框.滚动条既可以作为一个独立控件存在,也可以作为窗口、列表框和组合框的一部分.Windows 95的滚动条支持比例滚动框,即用滚动框的大小来反映页相对于整个范围的大小.Windows 3.x使用单独的滚动条控件来调整调色板、键盘速度以及鼠标灵敏度,在Windows 95中,滚动条控件被轨道条取代(参见6.2.3)不提倡使用单独的滚动条控件. 需要指出的是,从性质上划分,滚动条可分为标准滚动条和滚动条控件两种.标准滚动条是由WS_HSCROLL或WS_VSCROLL风格指定的,它不是一个实际的窗口,而是窗口的一个组成部分(例如列表框中的滚动条),只能位于窗口的右侧(垂直滚动条)或底端(水平滚动条).标准滚动条是在窗口的非客户区中创建的.与之相反,滚动条控件并不是窗口的一个零件,而是一个实际的窗口,可以放置在窗口客户区的任意地方,它既可以独立存在,也可以与某一个窗口组合,行使滚动窗口的职能.由于滚动条控件是一个独立窗口,因此可以拥有输入焦点,可以响应光标控制键,如PgUp、PgDown、Home和End. MFC的CScrollBar类封装了滚动条控件.CScrollBar类的Create成员函数负责创建控件,该函数的声明为 BOOL Create( DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID ); 参数dwStyle指定了控件的风格.rect说明了控件的位置和尺寸.pParentWnd指向父窗口,该参数不能为NULL。nID则说明了控件的ID。如果创建成功,该函数返回TRUE,否则返回FALSE. 要创建一个普通的水平滚动条控件,应指定风格WS_CHILD|WS_VISIBLE|BS_HORZ.要创建一个普通的垂直滚动条控件,应指定风格WS_CHILD|WS_VISIBLE|BS_VERT. 主要的CScrollBar类成员函数如下所示: int GetScrollPos( ) const; int SetScrollPos( int nPos, BOOL bRedraw = TRUE ); void GetScrollRange( LPINT lpMinPos, LPINT lpMaxPos ) const; void SetScrollRange( int nMinPos, int nMaxPos, BOOL bRedraw = TRUE ); BOOL GetScrollInfo( LPSCROLLINFO lpScrollInfo, UINT nMask ); BOOL SetScrollInfo( LPSCROLLINFO lpScrollInfo, BOOL bRedraw = TRUE ); CWnd类也提供了一些函数来查询和设置所属的标准滚动条.这些函数与CScrollBar类的函数同名,且功能相同,但每个函数都多了一个参数,用来选择滚动条.例如,CWnd:: GetScrollPos 的声明为 int GetScrollPos( int nBar ) const; 无论是标准滚动条,还是滚动条控件,滚动条的通知消息都是用WM_HSCROLL和WM_VSCROLL消息发送出去的.对这两个消息的确省处理函数是CWnd::OnHScroll和CWnd::OnVScroll,它们几乎什么也不做.一般需要在派生类中对这两个函数从新设计,以实现滚动功能.这两个函数的声明为 afx_msg void OnHScroll( UINT nSBCode, UINT nPos, CScrollBar* pScrollBar ); afx_msg void OnVScroll( UINT nSBCode, UINT nPos, CScrollBar* pScrollBar );
表6.8 滚动条的通知消息码
6.1.8小节的例子中,读者将学会如何使用滚动条以及如何编写自己的OnHScroll函数. 6.1.6 列表框控件 列表框主要用于输入,它允许用户从所列出的表项中进行单项或多项选择,被选择的项呈高亮度显示.列表框具有边框,并且一般带有一个垂直滚动条.列表框分单选列表框和多重选择列表框两种.单选列表框一次只能选择一个列表项,而多重选择列表框可以进行多重选择.对于列表项的选择,微软公司有如下建议: 单击鼠标选择一个列表项,单击一个按钮来处理选择的项. 双击鼠标选择一个列表项是处理选择项的快捷方法. 列表框会向父窗口发送如表6.9所示的通知消息.
表6.9 列表框控件的通知消息
MFC的CListBox类封装了列表框.CListBox类的Create成员函数负责列表框的创建,该函数的声明是
参数dwStyle指定了列表框控件的风格,如表6.10所示,dwStyle可以是这些风格的组合.rect说明了控件的位置和尺寸.pParentWnd指向父窗口,该参数不能为NULL。nID则说明了控件的ID。如果创建成功,该函数返回TRUE,否则返回FALSE.
表6.10 列表框控件的风格
除了上表中的风格外,一般还要为列表框控件指定WS_CHILD、WS_VISIBLE、WS_TABSTOP、WS_BORDER和WS_VSCROLL风格.要创建一个普通的单选择列表框,应指定的风格为WS_CHILD|WS_VISIBLE|WS_TABSTOP|LBS_STANDARD.要创建一个多重选择列表框,应该在单选择列表框风格的基础上再加上 LBS_MULTIPLESEL或LBS_ EXTENDEDSEL.如果不希望列表框排序,就不能使用LBS_STANDARD风格. 对于用对话框模板编辑器创建的列表框控件,可以在控件的属性对话框中指定表6.10中列出的控件风格。例如,在属性对话框中选择Sort项,相当与指定了LBS_SORT风格。 CListBox类的成员函数有数十个之多.我们可以把一些常用的函数分为三类,在下面列出.需要说明的是,可以用索引来指定列表项,索引是从零开始的. 首先,CListBox成员函数提供了下列函数用于插入和删除列表项. int AddString( LPCTSTR lpszItem ); int InsertString( int nIndex, LPCTSTR lpszItem ); int DeleteString( UINT nIndex ); void ResetContent( ); int Dir( UINT attr, LPCTSTR lpszWildCard );
表6.11 Dir函数attr参数的含义
下列的CListBox成员函数用于搜索、查询和设置列表框. int GetCount( ) const; int FindString( int nStartAfter, LPCTSTR lpszItem ) const; int GetText( int nIndex, LPTSTR lpszBuffer ) const; int GetTextLen( int nIndex ) const; DWORD GetItemData( int nIndex ) const; int SetItemData( int nIndex, DWORD dwItemData );
int GetTopIndex( ) const; int SetTopIndex( int nIndex );
下列CListBox的成员函数与列表项的选择有关. int GetSel( int nIndex ) const; int GetCurSel( ) const; int SetCurSel( int nSelect ); int SelectString( int nStartAfter, LPCTSTR lpszItem ); int GetSelCount( ) const; int SetSel( int nIndex, BOOL bSelect = TRUE ); int GetSelItems( int nMaxItems, LPINT rgIndex ) const; int SelItemRange( BOOL bSelect, int nFirstItem, int nLastItem ); 在6.1.8小节的例子中,读者将会看到对列表框的测试. 6.1.7 组合框控件 组合框把一个编辑框和一个单选择列表框结合在了一起.用户既可以在编辑框中输入,也可以从列表框中选择一个列表项来完成输入.如上一章所提到的,组合框分为简易式(Simple)、下拉式(Dropdown)和下拉列表式(Drop List)三种.简易式组合框包含一个编辑框和一个总是显示的列表框。下拉式组合框同简易式组合框类似,二者的区别在于仅当单击下滚箭头后列表框才会弹出。下拉列表式组合框也有一个下拉的列表框,但它的编辑框是只读的,不能输入字符。 Windows中比较常用的是下拉式和下拉列表式组合框,在Developer Studio中就大量使用了这两种组合框.二者都具有占地小的特点,这在界面日益复杂的今天是十分重要的.下拉列表式组合框的功能与列表框类似.下拉式组合框的典型应用是作为记事列表框使用,既把用户在编辑框中敲入的东西存储到列表框组件中,这样当用户要重复同样的输入时,可以从列表框组件中选取而不必在编辑框组件中从新输入.在Developer Studio中的Find对话框中就可以找到一个典型的下拉式组合框. 要设计一个记事列表框,应采取下列原则: 在创建组合框时指定CBS_DROPDOWNLIST风格. 要限制列表项的数目,以防止内存不够. 如果在编辑框中输入的字符串不能与列表框组件中的列表项匹配,那么应该把该字符串插入到列表框中的0位置处.最老的项处于列表的末尾.如果列表项的数目超出了限制,则应把最老的项删除. 如果在编辑框中输入的字符串可以与列表框组件中的某一项完全匹配,则应该先把该项从列表的当前位置删除,然后在将其插入道列表的0位置处. 组合框控件会向父窗口发送表6.12所示的通知消息.
表6.12 组合框控件的通知消息
MFC的CComboBox类封装了组合框.需要指出的是,虽然组合框是编辑框和列表框的选择,但是CComboBox类并不是CEdit类和CListBox类的派生类,而是CWnd类的派生类. CComboBox的成员函数Create负责创建组合框,该函数的说明如下:
参数dwStyle指定了组合框控件的风格,如表6.10所示,dwStyle可以是这些风格的组合.rect说明的是列表框组件下拉后组合框的位置和尺寸.pParentWnd指向父窗口,该参数不能为NULL。nID则说明了控件的ID。如果创建成功,该函数返回TRUE,否则返回FALSE.
表6.13 组合框的风格
CBS_SIMPLE、CBS_DROPDOWN和CBS_DROPDOWNLIST分别用来将组合框指定为简易式、下拉式和下拉列表式.一般还要为组合框指定WS_CHILD、WS_VISIBLE、WS_TABSTOP、WS_VSCROLL和CBS_AUTOHSCROLL风格.如果要求自动排序,还应指定CBS_SORT风格. 对于用对话框模板编辑器创建的组合框控件,可以在控件的属性对话框中指定上表中列出的控件风格。例如,在属性对话框中选择Dropdown,相当于指定了CBS_DROPDOWN. CComboBox类的成员函数较多.其中常用的函数可粗分为两类,分别针对编辑框组件和列表框组件.可以想象,这些函数与CEdit类和CListBox类的成员函数肯定有很多类似之处,但它们也会有一些不同的特点.如果读者能从"组合框是由编辑框和列表框组成"这一概念出发,就能够很快的掌握CComboBox的主要成员函数. 事实上,绝大部分CComboBox的成员函数都可以看成是CEdit或CListBox成员函数的翻版.函数的功能,函数名,甚至函数的参数都是类似的.为了方便学习,在下面列出CComboBox类的成员函数时,采用了与对应的CEdit或CListBox成员函数相比较的做法.在成员函数的列表中,分别列出了成员函数名,对应的CEdit或CListBox成员函数,以及二者之间的不同之处.不同之处是指函数的功能、参数以及返回值有什么差别. 针对编辑框组件的主要成员函数如表6.14所示.该表的前三个函数实际上是CWnd类的成员函数,可用来查询和设置编辑框组件.
表6.14 针对编辑框组件的CComboBox成员函数
与CListBox的成员函数类似,针对列表框组件的CComboBox成员函数也可以分为三类.表6.15列出了用于插入和删除列表项的成员函数,表6.16列出了用于搜索、查询和设置列表框的成员函数,与列表项的选择有关的成员函数在表6.17中列出.需要指出的是,如果这些函数出错,则反回CB_ERR,而不是LB_ERR.另外,排序的组合框具有的是CBS_SORT风格,而不是LBS_SORT.
6.15 用于插入和删除列表项的CComboBox成员函数
6.16 用于搜索、查询和设置列表框的CComboBox成员函数
表6.17 与列表项的选择有关的CComboBox成员函数
另外,CComboBox的ShowDropDown成员函数专门负责显示或隐藏列表框组件,该函数的声明为
如果参数bShowIt的值为TRUE,那么将显示列表框组件,否则,就隐藏之.该函数对简易式组合框没有作用.
6.1.8 测试传统控件的一个例子 现在让我们编写一个程序来测试一下上面介绍的一些传统控件.该程序名为CtrlTest,其界面如图6.1所示.前面介绍的程序都是基于框架窗口的,而CtrlTest程序是一个基于对话框的应用程序,即以对话框作为程序的主窗口.该程序主要对组合框、列表框、多行编辑框和滚动条控件进行了测试,其中: Input组合框是一个记事列表框.在编辑框组件中输入字符串,或从列表框组件中选择以前输入过的字符串,然后按Add按钮,该字符串就会被加入到List列表框中. List列表框是一个多重选择列表框.该列表框具有LBS_EXTENDEDSEL风格,用户可以单击鼠标进行单项选择,也可以按住Shift或Ctrl键后单击鼠标来进行多重选择.用户可以按Delete键删除列表框中选择的项. History of SELCHANGE多行编辑框.该编辑框用于跟踪Input组合框的列表框组件发出的CBN_SELCHANGE通知消息,编辑框对该消息的响应是显示XXXX selected,以表明用户新选择了一个列表项.读者通过该编辑框可以了解组合框是在什么情况下发送CBN_SELCHANGE通知消息的.按Clear按钮将清除编辑框. 水平滚动条控件的滚动范围是0 — 50.在滚动条的左边有一个静态正文控件用来动态反映当前滚动框的位置. 图6.1 CtrlTest程序
首先,让我们用AppWizard建立一个基于对话框的MFC应用程序.这一过程很简单,先将新建的工程命名为CtrlTest,然后在MFC AppWizard对话框的第一步中选择Dialog based就行了. AppWizard会自动建立一个用于应用程序主窗口的对话框模板IDD_CTRLTEST_DIALOG及其对应的对话框类CCtrlTestDlg.对该对话框的使用与普通对话框并没有什么不同,只不过在程序启动后对话框会自动显示出来,而当用户关闭对话框后,应用程序也就终止了.如果读者观察CCtrlTestApp:: InitInstance函数就会发现,该函数调用DoModal来显示一个CCtrlTestDlg对话框,并使m_pMainWnd指针指向CCtrlTestDlg对象,从而使该对话框成为程序的主窗口. 接下来,需要设计IDD_CTRLTEST_DIALOG对话框模板.请读者将该模板上除OK按钮以外的控件都删除掉,将OK按钮的标题改为Cl&ose,并去掉该按钮的Default button(缺省按钮)属性.当用户在对话框内按回车键时,会激活缺省按钮,一般应该把用来确认用户输入操作的按钮设计成缺省按钮.在本例中,显然应该把Add按钮设计成缺省按钮,而不是Close按钮.这样,用户在Input组合框中输入字符串后,按回车键就可以将该串加入到List列表框中. 请读者根据图6.1和表6.18,向IDD_CTRLTEST_DIALOG对话框模板中加入测试用的控件.
表6.18
接着,利用ClassWizard为CCtrlTestDlg类加入成员变量,如表6.19所示,这些成员变量都是控件对象.
表6.19 CCtrlTestDlg类的成员变量
接下来,用ClassWizard为CCtrlTestDlg类加入控件通知消息处理函数,如表6.20所示. 表6.20 CCtrlTestDlg的控件通知消息处理函数
最后,请读者按清单6.1修改源代码,限于篇幅,这里仅列出需要手工修改的那一部分. 清单6.1 CCtrlTestDlg类的部分源代码 // CtrlTestDlg.cpp : implementation file //
#define MAX_HISTORY 5 . . . . . . BOOL CCtrlTestDlg::OnInitDialog() {
. . . . . . // TODO: Add extra initialization here
m_ScrollBar.SetScrollRange(0,50); m_Indicator.SetWindowText("0"); m_ComboBox.SetFocus(); //使组合框获得输入焦点
return FALSE; // 返回FALSE以表明为某一控件设置了输入焦点 }
void CCtrlTestDlg::OnAdd() { // TODO: Add your control notification handler code here
int i; CString str; m_ComboBox.GetWindowText(str); m_ListBox.AddString(str); i=m_ComboBox.FindString(-1,str); if(i>=0) { m_ComboBox.DeleteString(i); m_ComboBox.InsertString(0,str); //将匹配项移到0位置 } else { m_ComboBox.InsertString(0,str); if(m_ComboBox.GetCount()>MAX_HISTORY) m_ComboBox.DeleteString(m_ComboBox.GetCount()-1); //删除旧的项 } }
void CCtrlTestDlg::OnClear() { // TODO: Add your control notification handler code here
m_MultiEdit.SetSel(0,-1); m_MultiEdit.ReplaceSel(""); }
void CCtrlTestDlg::OnDelete() { // TODO: Add your control notification handler code here
int i,count; int *pBuffer; count=m_ListBox.GetSelCount(); if(count<=0)return; pBuffer=new int[count]; m_ListBox.GetSelItems(count,pBuffer); for(i=count-1;i>=0;i--) //倒序删除选择项 m_ListBox.DeleteString(pBuffer[i]); delete pBuffer; }
void CCtrlTestDlg::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) { // TODO: Add your message handler code here and/or call default
int nScrollMin,nScrollMax,nScrollPos; int nPageSize; CString str; if(&m_ScrollBar!=pScrollBar)return; nScrollPos=m_ScrollBar.GetScrollPos(); m_ScrollBar.GetScrollRange(&nScrollMin,&nScrollMax); nPageSize=(nScrollMax-nScrollMin)/10; //指定页长 switch(nSBCode) { case SB_LEFT: nScrollPos=nScrollMin; break; case SB_RIGHT: nScrollPos=nScrollMax; break; case SB_LINELEFT: nScrollPos-=1; break; case SB_LINERIGHT: nScrollPos+=1; break; case SB_PAGELEFT: nScrollPos-=nPageSize; break; case SB_PAGERIGHT: nScrollPos+=nPageSize; break; case SB_THUMBPOSITION: nScrollPos=nPos; //由参数nPos获取滚动框的位置 break; case SB_THUMBTRACK: nScrollPos=nPos; //由参数nPos获取滚动框的位置 break; default:; } if(nScrollPos<nScrollMin)nScrollPos=nScrollMin; if(nScrollPos>nScrollMax)nScrollPos=nScrollMax; if(nScrollPos!=m_ScrollBar.GetScrollPos()) m_ScrollBar.SetScrollPos(nScrollPos); //设置滚动框的新位置 str.Format("%d",nScrollPos); m_Indicator.SetWindowText(str); //更新静态正文
CDialog::OnHScroll(nSBCode, nPos, pScrollBar); }
void CCtrlTestDlg::OnSelchangeCombobox() { // TODO: Add your control notification handler code here
int length=m_MultiEdit.GetWindowTextLength(); CString str; m_MultiEdit.SetSel(-1,0); m_MultiEdit.SetSel(length,length); //移动插入符到编辑正文的末尾 m_ComboBox.GetLBText(m_ComboBox.GetCurSel(),str); str+=" selected\r\n"; m_MultiEdit.ReplaceSel(str); } 在OnInitDialog成员函数中,对一些控件进行了初始化,包括设置滚动条的范围,将静态正文的显示置为 “0”,以及使组合框获得输入焦点.注意,缺省时,OnInitDialog返回TRUE,而新版的函数返回了FALSE.如果OnInitDialog返回TRUE,那么Windows将使tab顺序最靠前的可输入控件获得输入焦点,如果返回FALSE,则表明在OnInitDialog函数中人为地使某个控件获得输入焦点,函数返回后系统就不会再设置输入焦点了.有时,只要合理的安排了控件的tab顺序,就不必在OnInitDialog中人为设置输入焦点. 当用户点击Add按钮或按回车键后,成员函数OnAdd被调用.该函数将组合框的编辑框中的字符串加入到List列表框的末尾,并将该字符串存入到记事列表框中.这时函数会判断,如果在记事列表中没有匹配的项,则把字符串插入0位置,并在必要时删除最老的列表项,在本例中,记事列表框最多可以容纳5项;如果在记事列表中有匹配的项,那么就把该项移到0位置. 当用户点击Delete按钮时,成员函数OnDelete被调用,该函数根据CComboBox::GetSelCount获得选择项的数目,并根据这个数目动态创建一个整型数组以存放选择项的索引.然后,调用CComboBox::GetSelItems来获取选择项的索引.最后,把这些选择项删除.注意,这里是倒序删除的,如果按顺序删除,则会使选择项的索引产生错位. 成员函数OnSelchangeCombobox是Input组合框的CBN_SELCHANGE消息的处理函数.该函数先把多行编辑框的插入符移到编辑正文的末尾,然后从插入符处加入一行形如"XXXX selected"的字符串,以表明用户从记事列表框中新选择了哪个列表项.值得一提的是,上一章的Register程序是用SetWindowText来在编辑正文中插入新的正文的,此方法有一个缺点,就是不能把插入符滚动到新修改过的地方.在本例中,插入编辑正文的方法是先调用CEdit::SetSel移动插入符到指定位置(必要时要滚动以使该位置可见),然后再调用CEdit::ReplaceSel插入新的正文,这样做的好处是编辑框总是滚动到新修改过的地方,从而使得新修改过的地方总是可见的. 对滚动条控件的测试是在OnHScroll成员函数中完成的.该函数是对话框也即父窗口对水平滚动条控件产生的WM_HSCROLL消息的处理函数.该函数负责移动滚动框并及时更新静态正文的显示以反映滚动框的当前位置.在函数的开头,首先判断是不是m_ScrollBar滚动条发来的消息,这是因为可能会有几个滚动条控件.在该函数中有一个大的switch分枝语句,用来获取滚动框的新位置.需要指出的是,对于SB_THUMBPOSITION和SB_THUMBTRACK这两种情况,应该从OnHScroll函数的nPos参数中获取滚动框的新位置.对于SB_THUMBTRACK,不要企图用CScrollBar::GetScrollPos来获取滚动框的新位置,因为该函数不能正确返回拖动时的滚动框位置.另一个要注意的问题是Windows本身不会自动地使滚动框移动到新位置上,所以需要在OnHScroll中调用CScrollBar::SetScrollPos来移动滚动框. |