上一页 下一页 返回

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消息,相应的消息映射如下所示: 

ON_CONTROL_RANGE(BN_CLICKED, IDC_FIRST, IDC_LAST, OnRadioClicked) 

 函数OnRadioClicked的声明如下,该函数比上面的OnAdd多了一个参数nID以说明发送通知消息的控件ID. 

 afx_msg void OnRadioClicked(UINT nID); 

 ClassWizard不支持ON_CONTROL_RANGE宏,所以需要手工建立消息映射和消息处理函数. 

 提示:事实上,在使用ClassWizard时只要运用一个小小的技巧,就可以把不同控件的通知消息映射到同一个处理函数上,也可以把一个控件的不同通知消息映射到同一个处理函数上.这个技巧就是在用ClassWizard创建消息处理函数时,指定相同的函数名即可.此方法的优点在于控件的ID不必是连续的,缺点是处理函数没有nID参数,因而不能确定是哪一个控件发送的消息. 

 6.1.2 静态控件 

  静态控件包括静态正文(Static Text)和图片控件(Picture)。静态正文控件用来显示正文。图片控件可以显示位图、图标、方框和图元文件,在图片控件中显示图片的好处是不必操心图片的重绘问题。静态控件不能接收用户的输入。在上一章中,读者已经用过静态正文和组框控件。图片控件的例子可以在AppWizard创建的IDD_ABOUTBOX对话框模板中找到,在该模板中有一个图片控件用来显示图标。 

  静态控件的主要起说明和装饰作用。MFC的CStatic类封装了静态控件。CStatic类的成员函数Create负责创建静态控件,该函数的声明为 

 

 BOOL Create( LPCTSTR lpszText, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID = 0xffff ); 

  参数lpszText指定了控件显示的正文。dwStyle指定了静态控件的风格,表6.1显示了静态控件的各种风格,dwStyle可将这些风格组合起来。rect是一个对RECT或CRect结构的引用,用来说明控件的位置和尺寸。pParentWnd指向父窗口,该参数不能为NULL。nID则说明了控件的ID。如果创建成功,该函数返回TRUE,否则返回FALSE. 

 表6.1 静态控件的风格 

 控件风格 

 含义 

 SS_BLACKFRAME 

 指定一个具有与窗口边界同色的框(缺省为黑色)。 

 SS_BLACKRECT 

 指定一个具有与窗口边界同色的实矩形(缺省为黑色)。 

 SS_CENTER 

 使显示的正文居中对齐,正文可以回绕。 

 SS_GRAYFRAME 

 指定一个具有与屏幕背景同色的边框。 

 SS_GRAYRECT 

 指定一个具有与屏幕背景同色的实矩形。 

 SS_ICON 

 使控件显示一个在资源中定义的图标,图标的名字有Create函数的lpszText参数指定。 

 SS_LEFT 

 左对齐正文,正文能回绕。 

 SS_LEFTNOWORDWRAP 

 左对齐正文,正文不能回绕。 

 SS_NOPREFIX 

 使静态正文串中的&不是一个热键提示符。 

 SS_NOTIFY 

 使控件能向父窗口发送鼠标事件消息。 

 SS_RIGHT 

 右对齐正文,可以回绕。 

 SS_SIMPLE 

 使静态正文在运行时不能被改变并使正文显示在单行中。 

 SS_USERITEM 

 指定一个用户定义项。 

 SS_WHITEFRAME 

 指定一个具有与窗口背景同色的框(缺省为白色)。 

 SS_WHITERECT 

 指定一个具有与窗口背景同色的实心矩形(缺省为白色)。 

  除了上表中的风格外,一般还要为控件指定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类的主要成员函数 

 函数声明 

 用途 

 HBITMAP SetBitmap( HBITMAP hBitmap ); 

 指定要显示的位图。 

 HBITMAP GetBitmap( ) const; 

 获取由SetBitmap指定的位图。 

 HICON SetIcon( HICON hIcon ); 

 指定要显示的图标。 

 HICON GetIcon( ) const; 

 获取由SetIcon指定的图标。 

 HCURSOR SetCursor( HCURSOR hCursor ); 

 指定要显示的光标图片。 

 HCURSOR GetCursor( ); 

 获取由SetCursor指定的光标。 

 HENHMETAFILE SetEnhMetaFile( HENHMETAFILE hMetaFile ); 

 指定要显示的增强图元文件。 

 HENHMETAFILE GetEnhMetaFile( ) const; 

 获取由SetEnhMetaFile指定的图元文件。 

  静态控件较简单,故这里就不举例说明了。 

 6.1.3 按钮控件 

  按钮是指可以响应鼠标点击的小矩形子窗口。按钮控件包括命令按钮(Pushbutton)、检查框(Check Box)、单选按钮(Radio Button)、组框(Group Box)和自绘式按钮(Owner-draw Button)。命令按钮的作用是对用户的鼠标单击作出反应并触发相应的事件,在按钮中既可以显示正文,也可以显示位图。选择框控件可作为一种选择标记,可以有选中、不选中和不确定三种状态。单选按钮控件一般都是成组出现的,具有互斥的性质,即同组单选按钮中只能有一个是被选中的。组框用来将相关的一些控件聚成一组.自绘式按钮是指由程序而不是系统负责重绘的按钮。 

  按钮主要是指命令按钮、选择框和单选按钮。后二者实际上是一种特殊的按钮,它们有选择和未选择状态。当一个选择框处于选择状态时,在小方框内会出现一个“√”,当单选按钮处于选择状态时,会在圆圈中显示一个黑色实心圆。此外,检查框还有一种不确定状态,这时检查框呈灰色显示,不能接受用户的输入,以表明控件是无效的或无意义的。 

  按钮控件会向父窗口发出如表6.3所示的控件通知消息。 

 表6.3 按钮控件的通知消息 

 消息 

 含义 

 BN_CLICKED 

 用户在按钮上单击了鼠标。 

 BN_DOUBLECLICKED 

 用户在按钮上双击了鼠标。 

 FC的CButton类封装了按钮控件。CButton类的成员函数Create负责创建按钮控件,该函数的声明为 

 

 BOOL Create( LPCTSTR lpszCaption, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID ); 

  参数lpszCaption指定了按钮显示的正文。dwStyle指定了按钮的风格,如表6.4所示,dwStyle可以是这些风格的组合。rect说明了按钮的位置和尺寸。pParentWnd指向父窗口,该参数不能为NULL。nID是按钮的ID。如果创建成功,该函数返回TRUE,否则返回FALSE. 

   

 表6.4 按钮的风格 

 控件风格 

 含义 

 BS_AUTOCHECKBOX 

 同BS_CHECKBOX,不过单击鼠标时按钮会自动反转。 

 BS_AUTORADIOBUTTON 

 同BS_RADIOBUTTON,不过单击鼠标时按钮会自动反转。 

 BS_AUTO3STATE 

 同BS_3STATE,不过单击按钮时会改变状态。 

 BS_CHECKBOX 

 指定在矩形按钮右侧带有标题的选择框。 

 BS_DEFPUSHBUTTON 

 指定缺省的命令按钮,这种按钮的周围有一个黑框,用户可以按回车键来快速选择该按钮。 

 BS_GROUPBOX 

 指定一个组框。 

 BS_LEFTTEXT 

 使控件的标题显示在按钮的左边。 

 BS_OWNERDRAW 

 指定一个自绘式按钮。 

 BS_PUSHBUTTON 

 指定一个命令按钮。 

 BS_RADIOBUTTON 

 指定一个单选按钮,在圆按钮的右边显示正文。 

 BS_3STATE 

 同BS_CHECKBOX,不过控件有三种状态:选择、未选择和变灰。 

 

  除了上表中的风格外,一般还要为控件指定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 );
当参数bHeightlight值为TRUE时,该函数将按钮设置为高亮度状态,否则,去除按钮的高亮度状态。 

  int GetCheck( ) const;
返回检查框或单选按钮的选择状态。返回值0表示按钮未被选择,1表示按钮被选择,2表示按钮处于不确定状态(仅用于检查框)。 

  void SetCheck( int nCheck );
设置检查框或单选按钮的选择状态。参数nCheck值的含义与GetCheck返回值相同。 

  UINT GetButtonStyle( ) const;
获得按钮控件的BS_XXXX风格。 

  void SetButtonStyle( UINT nStyle, BOOL bRedraw = TRUE );
设置按钮的风格。参数nStyle指定了按钮的风格。bRedraw为TRUE则重绘按钮,否则就不重绘。 

  HBITMAP SetBitmap( HBITMAP hBitmap );
设置按钮显示的位图。参数hBitmap指定了位图的句柄。该函数还会返回按钮原来的位图。 

  HBITMAP GetBitmap( ) const;
返回以前用SetBitmap设置的按钮位图。 

  HICON SetIcon( HICON hIcon );
设置按钮显示的图标。参数hIcon指定了图标的句柄。该函数还会返回按钮原来的图标。 

  HICON GetIcon( ) const;
返回以前用SetIcon设置的按钮图标。 

  HCURSOR SetCursor( HCURSOR hCursor );
设置按钮显示的光标图。参数hCursor指定了光标的句柄。该函数还会返回按钮原来的光标。 

  HCURSOR GetCursor( );
返回以前用GetCursor设置的光标。 

  另外,可以使用下列的一些与按钮控件有关的CWnd成员函数来设置或查询按钮的状态。用这些函数的好处在于不必构建按钮控件对象,只要知道按钮的ID,就可以直接设置或查询按钮。 

  void CheckDlgButton( int nIDButton, UINT nCheck );
用来设置按钮的选择状态。参数nIDButton指定了按钮的ID。nCheck的值0表示按钮未被选择,1表示按钮被选择,2表示按钮处于不确定状态。 

  void CheckRadioButton( int nIDFirstButton, int nIDLastButton, int nIDCheckButton );
用来选择组中的一个单选按钮。参数nIDFirstButton指定了组中第一个按钮的ID,nIDLastButton指定了组中最后一个按钮的ID,nIDCheckButton指定了要选择的按钮的ID。 

  int GetCheckedRadioButton( int nIDFirstButton, int nIDLastButton );
该函数用来获得一组单选按钮中被选中按钮的ID。参数nIDFirstButton说明了组中第一个按钮的ID,nIDLastButton说明了组中最后一个按钮的ID。 

  UINT IsDlgButtonChecked( int nIDButton ) const;
返回检查框或单选按钮的选择状态。返回值0表示按钮未被选择,1表示按钮被选择,2表示按钮处于不确定状态(仅用于检查框)。 

  可以调用CWnd成员函数GetWindowText,GetWindowTextLength和SetWindowText来查询或设置按钮中显示的正文. 

  MFC还提供了CButton的派生类CBitmapButton。利用该类可以创建一个拥有四幅位图的命令按钮,按钮在不同状态时会显示不同的位图,这样可以使界面显得生动活泼。如果读者对CBitmapButton感兴趣,可以参看VC5.0随盘提供的MFC例子CTRLTEST。 

  在上一章的Register例子中已演示了各种按钮控件的使用,故这里就不再举例了。 

   

 6.1.4 编辑框控件 

  编辑框(Edit Box)控件实际上是一个简易的正文编辑器,用户可以在编辑框中输入并编辑正文。编辑框既可以是单行的,也可以是多行的,多行编辑框是从零开始编行号的.在一个多行编辑框中,除了最后一行外,每一行的结尾处都有一对回车换行符(用"\r\n"表示).这对回车换行符是正文换行的标志,在屏幕上是不可见的. 

  编辑框控件会向父窗口发出如表6.5所示的控件通知消息。 

   表6.5 

 消息 

 含义 

 EN_CHANGE 

 编辑框的内容被用户改变了。与EN_UPDATE不同,该消息是在编辑框显示的正文被刷新后才发出的。 

 EN_ERRSPACE 

 编辑框控件无法申请足够的动态内存来满足需要。 

 EN_HSCROLL 

 用户在水平滚动条上单击鼠标。 

 EN_KILLFOCUS 

 编辑框失去输入焦点。 

 EN_MAXTEXT 

 输入的字符超过了规定的最大字符数。在没有ES_AUTOHSCROLL或ES_AUTOVSCROLL的编辑框中,当正文超出了编辑框的边框时也会发出该消息。 

 EN_SETFOCUS 

 编辑框获得输入焦点。 

 EN_UPDATE 

 在编辑框准备显示改变了的正文时发送该消息。 

 EN_VSCROLL 

 用户在垂直滚动条上单击鼠标。 

 

   

  MFC的CEdit类封装了编辑框控件。CEdit类的成员函数Create负责创建按钮控件,该函数的声明为 

 

 BOOL Create( DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID ); 

  参数dwStyle指定了编辑框控件风格,如表6.6所示,dwStyle可以是这些风格的组合。rect指定了编辑框的位置和尺寸。pParentWnd指定了父窗口,不能为NULL。编辑框的ID由nID指定。如果创建成功,该函数返回TRUE,否则返回FALSE. 

   表6.6 编辑框控件的风格 

 控件风格 

 含义 

 ES_AUTOHSCROLL 

 当用户在行尾键入一个字符时,正文将自动向右滚动10个字符,当用户按回车键时,正文总是滚向左边。 

 ES_AUTOVSCROLL 

 当用户在最后一个可见行按回车键时,正文向上滚动一页。 

 ES_CENTER 

 在多行编辑框中使正文居中。 

 ES_LEFT 

 左对齐正文。 

 ES_LOWERCASE 

 把用户输入的字母统统转换成小写字母。 

 ES_MULTILINE 

 指定一个多行编辑器。若多行编辑器不指定ES_AUTOHSCROLL风格,则会自动换行,若不指定ES_AUTOVSCROLL,则多行编辑器会在窗口中正文装满时发出警告声响。 

 ES_NOHIDESEL 

 缺省时,当编辑框失去输入焦点后会隐藏所选的正文,当获得输入焦点时又显示出来。设置该风格可禁止这种缺省行为。 

 ES_OEMCONVERT 

 使编辑框中的正文可以在ANSI字符集和OEM字符集之间相互转换。这在编辑框中包含文件名时是很有用的。 

 ES_PASSWORD 

 使所有键入的字符都用“*”来显示。 

 ES_RIGHT 

 右对齐正文。 

 ES_UPPERCASE 

 把用户输入的字母统统转换成大写字母。 

 ES_READONLY 

 将编辑框设置成只读的。 

 ES_WANTRETURN 

 使多行编辑器接收回车键输入并换行。如果不指定该风格,按回车键会选择缺省的命令按钮,这往往会导致对话框的关闭。 

 

  除了上表中的风格外,一般还要为控件指定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成员函数 

 函数声明 

 用途 

 void Clear( ) 

 清除编辑框中被选择的正文。 

 void Copy( ) 

 把在编辑框中选择的正文拷贝到剪贴板中。 

 void Cut( ) 

 清除编辑框中被选择的正文并把这些正文拷贝到剪贴板中。 

 void Paste( ) 

 将剪贴板中的正文插入到编辑框的当前插入符处。 

 BOOL Undo( ) 

 撤消上一次键入。对于单行编辑框,该函数总返回TRUE,对于多行编辑框,返回TRUE表明操作成功,否则返回FALSE。 

  可以用下列CEdit或CWnd类的成员函数来查询编辑框。在学习下面的函数时,读者会经常遇到术语字符索引.字符的字符索引是指从编辑框的开头字符开始的字符编号,它是从零开始编号的.也就是说,字符索引实际上是指当把整个编辑正文看作一个字符串数组时,该字符所在的数组元素的下标. 

   

  int GetWindowText( LPTSTR lpszStringBuf, int nMaxCount ) const;
void GetWindowText( CString& rString ) const;
这两个函数均是CWnd类的成员函数,可用来获得窗口的标题或控件中的正文。第一个版本的函数用lpszStringBuf参数指向的字符串数组作为拷贝正文的缓冲区,参数nMaxCount可以拷贝到缓冲区中的最大字符数,该函数返回以字节为单位的实际拷贝字符数(不包括结尾的空字节)。第二个版本的函数用一个CString对象作为缓冲区。 

  int GetWindowTextLength( ) const;
CWnd的成员函数,可用来获得窗口的标题或控件中的正文的长度。 

  DWORD GetSel( ) const;
void GetSel( int& nStartChar, int& nEndChar ) const;
两个函数都是CEdit的成员函数,用来获得所选正文的位置。GetSel的第一个版本返回一个DWORD值,其中低位字说明了被选择的正文开始处的字符索引,高位字说明了选择的正文结束处的后面一个字符的字符索引,如果没有正文被选择,那么返回的低位和高位字节都是当前插入符所在字符的字符索引。GetSel的第二个版本的两个参数是两个引用,其含义与第一个版本函数返回值的低位和高位字相同。 

  int LineFromChar( int nIndex = –1 ) const;
CEdit的成员函数,仅用于多行编辑框,用来返回指定字符索引所在行的行索引(从零开始编号)。参数nIndex指定了一个字符索引,如果nIndex是-1,那么函数将返回选择正文的第一个字符所在行的行号,若没有正文被选择,则该函数会返回当前的插入符所在行的行号。 

  int LineIndex( int nLine = –1 ) const;
CEdit的成员函数,仅用于多行编辑框,用来获得指定行的开头字符的字符索引,如果指定行超过了编辑框中的最大行数,该函数将返回-1。参数nLine是指定了从零开始的行索引,如果它的值为-1,则函数返回当前的插入符所在行的字符索引。 

  int GetLineCount( ) const;
CEdit的成员函数,仅用于多行编辑框,用来获得正文的行数。如果编辑框是空的,那么该函数的返回值是1。 

  int LineLength( int nLine = –1 ) const;
CEdit的成员函数,用于获取指定字符索引所在行的字节长度(行尾的回车和换行符不计算在内)。参数nLine说明了字符索引.如果nLine的值为-1,则函数返回当前行的长度(假如没有正文被选择),或选择正文占据的行的字符总数减去选择正文的字符数(假如有正文被选择)。若用于单行编辑框,则函数返回整个正文的长度。 

  int GetLine( int nIndex, LPTSTR lpszBuffer ) const;
int GetLine( int nIndex, LPTSTR lpszBuffer, int nMaxLength ) const;
CEdit的成员函数,仅用于多行编辑框,用来获得指定行的正文(不包括行尾的回车和换行符)。参数nIndex是行号,lpszBuffer指向存放正文的缓冲区,nMaxLength规定了拷贝的最大字节数,若。函数返回实际拷贝的字节数,若指定的行号大于编辑框的实际行数,则函数返回0。需要注意的是,GetLine函数不会在缓冲区中字符串的末尾加字符串结束符(NULL). 

  下列CWnd或CEdit类的成员函数可用来修改编辑框控件。 

  void SetWindowText( LPCTSTR lpszString );
CWnd的成员函数,可用来设置窗口的标题或控件中的正文。参数lpszString可以是一个CString对象,或是一个指向字符串的指针。 

  void SetSel( DWORD dwSelection, BOOL bNoScroll = FALSE );
void SetSel( int nStartChar, int nEndChar, BOOL bNoScroll = FALSE );
CEdit的成员函数,用来选择编辑框中的正文。参数dwSelection的低位字说明了选择开始处的字符索引,高位字说明了选择结束处的字符索引。如果低位字为0且高位字节为-1,那么就选择所有的正文,如果低位字节为-1,则取消所有的选择.参数bNoScroll的值如果是FALSE,则滚动插入符并使之可见,否则就不滚动.参数nStartChar和nEndChar的含义与参数dwSelection的低位字和高位字相同. 

  void ReplaceSel( LPCTSTR lpszNewText, BOOL bCanUndo = FALSE );
CEdit的成员函数,用来将所选正文替换成指定的正文.参数lpszNewText指向用来替换的字符串.参数bCanUndo的值为TRUE说明替换是否可以被撤消的. 

  在调用上述函数时,如果涉及的是一个多行编辑框,那么除了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来在字符索引和字符的行列坐标之间相互转换.下列代码演示了在已知字符索引的情况下,如何获得对应的行列坐标:
int row,column;
row=MyEdit.LineFromChar(charIndex);
column=charIndex-MyEdit.LineIndex(row);
  下列代码演示了在已知字符的行列坐标的情况下,如何获得对应的字符索引:
int charIndex;
charIndex=MyEdit.LineIndex(row)+column;
  不难看出字符索引与对应的行列坐标的关系是:字符索引=LineIndex(行坐标)+列坐标. 

  对于选择正文的查询和设置,应该利用函数GetSel、SetSel和ReplaceSel. 

  可以利用GetSel和SetSel来查询和设置插入符的位置.SetSel可以使编辑框滚动到插入符的新位置. 要获取插入符的行列坐标,可用下面的代码实现:

MyEdit.SetSel(-1,0); //取消正文的选择
int start,end,row,column;
MyEdit.GetSel(start,end); //start或end的值就是插入符的字符索引
row=MyEdit.LineFromChar(start); //获取插入符的行坐标
column=start-MyEdit.LineIndex(row); //获取插入符的列坐标
下面的代码演示了如何把插入符移到指定的行和列:
MyEdit.SetSel(-1,0); //取消正文的选择
int charIndex=MyEdit.LineIndex(row)+column;
MyEdit.SetSel(charIndex,charIndex); 

  可以利用ReplaceSel函数在 插入符处插入正文,典型的代码如下所示:
MyEdit.SetSel(-1,0); //取消正文的选择
MyEdit.ReplaceSel(“......”); 

  可以利用ReplaceSel清除编辑框中的正文,典型的代码如下所示:
MyEdit.SetSel(0,-1); //选择全部正文
MyEdit.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;
该函数返回滚动框的当前位置.若操作失败则返回0. 

  int SetScrollPos( int nPos, BOOL bRedraw = TRUE );
该函数将滚动框移动到指定位置.参数nPos指定了新的位置.参数bRedraw表示是否需要重绘滚动条,如果为TRUE,则重绘之.函数返回滚动框原来的位置.若操作失败则返回0. 

  void GetScrollRange( LPINT lpMinPos, LPINT lpMaxPos ) const;
该函数对滚动条的滚动范围进行查询.参数lpMinPos和lpMaxPos分别指向滚动范围的最小最大值. 

  void SetScrollRange( int nMinPos, int nMaxPos, BOOL bRedraw = TRUE );
该函数用于指定滚动条的滚动范围.参数nMinPos和nMaxPos分别指定了滚动范围的最小最大值.由这两者指定的滚动范围不得超过32767.当两者都为0时,滚动条将被隐藏.参数bRedraw表示是否需要重绘滚动条,如果为TRUE,则重绘之. 

  BOOL GetScrollInfo( LPSCROLLINFO lpScrollInfo, UINT nMask );
该函数用来获取滚动条的各种状态,包括滚动范围、滚动框的位置和页尺寸.参数lpScrollInfo指向一个SCROLLINFO结构,该结构如下所示:
typedef struct tagSCROLLINFO {
UINT cbSize; //结构的尺寸(字节为单位)
UINT fMask; /*说明结构中的哪些参数是有效的,可以是屏蔽值的组合, 如SIF_POS|SIF_PAGE,若为SIF_ALL则整个结构都有效*/
int nMin; //滚动范围最大值,当fMask中包含SIF_RANGE时有效
int nMax; //滚动范围最小值,当fMask中包含SIF_RANGE时有效
UINT nPage; /*页尺寸,用来确定比例滚动框的大小,当fMask中包含 SIF_PAGE时有效*/
int nPos; //滚动框的位置,当fMask中包含SIF_POS有效
int nTrackPos; /*拖动时滚动框的位置,当fMask中包含 SIF_TRACKPOS时有效,该参数只能查询,不能设 置,最好不要用该参数来查询拖动时滚动框的位置*/
} SCROLLINFO;
typedef SCROLLINFO FAR *LPSCROLLINFO;
参数nMask的意义与SCROLLINFO结构中的fMask相同.函数在获得有效值后返回TRUE,否则返回FALSE. 

  BOOL SetScrollInfo( LPSCROLLINFO lpScrollInfo, BOOL bRedraw = TRUE );
该函数用于设置滚动条的各种状态,一个重要用途是设定页尺寸从而实现比例滚动框.参数lpScrollInfo指向一个SCROLLINFO结构,参数bRedraw表示是否需要重绘滚动条,如果为TRUE,则重绘之.若操作成功,该函数返回TRUE,否则返回FALSE. 

 

  CWnd类也提供了一些函数来查询和设置所属的标准滚动条.这些函数与CScrollBar类的函数同名,且功能相同,但每个函数都多了一个参数,用来选择滚动条.例如,CWnd:: GetScrollPos 的声明为 

 

  int GetScrollPos( int nBar ) const;
参数nBar用来选择滚动条,可以为下列值:
SB_HORZ //指定水平滚动条
SB_VERT //指定垂直滚动条 

 

  无论是标准滚动条,还是滚动条控件,滚动条的通知消息都是用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 );
参数nSBCode是通知消息码,如表6.8所示.nPos是滚动框的位置,只有在nSBCode为SB_THUMBPOSITION或SB_THUMBTRACK时,该参数才有意义.如果通知消息是滚动条控件发来的,那么pScrollBar是指向该控件的指针,如果是标准滚动条发来的,则pScrollBar为NULL. 

   

 表6.8 滚动条的通知消息码 

 消息 

 含义 

 SB_BOTTOM / SB_RIGHT(二者的消息码是一样的,因此可以混用,下同) 

 滚动到底端(右端). 

 SB_TOP / SB_LEFT 

 滚动到顶端(左端). 

 SB_LINEDOWN / SB_LINERIGHT 

 向下(向右)滚动一行(列). 

 SB_LINEUP / SB_LINELEFT 

 向上(向左)滚动一行(列). 

 SB_PAGEDOWN / SB_PAGERIGHT 

 向下(向右)滚动一页. 

 SB_PAGEUP / SB_PAGELEFT 

 向上(向左)滚动一页. 

 SB_THUMBPOSITION 

 滚动到指定位置. 

 SB_THUMBTRACK 

 滚动框被拖动.可利用该消息来跟踪对滚动框的拖动. 

 SB_ENDSCROLL 

 滚动结束. 

  6.1.8小节的例子中,读者将学会如何使用滚动条以及如何编写自己的OnHScroll函数. 

 6.1.6 列表框控件 

  列表框主要用于输入,它允许用户从所列出的表项中进行单项或多项选择,被选择的项呈高亮度显示.列表框具有边框,并且一般带有一个垂直滚动条.列表框分单选列表框和多重选择列表框两种.单选列表框一次只能选择一个列表项,而多重选择列表框可以进行多重选择.对于列表项的选择,微软公司有如下建议: 

 

  单击鼠标选择一个列表项,单击一个按钮来处理选择的项. 

  双击鼠标选择一个列表项是处理选择项的快捷方法. 

 

  列表框会向父窗口发送如表6.9所示的通知消息. 

   

 表6.9 列表框控件的通知消息 

 消息 

 含义 

 LBN_DBLCLK 

 用户用鼠标双击了一列表项.只有具有LBS_NOTIFY的列表框才能发送该消息. 

 LBN_ERRSPACE 

 列表框不能申请足够的动态内存来满足需要. 

 LBN_KILLFOCUS 

 列表框失去输入焦点. 

 LBN_SELCANCEL 

 当前的选择被取消.只有具有LBS_NOTIFY的列表框才能发送该消息. 

 LBN_SELCHANGE 

 单击鼠标选择了一列表项.只有具有LBS_NOTIFY的列表框才能发送该消息. 

 LBN_SETFOCUS 

 列表框获得输入焦点. 

 WM_CHARTOITEM 

 当列表框收到WM_CHAR消息后,向父窗口发送该消息.只有具有LBS_WANTKEYBOARDINPUT风格的列表框才会发送该消息. 

 WM_VKEYTOITEM 

 当列表框收到WM_KEYDOWN消息后,向父窗口发送该消息.只有具有LBS_WANTKEYBOARDINPUT风格的列表框才会发送该消息. 

 

   

  MFC的CListBox类封装了列表框.CListBox类的Create成员函数负责列表框的创建,该函数的声明是 

 

 BOOL Create( DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID ); 

 

  参数dwStyle指定了列表框控件的风格,如表6.10所示,dwStyle可以是这些风格的组合.rect说明了控件的位置和尺寸.pParentWnd指向父窗口,该参数不能为NULL。nID则说明了控件的ID。如果创建成功,该函数返回TRUE,否则返回FALSE. 

   

 表6.10 列表框控件的风格 

 控件风格 

 含义 

 LBS_EXTENDEDSEL 

 支持多重选择.在点击列表项时按住Shift键或Ctrl键即可选择多个 项. 

 LBS_HASSTRINGS 

 指定一个含有字符串的自绘式列表框. 

 LBS_MULTICOLUMN 

 指定一个水平滚动的多列列表框,通过调用CListBox::SetColumnWidth来设置每列的宽度. 

 LBS_MULTIPLESEL 

 支持多重选择.列表项的选择状态随着用户对该项单击或双击鼠标而翻转. 

 LBS_NOINTEGRALHEIGHT 

 列表框的尺寸由应用程序而不是Windows指定.通常,Windows指定尺寸会使列表项的某些部分隐藏起来. 

 LBS_NOREDRAW 

 当选择发生变化时防止列表框被更新,可发送WM_SETREDRAW来改变该风格. 

 LBS_NOTIFY 

 当用户单击或双击鼠标时通知父窗口. 

 LBS_OWNERDRAWFIXED 

 指定自绘式列表框,即由父窗口负责绘制列表框的内容,并且列表项有相同的高度. 

 LBS_OWNERDRAWVARIABLE 

 指定自绘式列表框,并且列表项有不同的高度. 

 LBS_SORT 

 使插入列表框中的项按升序排列. 

 LBS_STANDARD 

 相当于指定了WS_BORDER|WS_VSCROLL|LBS_SORT |LBS_NOTIFY. 

 LBS_USETABSTOPS 

 使列表框在显示列表项时识别并扩展制表符(‘\t’),缺省的制表宽度是32个对话框单位. 

 LBS_WANTKEYBOARDINPUT 

 允许列表框的父窗口接收WM_VKEYTOITEM和WM_CHARTOITEM消息,以响应键盘输入. 

 LBS_DISABLENOSCROLL 

 使列表框在不需要滚动时显示一个禁止的垂直滚动条. 

 

   

  除了上表中的风格外,一般还要为列表框控件指定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 );
该函数用来往列表框中加入字符串,其中参数lpszItem指定了要添加的字符串.函数的返回值是加入的字符串在列表框中的位置,如果发生错误,会返回LB_ERR或LB_ERRSPACE(内存不够).如果列表框未设置LBS_SORT风格,那么字符串将被添加到列表的末尾,如果设置了LBS_SORT风格,字符串会按排序规律插入到列表中. 

  int InsertString( int nIndex, LPCTSTR lpszItem );
该函数用来在列表框中的指定位置插入字符串.参数nIndex给出了插入位置(索引),如果值为-1,则字符串将被添加到列表的末尾.参数lpszItem指定了要插入的字符串.函数返回实际的插入位置,若发生错误,会返回LB_ERR或LB_ERRSPACE.与AddString函数不同,InsertString函数不会导致LBS_SORT风格的列表框重新排序.不要在具有LBS_SORT风格的列表框中使用InsertString函数,以免破坏列表项的次序. 

  int DeleteString( UINT nIndex );
该函数用于删除指定的列表项,其中参数nIndex指定了要删除项的索引.函数的返回值为剩下的表项数目,如果nIndex超过了实际的表项总数,则返回LB_ERR. 

  void ResetContent( );
该函数用于清除所有列表项. 

  int Dir( UINT attr, LPCTSTR lpszWildCard );
该函数用来向列表项中加入所有与指定通配符相匹配的文件名或驱动器名.参数attr为文件类型的组合,如表6.11所示.参数lpszWildCard指定了通配符(如*.cpp,*.*等). 

 

   

 表6.11 Dir函数attr参数的含义 

 值 

 含义 

 0x0000 

 普通文件(可读写的文件). 

 0x0001 

 只读文件. 

 0x0002 

 隐藏文件. 

 0x0004 

 系统文件. 

 0x0010 

 目录. 

 0x0020 

 文件的归档位已被设置. 

 0x4000 

 包括了所有与通配符相匹配的驱动器. 

 0x8000 

 排除标志.若指定该标志,则只列出指定类型的文件名,否则,先要列出普通文件,然后再列出指定的文件. 

 

   

  下列的CListBox成员函数用于搜索、查询和设置列表框. 

 

  int GetCount( ) const;
该函数返回列表项的总数,若出错则返回LB_ERR. 

  int FindString( int nStartAfter, LPCTSTR lpszItem ) const;
该函数用于对列表项进行与大小写无关的搜索.参数nStartAfter指定了开始搜索的位置, 合理指定nStartAfter可以加快搜索速度,若nStartAfter为-1,则从头开始搜索整个列表.参数lpszItem指定了要搜索的字符串.函数返回与lpszItem指定的字符串相匹配的列表项的索引,若没有找到匹配项或发生了错误,函数会返回LB_ERR.FindString函数先从nStartAfter指定的位置开始搜索,若没有找到匹配项,则会从头开始搜索列表.只有找到匹配项,或对整个列表搜索完一遍后,搜索过程才会停止,所以不必担心会漏掉要搜索的列表项. 

  int GetText( int nIndex, LPTSTR lpszBuffer ) const;
void GetText( int nIndex, CString& rString ) const;
用于获取指定列表项的字符串.参数nIndex指定了列表项的索引.参数lpszBuffer指向一个接收字符串的缓冲区.引用参数rString则指定了接收字符串的CString对象.第一个版本的函数会返回获得的字符串的长度,若出错,则返回LB_ERR. 

  int GetTextLen( int nIndex ) const;
该函数返回指定列表项的字符串的字节长度.参数nIndex指定了列表项的索引.若出错则返回LB_ERR. 

  DWORD GetItemData( int nIndex ) const;
每个列表项都有一个32位的附加数据.该函数返回指定列表项的附加数据,参数nIndex指定了列表项的索引.若出错则函数返回LB_ERR. 

  int SetItemData( int nIndex, DWORD dwItemData );
该函数用来指定某一列表项的32位附加数据.参数nIndex指定了列表项的索引.dwItemData是要设置的附加数据值. 

 提示:列表项的32位附加数据可用来存储与列表项相关的数据,也可以放置指向相关数据的指针.这样,当用户选择了一个列表项时,程序可以从附加数据中快速方便地获得与列表项相关的数据. 

 

  int GetTopIndex( ) const;
该函数返回列表框中第一个可见项的索引,若出错则返回LB_ERR. 

  int SetTopIndex( int nIndex );
用来将指定的列表项设置为列表框的第一个可见项,该函数会将列表框滚动到合适的位置.参数nIndex指定了列表项的索引.若操作成功,函数返回0值,否则返回LB_ERR. 

 提示:由于列表项的内容一般是不变的,故CListBox未提供更新列表项字符串的函数.如果要改变某列表项的内容,可以先调用DeleteString删除该项,然后再用InsertString或AddString将更新后的内容插入到原来的位置. 

  下列CListBox的成员函数与列表项的选择有关. 

 

  int GetSel( int nIndex ) const;
该函数返回指定列表项的状态.参数nIndex指定了列表项的索引.如果查询的列表项被选择了,函数返回一个正值,否则返回0,若出错则返回LB_ERR. 

  int GetCurSel( ) const;
该函数仅适用于单选择列表框,用来返回当前被选择项的索引,如果没有列表项被选择或有错误发生,则函数返回LB_ERR. 

  int SetCurSel( int nSelect );
该函数仅适用于单选择列表框,用来选择指定的列表项.该函数会滚动列表框以使选择项可见.参数nIndex指定了列表项的索引,若为-1,那么将清除列表框中的选择.若出错函数返回LB_ERR. 

  int SelectString( int nStartAfter, LPCTSTR lpszItem );
该函数仅适用于单选择列表框,用来选择与指定字符串相匹配的列表项.该函数会滚动列表框以使选择项可见.参数的意义及搜索的方法与函数FindString类似.如果找到了匹配的项,函数返回该项的索引,如果没有匹配的项,函数返回LB_ERR并且当前的选择不被改变. 

  int GetSelCount( ) const;
该函数仅用于多重选择列表框,它返回选择项的数目,若出错函数返回LB_ERR. 

  int SetSel( int nIndex, BOOL bSelect = TRUE );
该函数仅适用于多重选择列表框,它使指定的列表项选中或落选.参数nIndex指定了列表项的索引,若为-1,则相当于指定了所有的项.参数bSelect为TRUE时选中列表项,否则使之落选.若出错则返回LB_ERR. 

  int GetSelItems( int nMaxItems, LPINT rgIndex ) const;
该函数仅用于多重选择列表框,用来获得选中的项的数目及位置.参数nMaxItems说明了参数rgIndex指向的数组的大小.参数rgIndex指向一个缓冲区,该数组是一个整型数组,用来存放选中的列表项的索引.函数返回放在缓冲区中的选择项的实际数目,若出错函数返回LB_ERR. 

  int SelItemRange( BOOL bSelect, int nFirstItem, int nLastItem );
该函数仅用于多重选择列表框,用来使指定范围内的列表项选中或落选.参数nFirstItem和nLastItem指定了列表项索引的范围.如果参数bSelect为TRUE,那么就选择这些列表项,否则就使它们落选.若出错函数返回LB_ERR. 

  在6.1.8小节的例子中,读者将会看到对列表框的测试. 

 6.1.7 组合框控件 

  组合框把一个编辑框和一个单选择列表框结合在了一起.用户既可以在编辑框中输入,也可以从列表框中选择一个列表项来完成输入.如上一章所提到的,组合框分为简易式(Simple)、下拉式(Dropdown)和下拉列表式(Drop List)三种.简易式组合框包含一个编辑框和一个总是显示的列表框。下拉式组合框同简易式组合框类似,二者的区别在于仅当单击下滚箭头后列表框才会弹出。下拉列表式组合框也有一个下拉的列表框,但它的编辑框是只读的,不能输入字符。 

  Windows中比较常用的是下拉式和下拉列表式组合框,在Developer Studio中就大量使用了这两种组合框.二者都具有占地小的特点,这在界面日益复杂的今天是十分重要的.下拉列表式组合框的功能与列表框类似.下拉式组合框的典型应用是作为记事列表框使用,既把用户在编辑框中敲入的东西存储到列表框组件中,这样当用户要重复同样的输入时,可以从列表框组件中选取而不必在编辑框组件中从新输入.在Developer Studio中的Find对话框中就可以找到一个典型的下拉式组合框. 

  要设计一个记事列表框,应采取下列原则: 

 

  在创建组合框时指定CBS_DROPDOWNLIST风格. 

  要限制列表项的数目,以防止内存不够. 

  如果在编辑框中输入的字符串不能与列表框组件中的列表项匹配,那么应该把该字符串插入到列表框中的0位置处.最老的项处于列表的末尾.如果列表项的数目超出了限制,则应把最老的项删除. 

  如果在编辑框中输入的字符串可以与列表框组件中的某一项完全匹配,则应该先把该项从列表的当前位置删除,然后在将其插入道列表的0位置处. 

 

  组合框控件会向父窗口发送表6.12所示的通知消息. 

   

 表6.12 组合框控件的通知消息 

 消息 

 含义 

 CBN_CLOSEUP 

 组合框的列表框组件被关闭.简易式组合框不会发出该消息. 

 CBN_DBLCLK 

 用户在某列表项上双击鼠标.只有简易式组合框才会发出该消息. 

 CBN_DROPDOWN 

 组合框的列表框组件下拉.简易式组合框不会发出该消息. 

 CBN_EDITCHANGE 

 编辑框的内容被用户改变了。与CBN_EDITUPDATE不同,该消息是在编辑框显示的正文被刷新后才发出的。下拉列表式组合框不会发出该消息. 

 CBN_EDITUPDATE 

 在编辑框准备显示改变了的正文时发送该消息。下拉列表式组合框不会发出该消息. 

 CBN_ERRSPACE 

 组合框无法申请足够的内存来容纳列表项. 

 CBN_SELENDCANCEL 

 表明用户的选择应该取消.当用户在列表框中选择了一项,然后又在组合框控件外单击鼠标时就会导致该消息的发送. 

 CBN_SELENDOK 

 用户选择了一项,然后按了回车键或单击了下滚箭头.该消息表明用户确认了自己所作的选择. 

 CBN_KILLFOCUS 

 组合框失去了输入焦点. 

 CBN_SELCHANGE 

 用户通过点击或移动箭头键改变了列表的选择. 

 CBN_SETFOCUS 

 组合框获得了输入焦点. 

 

   

  MFC的CComboBox类封装了组合框.需要指出的是,虽然组合框是编辑框和列表框的选择,但是CComboBox类并不是CEdit类和CListBox类的派生类,而是CWnd类的派生类. 

 CComboBox的成员函数Create负责创建组合框,该函数的说明如下: 

 

 BOOL Create( DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID ); 

 

  参数dwStyle指定了组合框控件的风格,如表6.10所示,dwStyle可以是这些风格的组合.rect说明的是列表框组件下拉后组合框的位置和尺寸.pParentWnd指向父窗口,该参数不能为NULL。nID则说明了控件的ID。如果创建成功,该函数返回TRUE,否则返回FALSE. 

 提示:在用Create函数创建组合框时,参数rect说明的是包括列表框组件在内的组合框的位置和尺寸,而不是列表框组件隐藏时的编辑框组件尺寸.要设置编辑框组件的高度,可以调用成员函数SetItemHeight(-1,cyItemHeight),其中参数cyItemHeight指定了编辑框的高度(以像素为单位). 

 表6.13 组合框的风格 

 控件风格 

 含义 

 CBS_AUTOHSCROLL 

 使编辑框组件具有水平滚动的风格. 

 CBS_DROPDOWN 

 指定一个下拉式组合框. 

 CBS_DROPDOWNLIST 

 指定一个下拉列表式组合框. 

 CBS_HASSTRINGS 

 指定一个含有字符串的自绘式组合框. 

 CBS_OEMCONVERT 

 使编辑框组件中的正文可以在ANSI字符集和OEM字符集之间相互转换。这在编辑框中包含文件名时是很有用的。 

 CBS_OWNERDRAWFIXED 

 指定自绘式组合框,即由父窗口负责绘制列表框的内容,并且列表项有相同的高度. 

 CBS_OWNERDRAWVARIABLE 

 指定自绘式组合框,并且列表项有不同的高度. 

 CBS_SIIMPLE 

 指定一个简易式组合框. 

 CBS_SORT 

 自动对列表框组件中的项进行排序. 

 CBS_DISABLENOSCROLL 

 使列表框在不需要滚动时显示一个禁止的垂直滚动条. 

 CBS_NOINTEGRALHEIGHT 

 组合框的尺寸由应用程序而不是Windows指定.通常,由Windows指定尺寸会使列表项的某些部分隐藏起来. 

 

   

  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成员函数 

 成员函数名 

 对应的CEdit成员函数 

 与CEdit成员函数的不同之处 

 CWnd::GetWindowText 

 CWnd::GetWindowText 

 无. 

 CWnd::SetWindowText 

 CWnd::SetWindowText 

 无. 

 CWnd::GetWindowTextLength 

 CWnd::GetWindowTextLength 

   

 GetEditSel 

 GetSel的第一个版本 

 仅函数名不同. 

 SetEditSel 

 SetSel的第二个版本 

 函数名不同,且无bNoScroll参数. 

 Clear 

 Clear 

 无. 

 Copy 

 Copy 

 无. 

 Cut 

 Cut 

 无. 

 Paste 

 Paste 

 无. 

 

   

  与CListBox的成员函数类似,针对列表框组件的CComboBox成员函数也可以分为三类.表6.15列出了用于插入和删除列表项的成员函数,表6.16列出了用于搜索、查询和设置列表框的成员函数,与列表项的选择有关的成员函数在表6.17中列出.需要指出的是,如果这些函数出错,则反回CB_ERR,而不是LB_ERR.另外,排序的组合框具有的是CBS_SORT风格,而不是LBS_SORT. 

   

   

 6.15 用于插入和删除列表项的CComboBox成员函数 

 成员函数名 

 对应的CListBox成员函数 

 与CListBox成员函数的不同之处 

 AddString 

 AddString 

 无. 

 InsertString 

 InsertString 

 无. 

 DeleteString 

 DeleteString 

 无. 

 ResetContent 

 ResetContent 

 无. 

 Dir 

 Dir 

 无. 

   

   

 6.16 用于搜索、查询和设置列表框的CComboBox成员函数 

 成员函数名 

 对应的CListBox成员函数 

 与CListBox成员函数的不同之处 

 GetCount 

 GetCount 

 无. 

 FindString 

 FindString 

 无. 

 GetLBText 

 GetText 

 仅函数名不同. 

 GetLBTextLen 

 GetTextLen 

 仅函数名不同. 

 GetItemData 

 GetItemData 

 无. 

 SetItemData 

 SetItemData 

 无. 

 GetTopIndex 

 GetTopIndex 

 无. 

 SetTopIndex 

 SetTopIndex 

 无. 

   

   

 表6.17 与列表项的选择有关的CComboBox成员函数 

 成员函数名 

 对应的CListBox成员函数 

 与CListBox成员函数的不同之处 

 GetCurSel 

 GetCurSel 

 无. 

 SetCurSel 

 SetCurSel 

 新选的列表项的内容会被拷贝到编辑框组件中. 

 SelectString 

 SelectString 

 新选的列表项的内容会被拷贝到编辑框组件中. 

   

   

  另外,CComboBox的ShowDropDown成员函数专门负责显示或隐藏列表框组件,该函数的声明为 

 void ShowDropDown( BOOL bShowIt = TRUE ); 

 

  如果参数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.在滚动条的左边有一个静态正文控件用来动态反映当前滚动框的位置. 

 T6_1.tif (122740 bytes) 

 图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 

 控件类型   ID   标题(Caption)   其它属性 

 静态正文 

 缺省 

 Input: 

 缺省. 

 组合框 

 ID_COMBOBOX 

   

 去掉Sort属性. 

 命令按钮 

 IDC_ADD 

 &Add 

 选择Default button属性. 

 静态正文 

 缺省 

 List: 

 缺省. 

 列表框 

 IDC_LISTBOX 

   

 在Selection栏中选择Extended,并去掉Sort属性. 

 命令按钮 

 IDC_DELETE 

 &Delete 

 缺省. 

 静态正文 

 缺省 

 History of SELCHANGE 

 缺省. 

 编辑框 

 IDC_MULTIEDIT 

   

 选择Multi-line,Vertical scroll,AutoVScroll和Want return属性. 

 命令按钮 

 IDC_CLEAR 

 &Clear 

 缺省. 

 静态正文 

 IDC_INDICATOR 

 缺省 

 缺省. 

 滚动条 

 IDC_SCROLLBAR 

   

 缺省. 

 

   

  接着,利用ClassWizard为CCtrlTestDlg类加入成员变量,如表6.19所示,这些成员变量都是控件对象. 

   

 表6.19 CCtrlTestDlg类的成员变量 

 控件ID 

 变量类型 

 变量名 

 IDC_COMBOBOX 

 CComboBox 

 m_ComboBox 

 IDC_LISTBOX 

 CListBox 

 m_ListBox 

 IDC_MULTIEDIT 

 CEdit 

 m_MultiEdit 

 IDC_INDICATOR 

 CStatic 

 m_Indicator 

 IDC_SCROLLBAR 

 CScrollBar 

 m_ScrollBar 

 

  接下来,用ClassWizard为CCtrlTestDlg类加入控件通知消息处理函数,如表6.20所示. 

表6.20 CCtrlTestDlg的控件通知消息处理函数 

 Object IDS 

 Messages 

 Member functions 

 IDC_ADD 

 BN_CLICKED 

 OnAdd(缺省名) 

 IDC_DELETE 

 BN_CLICKED 

 OnDelete(缺省名) 

 IDC_CLEAR 

 BN_CLICKED 

 OnClear(缺省名) 

 IDC_COMBOBOX 

 CBN_SELCHANGE 

 OnSelchangeCombobox(缺省名) 

 CCtrlTestDlg 

 WM_HSCROLL 

 OnHScroll(缺省名) 

 

  最后,请读者按清单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来移动滚动框.