注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

老旺旺的个人小站

欢迎你,亲爱的朋友!

 
 
 

日志

 
 
关于我

喜欢编点小程序,大的做不来

网易考拉推荐

win32通用控件  

2007-06-15 09:48:05|  分类: 技术文档 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

 

6.2 新的Win32控件

  从Windows 95和Windows NT 3.51版开始,Windows提供了一些先进的Win32控件.这些新控件弥补了传统控件的某些不足之处,并使Windows的界面丰富多彩且更加友好.MFC的新控件类封装了这些控件,新控件及其对应的控件类如表6.21所示.

表6.21 新的Win32控件及其控件类

控件名

功能

对应的控件类

动画(Animate)

可播放avi文件.

CAnimateCtrl

热键(Hot Key)

使用户能选择热键组合.

CHotKeyCtrl

列表视图(List View)

能够以列表、小图标、大图标或报告格式显示数据.

CListCtrl

进度条(Progress Bar)

用于指示进度.

CProgressCtrl

滑尺(Slider)

也叫轨道条(Trackbar),用户可以移动滑尺来在某一范围中进行选择.

CSliderCtrl

旋转按钮(Spin Button)

有时被称为上下控件.有一对箭头按钮,用来调节某一值的大小.

CSpinButtonCtrl

标签(Tab)

用来作为标签使用.

CTabCtrl

树形视图(Tree View)

以树状结构显示数据.

CTreeCtrl

本节将主要介绍列表视图、树形视图、进度条、旋转按钮和滑尺控件,动画控件将在第十二章介绍.

6.2.1 Win32控件的通知消息

  较之传统的Windows 3.x控件,新的Win32控件更加复杂和先进.在新控件发送通知消息的同时,往往还需要附加一些数据来描述控件的状态.传统的WM_COMMAND消息通知机制显然不能完成这一任务,因为WM_COMMAND消息的wParam和lParam已经被占满了(见6.1.1),无法容纳新的数据.

  在Win32中,采用新的WM_NOTIFY消息来实现新控件的消息通知机制.在该消息的wParam中含有控件的ID,lParam中则有一个指针,这个指针指向一个结构.这个结构要么是NMHDR结构,要么是一个以NMHDR结构作为第一个成员的扩充结构.通过NMHDR结构及其扩充结构可以传递附加数据.从理论上讲,可以通过扩充结构传送任意多的数据.需要指出的是,由于NMHDR结构是扩充结构的第一个成员,因此lParam中的指针即可以认为是指向NMHDR结构的,也可以认为指向包含NMHDR结构的扩充结构的.

NMHDR结构如下所示:

typedef struct tagNMHDR {

HWND hwndFrom; //控件窗口的句柄

UINT idFrom; //控件的ID

UINT code; //控件的通知消息码

} NMHDR;

一个典型的扩充结构如下所示,该结构用于列表视图控件的LVN_KEYDOWN通知消息.

typedef struct tagLV_KEYDOWN {

NMHDR hdr; //NMHDR结构作为第一个成员

WORD wVKey;

UINT flags;

} LV_KEYDOWN;

有些控件通知消息是所有Win32控件共有的,这些消息在表6.22中列出.

 

表6.22 Win32控件共有的通知消息

通知消息码

含义

NM_CLICK

用户在控件上单击鼠标左键.

NM_DBLCLK

用户在控件上双击鼠标左键.

NM_RCLICK

用户在控件上单击鼠标右键.

NM_RDBLCLK

用户在控件上双击鼠标右键.

NM_RETURN

用户在控件上按回车键.

NM_SETFOCUS

控件获得输入焦点.

NM_KILLFOCUS

控件失去输入焦点.

NM_OUTOFMEMORY

内存不够.

 

 

WM_NOTIFY的消息映射由宏ON_NOTIFY负责,该消息映射宏具有如下形式:

ON_NOTIFY( wNotifyCode, id, memberFxn )

参数wNotifyCode说明了通知消息码,参数id是控件的ID,第三个参数则是消息处理函数名.消息处理函数应该按下面的形式声明,其中参数pNotifyStruct指向NMHDR及其扩充结构,参数result指向一个处理结果.

afx_msg void memberFxn( NMHDR * pNotifyStruct, LRESULT * result );

利用ClassWizard可以很方便地加入WM_NOTIFY消息映射及其处理函数,一个典型的WM_NOTIFY消息映射如下所示,其中LVN_KEYDOWN是IDC_LIST1列表视图控件发出的通知消息.

ON_NOTIFY( LVN_KEYDOWN, IDC_LIST1, OnKeydownList1 )

消息处理函数OnKeydownList1的定义如下面所示.在函数中ClassWizard自动把pNHHDR指针强制转换成LV_KEYDOWN型并赋给pLVKeyDow指针,这样,在函数中可通过这两个指针访问LV_KEYDOWN扩充结构及其所含的NMHDR结构.另外,在函数返回时,ClassWizard自动将处理结果赋0值.

void CMyDlg::OnKeydownList1(NMHDR* pNMHDR, LRESULT* pResult)

{

LV_KEYDOWN* pLVKeyDow = (LV_KEYDOWN*)pNMHDR;

// TODO: Add your control notification handler

// code here

*pResult = 0;

}

可以利用ON_NOTIFY_RANGE宏把多个ID连续的控件发出的相同消息映射到同一个处理函数上,具体形式如下,其中参数id和idLast分别说明明了一组连续的控件ID中的头一个和最后一个ID.

ON_NOTIFY_RANGE( wNotifyCode, id, idLast, memberFxn )

相应的消息处理函数应按下面的形式声明,与普通的WM_NOTIFY消息处理函数相比,该函数多了一个参数id用来说明发送通知消息的控件ID.

afx_msg void memberFxn( UINT id, NMHDR * pNotifyStruct, LRESULT * result );

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

6.2.2 旋转按钮控件

旋转按钮(Spin Button)有时也被称为上下控件(Up-Down Control).Windows 95控制面板中的日期/时间程序中就有两个典型的旋转按钮,如图6.2所示.旋转按钮由两个箭头按钮组成,用户在箭头按钮上单击鼠标可以在某一范围内增加或减少某一个值.旋转按钮一般不会单独存在,而是和编辑框或静态正文组成一个多部件控件来共同显示和控制某一个值,用户可以用旋转按钮修改编辑框中的数字,也可以直接在编辑框中修改.例如,在图6.2中,在旋转按钮的左测有一个编辑框,用户可以在编辑框中直接输入新的年份,也可以用旋转按钮来增减编辑框中的年份.通常,把与旋转按钮在一块的编辑框或静态正文称为"伙伴"(buddy).

图6.2 日期/时间程序中的旋转按钮

MFC的CSpinButtonCtrl类封装了旋转按钮的功能.CSpinButtonCtrl的成员函数Create负责创建控件,该函数的声明为

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

 

参数dwStyle是如表6.23所示的各种控件风格的组合.

 

表6.23 旋转按钮控件的风格

控件风格

含义

UDS_HORZ

指定一个水平旋转按钮.若不指定该风格则创建一个垂直的旋转按钮.

UDS_WRAP

当旋转按钮增大到超过最大值时,自动重置为最小值,当减小至低于最小值时,自动重置为最大值.

UDS_ARROWKEYS

当用户按下向下或向上箭头键时,旋转按钮值递增或递减.

UDS_SETBUDDYINT

旋转按钮将自动更新伙伴控件中显示的数值,如果伙伴控件能接受输入,则可在伙伴控件中输入新的旋转按钮值.

UDS_NOTHOUSANDS

伙伴控件中显示的数值每隔三位没有千位分隔符.

UDS_AUTOBUDDY

自动使旋转按钮拥有一个伙伴控件.

UDS_ALIGNRIGHT

旋转按钮在伙伴控件的右侧.

UDS_ALIGNLEFT

旋转按钮在伙伴控件的左侧.

 

 

除上表的风格外,一般还要为旋转按钮指定WS_CHILD和WS_VISIBLE风格.创建一个有伙伴的垂直旋转按钮控件,一般应指定的风格为WS_CHILD|WS_VISIBLE|UDS_AUTOBUDDY| UDS_SETBUDDYINT.对于用对话框模板创建的旋转按钮控件,可以在控件的属性对话框中指定上表中列出的控件风格。例如,在属性对话框中选择Auto buddy,相当于指定了UDS_AUTOBUDDY风格.

在对话框模板中,可以方便地为旋转按钮指定一个伙伴控件.首先,应该在旋转按钮控件的属性对话框中选择Auto buddy和Set buddy integer属性,并在Alignment栏中选择Left或Right,然后就可以确定伙伴控件了.需要指出的是,旋转按钮并不是把离它最近的控件作为伙伴的.伙伴的选择是以tab顺序为参照的,伙伴控件的tab顺序必需紧挨着按钮控件且比它小.例如,如果某一控件的tab顺序是3,而旋转按钮的tab顺序是4,则不论这两个控件距离有多远,在程序运行时,旋转按钮都会自动与该控件结合在一起,形成伙伴关系.

提示:在本章的开头说过,用ClassWizard无法为Win32控件创建数据变量.但我们可以为旋转按钮的伙伴控件(如编辑框)创建一个数据变量,该变量可看成是旋转按钮的数据变量.

通过CSpinButtonCtrl的成员函数,可以对旋转按钮进行查询和设置:

用GetRange和SetRange来查询和设置旋转按钮值的范围,缺省时值的范围是1-100.这两个函数的声明为

void GetRange( int &lower, int& upper ) const;

void SetRange( int nLower, int nUpper );

第一个参数是最小值,该值不能小于UD_MINVAL,第二个参数是最大值,该值不能大于UD_MAXVAL.值的范围不能超过UD_MAXVAL.

用GetPos和SetPos来查询和设置旋转按钮的当前值.函数的声明为

int GetPos( ) const;

int SetPos( int nPos );

用GetBase和SetBase来查询和设置旋转按钮值的计数制.函数的声明为

UINT GetBase( ) const;

int SetBase( int nBase );

如果参数nBase是10,则伙伴控件中显示的数值是十进制的,如果nBase是16,则是十六进制的.

用GetBuddy和SetBuddy来查询和设置旋转按钮的伙伴.上面已讲了在对话框模板中设置伙伴控件的方法,如果是用Create手工创建旋转按钮,则可以用SetBuddy来设置伙伴.函数的声明为

CWnd* GetBuddy( ) const;

CWnd* SetBuddy( CWnd* pWndBuddy );

参数pWndBuddy是指向伙伴控件对象的CWnd型指针.

可以用GetAccel和SetAccel来查询和设置旋转按钮的加速值.在平时,在旋转按钮上按一下只会增/减一个单位,而当按住按钮超过一定时间时,递增或递减的幅度将会加大到指定的加速值,从而加快了增减的速度.如果对缺省的加速值不满意,可以用SetAccel设置新的加速值.可以有一套以上的加速值.函数的声明为

UINT GetAccel( int nAccel, UDACCEL* pAccel ) const;

BOOL SetAccel( int nAccel, UDACCEL* pAccel );

参数nAccel指定了UDACCEL结构数组的大小.参数pAccel指向一个UDACCEL结构数组.UDACCEL结构含有加速值的信息,其定义如下

typedef struct {

int nSec; //加速值生效需要的时间(以秒为单位)

int nInc; //加速值

} UDACCEL;

旋转按钮常被认为是一个简化的滚动条.除了表6.22列出的通知消息外,旋转按钮特有的滚动通知消息是通过WM_HSCROLL和WM_VSCROLL消息发出的.消息处理函数OnHScroll或OnVScroll分别用来处理水平或垂直旋转按钮的事件通知.由于伙伴控件中的内容会自动随旋转按钮变化,所以旋转按钮的通知消息意义不大.如果非要处理通知消息,一个典型的OnVscroll函数如下所示:

void CMyDialog::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)

{

CSpinButtonCtrl* pSpin=(CSpinButtonCtrl*)pScrollBar;

int nPosition;

if(pSpin= =&m_Spin) //判断是否是该旋转按钮发来的消息

{

nPosition=m_Spin.GetPos( ); //获取旋转按钮的当前值

. . . . . .

}

. . . . . .

}

6.2.3 滑尺控件

滑尺(Slider)有时也被称作轨道条(Trackbar),在轨道条中有一个滑尺,在轨道条上通常会标有刻度,用户通过移动滑尺,可以在一个指定的范围内选择一个不精确的值.轨道条可用来调节一个模拟量,也可以用来在一些离散值中进行选择.在Windows 95中,大量使用了轨道条控件,例如,在控制面板中的键盘和鼠标设置程序中就使用了轨道条控件,如图6.3所示.轨道条不仅接受鼠标输入,也可以接受象左右箭头键、PgUp和PgDown这样的键盘输入.

图6.3 鼠标设置程序中的轨道条控件

与选择按钮不同,轨道条是一种模糊型的输入控件,用户不需要进行精确的选择,只要大致调整一下大小就行了.轨道条的这种特性非常符合人的行为习惯,因而在有些情况下是很有用,例如,对于音量的调节,显然用轨道条比用旋转按钮更符合人的日常习惯.

轨道条的滑尺的移动具有离散性.例如,如果指定轨道条的范围是5,那么滑尺只能在包括轨道条两端在内的6个均匀的位置上移动.当然,如果范围很大,则用户就感觉不出是离散的了.

轨道条控件与传统的滚动条控件有很多相似之处,实际上,前者是对后者的一种改进.除了表6.22列出的通知消息外,轨道条控件是依靠WM_HSCROLL和WM_VSCROLL来发送与滑尺有关的通知消息的,并且通知消息与滚动条极为相似.通知消息包括TB_BOTTOM、TB_LINEDOWN、TB_LINEUP、TB_TOP、TB_PAGEDOWN、TB_PAGEUP、TB_ENDTRACK、TB_THUMBPOSITION、TB_THUMBTRACK.对照滚动条的通知消息,读者不难明白这些消息码的含义.其中前四个消息只有在用键盘移动滑尺时才会发出,最后两个消息只有在用鼠标拖动滑尺时才会发出.与滚动条不同的是,Windows会自动把滑尺移动到新位置上.

MFC的CSliderCtrl类封装了轨道条.CSliderCtrl类的Create成员函数负责控件的创建,该函数的声明为

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

 

参数参数dwStyle是如表6.24所示的各种控件风格的组合.

 

表6.24 轨道条控件的风格

控件风格

含义

TBS_HORZ

指定一个水平轨道条.该风格是默认的.

TBS_VERT

指定一个垂直轨道条.

TBS_AUTOTICKS

在范围设定后,自动为轨道条加上刻度.

TBS_NOTICKS

轨道条无刻度.

TBS_BOTTOM

在水平轨道条的底部显示刻度,可与TBS_TOP一起使用.

TBS_TOP

在水平轨道条的顶部显示刻度,可与TBS_BOTTOM一起使用.

TBS_RIGHT

在垂直轨道条的右侧显示刻度,可与TBS_LEFT一起使用.

TBS_LEFT

在垂直轨道条的左侧显示刻度,可与TBS_RIGHT一起使用.

TBS_BOTH

在轨道条的上下部或左右两侧都显示刻度.

TBS_ENABLESELRANGE

在轨道条中显示一个选择范围.

 

 

除上表的风格外,一般还要为轨道条指定WS_CHILD和WS_VISIBLE风格.要创建一个具有刻度的水平轨道条,一般应指定风格为WS_CHILD|WS_VISIBLE|TBS_HORZ| TBS_AUTOTICKS.对于用对话框模板创建的轨道条控件,可以在控件的属性对话框中指定上表中列出的控件风格。例如,在属性对话框中选择Autoticks,相当于指定了TBS_AUTOTICKS风格.

通过调用CSliderCtrl类的成员函数,可以对轨道条进行查询和设置:

用GetRange和SetRange来查询和设置轨道条的范围,缺省的范围是0-100.函数的声明为

void GetRange( int& nMin, int& nMax ) const;

void SetRange( int nMin, int nMax, BOOL bRedraw = FALSE );

参数nMin和nMax分别是最小和最大值,参数bRedraw为TRUE时将重绘控件.

用GetPos和SetPos来查询和设置轨道条的当前值.函数的声明为

int GetPos( ) const;

void SetPos( int nPos );

用GetLineSize和SetLineSize来查询和设置在按一下左箭头键或右箭头键时滑尺的移动量,该移动量的缺省值是1个单位.函数的声明为

int GetLineSize( ) const;

int SetLineSize( int nSize );

用GetPageSize和SetPageSize来查询和设置滑尺的块移动量,块移动量是指当按下PgUp或PgDown键时滑尺的移动量.函数的声明为

int GetPageSize( ) const;

int SetPageSize( int nSize );

用SetTicFreq设置轨道条的刻度的频度.缺省的频度是每个单位都有一个刻度,在范围较大时,为了使刻度不至于过密,需要调用该函数设置一个合理的频度.函数的声明为

void SetTicFreq( int nFreq );

参数nFreq说明了两个刻度之间的间隔.

用函数SetTic来在指定位置设置刻度.Windows自动显示的刻度是均匀的,利用该函数可以人为设置不均匀的刻度,该函数的声明为

BOOL SetTic( int nTic );

用函数ClearTics来清除所有的刻度.该函数的声明为

void ClearTics( BOOL bRedraw = FALSE );

 

6.2.4 进度条控件

进度条(Progress Bar)的用途是向用户显示程序的进度.进度条是Win32控件中最简单的控件,只需少数设置即可.Windows 95中使用进度条的一个例子是磁盘扫描(ScanDisk)程序,如图6.4所示.进度条显示的数据是不精确的,它是一种模糊型的输出控件.

图6.4 磁盘扫描程序中的进度条

MFC的CProgressCtrl类封装了进度条控件.该类的Create成员函数负责创建控件,该函数的声明为

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

 

进度条没有专门的风格,所以参数dwStyle只能指定普通的窗口风格,一般只需指定WS_CHILD|WS_VISIBLE就可以了.

CProgressCtrl类提供了少量的成员函数用来设置进度条:

void SetRange( int nLower, int nUpper );

该函数用来设置进度条的范围.参数nLow和nUpper分别指定了最小和最大值,缺省时进度条的范围是0-100.

int SetPos( int nPos );

用来设置进度条的当前进度.函数返回的是进度条的前一个进度.

int StepIt( );

使进度增加一个步长,步长值是由SetStep函数设置的,缺省的步长值是10.函数返回进度条的前一个进度.

int SetStep( int nStep );

用来设置步长值.函数返回原来的步长值.

 

6.2.5 树形视图控件

树形视图(Tree View)是一种特殊的列表,它能以树形分层结构显示数据.在Windows 95的资源管理器(Windows Explorer)的左侧窗口中就有一个用于显示目录的典型的树形视图,如图6.5所示.在树形视图中,每个表项显示一个标题(Label),有时还会显示一幅图象,图象和标题分别提供了对数据的形象和抽象描述.通过图6.5可以看出,树形视图可以很清楚的显示出数据的分支和层次关系.由此可见,树形视图非常适合显示象目录,网络结构等这样的复杂数据.传统的列表框不能分层显示数据,因此树形视图可以看作是对列表框的一种重要改进.

图6.5 资源管理器中的树形视图和列表视图

树形视图是一种复杂的控件,它的复杂性体现在数据项之间具有分支和层次关系.例如,如果要向树形视图中加入新的项,则必需描述出该项与树形视图中已有项的相互关系,而不可能象往列表框中加入新项那样,调用一下AddString就完事了.另外,树形视图可以在每一项标题的左边显示一幅图象,这使控件显得更加形象生动,但同时也增加了控件的复杂程度.

在讨论如何使用树形视图控件以前,有必要先介绍一下与该控件有关的一些数据类型:

HTREEITEM型句柄.Windows用HTREEITEM型句柄来代表树形视图的一项,程序通过HTREEITEM句柄来区分和访问树形视图的各个项.

TV_ITEM结构.该结构用来描述一个表项,它包含了表项的各种属性,其定义如下

typedef struct _TV_ITEM

{ tvi

UINT  mask; /*包含一些屏蔽位(下面的括号中列出)的组合,用来表明结构的哪些成员是有效的*/

HTREEITEM hItem; //表项的句柄(TVIF_HANDLE)

UINT state; //表项的状态(TVIF_STATE)

UINT stateMask; //状态的屏蔽组合(TVIF_STATE)

LPSTR pszText; //表项的标题正文(TVIF_TEXT)

int cchTextMax; //正文缓冲区的大小(TVIF_TEXT)

int iImage; //表项的图象索引(TVIF_IMAGE)

int iSelectedImage; //选中的项的图象索引(TVIF_SELECTEDIMAGE)

int cChildren; /*表明项是否有子项(TVIF_CHILDREN),为1则有,为0则没有*/

LPARAM lParam; //一个32位的附加数据(TVIF_PARAM)

} TV_ITEM, FAR *LPTV_ITEM;

如果要使树形视图的表项显示图象,需要为树形视图建立一个位图序列,这时,iImage说明表项显示的图象在位图序列中的索引,iSelectedImage则说明了选中的表项应显示的图象,在绘制图标时,树形视图可以根据这两个参数提供的索引在位图序列中找到对应的位图.lParam可用来放置与表项相关的数据,这常常是很有用的.state和stateMask的常用值在表6.25中列出,其中stateMask用来说明要获取或设置哪些状态.

 

表6.25 树形视图表项项的常用状态

状态

对应的状态屏蔽

含义

TVIS_SELECTED

同左

项被选中.

TVIS_EXPANDED

同左

项的子项被展开.

TVIS_EXPANDEDONCE

同左

项的子项曾经被展开过.

TVIS_CUT

同左

项被选择用来进行剪切和粘贴操作.

TVIS_FOCUSED

同左

项具有输入焦点.

TVIS_DROPHILITED

同左

项成为拖动操作的目标.

 

 

TV_INSERTSTRUCT结构.在向树形视图中插入新项时要用到该结构,其定义为

typedef struct _TV_INSERTSTRUCT {

HTREEITEM hParent; //父项的句柄

HTREEITEM hInsertAfter; //说明应插入到同层中哪一项的后面

TV_ITEM item;

} TV_INSERTSTRUCT;

如果hParent的值为TVI_ROOT或NULL,那么新项将被插入到树形视图的最高层(根位置).hInsertAfter的值可以是TVI_FIRST、TVI_LAST或TVI_SORT,其含义分别是将新项插入到同一层中的开头、最后或排序插入.

NM_TREEVIEW结构.树形视图的大部分通知消息都会附带指向该结构的指针以提供一些必要的信息.该结构的定义为

typedef struct _NM_TREEVIEW { nmtv

NMHDR hdr; //标准的NMHDR结构

UINT action; //表明是用户的什么行为触发了该通知消息

TV_ITEM itemOld; //旧项的信息

TV_ITEM itemNew; //新项的信息

POINT ptDrag; //事件发生时鼠标的客户区坐标

} NM_TREEVIEW;

TV_KEYDOWN结构.提供与键盘事件有关的信息.该结构的定义为

pedef struct _TV_KEYDOWN { tvkd

NMHDR hdr; //标准的NMHDR结构

WORD wVKey; //虚拟键盘码

UINT flags; //为0

} TV_KEYDOWN;

TV_DISPINFO结构.提供与表项的显示有关的信息.该结构的定义为

typedef struct _TV_DISPINFO { tvdi

NMHDR hdr;

TV_ITEM item;

} TV_DISPINFO;

MFC的CTreeCtrl类封装了树形视图.该类的Create成员函数负责控件的创建,该函数的声明为

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

 

其中参数dwStyle是如表6.26所示的控件风格的组合.

 

表6.26 树形视图的风格

控件风格

含义

TVS_HASLINES

在父项与子项间连线以清楚地显示结构.

TVS_LINESATROOT

只在根部画线.

TVS_HASBUTTONS

显示带有"+"或"-"的小方框来表示某项能否被展开或已展开.

TVS_EDITLABELS

用户可以编辑表项的标题.

TVS_SHOWSELALWAYS

即使控件失去输入焦点,仍显示出项的选择状态.

TVS_DISABLEDRAGDROP

不支持拖动操作.

 

 

除上表的风格外,一般还要指定WS_CHILD和WS_VISIBLE窗口风格.对于用对话框模板创建的树形视图控件,可以在控件的属性对话框中指定上表中列出的控件风格。例如,在属性对话框中选择Has buttons,相当于指定了TVS_HASBUTTONS风格.

CTreeCtrl类提供了大量的成员函数.对于常用的函数,这里结合实际应用的需要,介绍如下:

向树形视图中插入新的表项.首先应提供一个TV_INSERTSTRUCT结构并在该结构中对插入项进行描述.如果要在树形视图中显示图象,则应该先创建一个CImageList对象并使该对象包含一个位图序列,然后调用SetImageList为树形视图设置位图序列.然后调用InsertItem函数把新项插入到树形视图中.函数的声明为

CImageList* SetImageList( CImageList * pImageList, int nImageListType );

参数pImageList指向一个CImageList对象,参数nImageListType一般应为TVSIL_NORMAL.

HTREEITEM InsertItem( LPTV_INSERTSTRUCT lpInsertStruct );

参数lpInsertStruct指向一个TV_INSERTSTRUCT结构.函数返回新插入项的句柄.

用DeleteItem来删除指定项,用DeleteAllItems删除所有项.函数的声明为

BOOL DeleteItem( HTREEITEM hItem );

BOOL DeleteAllItems( );

操作成功则函数返回TRUE,否则返回FALSE.

树形视图控件会根据用户的输入自动展开或折叠子项.但有时需要在程序中展开或折叠指定项,则应该调用Expand,该函数的声明为

BOOL Expand( HTREEITEM hItem, UINT nCode );

参数hItem指定了要展开或折叠的项.参数nCode是一个标志,指定了函数应执行的操作,它可以是TVE_COLLAPSE(折叠)、TVE_COLLAPSERESET(折叠并移走所有的子项)、TVE_EXPAND(展开)或TVE_TOGGLE(在展开和折叠状态之间翻转).

要查询或设置选择项,应调用GetSelectedItem或SelectItem.函数的声明为

HTREEITEM GetSelectedItem( );

BOOL SelectItem( HTREEITEM hItem );

要对指定的项查询或设置,可调用GetItem和SetItem.用这两个功能强大的函数,几乎可以查询和设置项的所有属性,包括表项的正文、图像及选择状态.函数的声明为

BOOL GetItem( TV_ITEM* pItem );

BOOL SetItem( TV_ITEM* pItem );

参数pItem是指向TV_ITEM结构的指针,函数是通过该结构来查询或设置指定项的,在调用函数前应该使该结构的hItem成员有效以指定表项.CTreeCtrl还提供了一系列函数可完成GetItem和SetItem的部分功能,其中GetItemState、GetItemText、GetItemData、GetItemImage和ItemHasChildren函数用于查询,SetItemState、SetItemText、SetItemData和SetItemImage函数用于设置.

在使用树形视图控件时,一个经常遇到的问题是对于一个已知表项,如何找到与该项有某种关系的项,例如,父项、子项、兄弟项、下一个或前一个可见的项.利用功能强大的GetNextItem函数,可以解决这个问题.该函数也可以用来搜索具有某种状态的表项.GetNextItem在遍历树形视图时是很有用的,它的声明为

HTREEITEM GetNextItem( HTREEITEM hItem, UINT nCode );

参数hItem指定了一个项.参数nCode是一个标志,标明了与指定项的关系,nCode可以是如表6.27所示的各种标志.如果找到相关的项,函数返回该项的句柄,否则函数返回NULL.

 

表6.27 关系标志

标志

含义

TVGN_CARET

返回当前的选择项.

TVGN_CHILD

返回指定表项的子项.

TVGN_DROPHILITE

返回拖动操作的目标项.

TVGN_FIRSTVISIBLE

返回第一个可见项.

TVGN_NEXT

返回指定项的下一个兄弟项(Sibling Item).

TVGN_NEXTVISIBLE

返回指定项的后一个可见项.

TVGN_PARENT

返回指定项的父项.

TVGN_PREVIOUS

返回指定项的前一个兄弟项.

TVGN_PREVIOUSVISIBLE

返回指定项的前一个可见项.

TVGN_ROOT

返回位于最高层(根位置)的第一个表项.

 

 

CTreeCtrl类提供了一系列的成员函数来完成GetNextItem的某一项功能,包括GetRootItem、GetFirstVisibleItem、GetNextVisibleItem、GetPrevVisibleItem、GetChildItem、GetNextSiblingItem、GetPrevSiblingItem、GetParentItem、GetSelectedItem和GetDropHilightItem.

 

除了表6.22列出的控件消息外,树形视图控件还会发送自己特有的通知消息,其中常用的有下面这几个:

TVN_SELCHANGING和TVN_SELCHANGED.在用户改变了对表项的选择时,控件会发送这两个消息.消息会附带一个指向NM_TREEVIEW结构的指针,程序可从该结构中获得必要的信息.两个消息都会在该结构的itemOld成员中包含原来的选择项的信息,在itemNew成员中包含新选择项的信息,在action成员中表明是用户的什么行为触发了该通知消息(若是TVC_BYKEYBOARD则表明是键盘,若是TVC_BYMOUSE则表明是鼠标,若是TVC_UNKNOWN则表示未知).两个消息的不同之处在于,如果TVN_SELCHANGING的消息处理函数返回TRUE,那么就阻止选择的改变,如果返回FALSE,则允许改变.

TVN_KEYDOWN.该消息表明了一个键盘事件.消息会附带一个指向TV_KEYDOWN结构的指针,通过该结构程序可以获得按键的信息.

TVN_BEGINLABELEDIT和TVN_ENDLABELEDIT.分别在用户开始编辑和结束编辑项的标题时发送.消息会附带一个指向TV_DISPINFO结构的指针,程序可从该结构中获得必要的信息.在前者的消息处理函数中,可以调用GetEditControl成员函数返回一个指向用于编辑标题的编辑框的指针,如果处理函数返回FALSE,则允许编辑,如果返回TRUE,则禁止编辑.在后者的消息处理函数中,TV_DISPINFO结构中的item.pszText指向编辑后的新标题,如果pszText为NULL,那么说明用户放弃了编辑,否则,程序应负责更新项的标题,这可以由SetItem或SetItemText函数来完成.

 

树形视图控件还可以支持拖放操作,限于篇幅,这里就不作介绍了.

6.2.6 列表视图控件

  列表视图(List View)用来成列地显示数据.在Windows 95的资源管理器的右侧窗口中就有一个典型的列表视图,如图6.5所示.列表视图的表项通常包括图标(Icon)和标题(Label)两部分,它们分别提供了对数据的形象和抽象描述.列表视图控件是对传统的列表框的重大改进,它能够以下列四种格式显示数据.读者可以在资源管理器中的视图(View)菜单中切换列表视图的显示格式,来看看四种格式的不同之处.

·   大图标格式(Large Icons).可逐行显示多列表项,图标的大小可由应用程序指定,通常是32×32像素,在图标的下面显示标题.

·   小图标格式(Small Icons).可逐行显示多列表项,图标的大小可由应用程序指定,通常是16×16像素,在图标的右面显示标题.表项以行的方式组织.

·   列表格式(List).与小图标格式类似.不同之处在于表项是逐列多列显示的.

·   报告格式(Report或Details).每行仅显示一个表项,在标题的左边显示一个图标,表项可以不显示图标而只显示标题.表项的右边可以附加若干列子项(Subitem),子项只显示正文.在控件的顶端还可以显示一个列表头用来说明各列的类型.列表视图的报告格式很适合显示报表(如数据库报表).

 

在讨论如何使用列表视图控件以前,显向读者介绍一下与该控件有关的一些数据类型:

LV_COLUMN结构.该结构仅用于报告式列表视图,用来描述表项的某一列.要想向表项中插入新的一列,需要用到该结构.LV_COLUMN结构的定义为

typedef struct _LV_COLUMN {

UINT mask; //屏蔽位的组合(见下面括号),表明哪些成员是有效的.

int fmt; /*该列的表头和子项的标题显示格式(LVCF_FMT).可以是LVCFMT_CENTER、LVCFMT_LEFT或LVCFMT_RIGHT*/

int cx; //以像素为单位的列的宽度(LVCF_FMT)

LPTSTR pszText; //指向存放列表头标题正文的缓冲区(LVCF_TEXT)

int cchTextMax; //标题正文缓冲区的长度(LVCF_TEXT)

int iSubItem; //说明该列的索引(LVCF_SUBITEM)

} LV_COLUMN;

LV_ITEM结构.该结构用来描述一个表项或子项,它包含了项的各种属性,其定义为

typedef struct _LV_ITEM {

UINT mask; //屏蔽位的组合(见下面括号),表明哪些成员是有效的

int iItem; //从0开始编号的表项索引(行索引)

int iSubItem; /*从1开始编号的子项索引(列索引),若值为0则说明该成员无效,结构描述的是一个表项而不是子项*/

UINT state; //项的状态(LVIF_STATE)

UINT stateMask; //项的状态屏蔽

LPTSTR pszText; //指向存放项的正文的缓冲区(LVIF_TEXT)

int cchTextMax; //正文缓冲区的长度(LVIF_TEXT)

int iImage; //图标的索引(LVIF_IMAGE)

LPARAM lParam; // 32位的附加数据(LVIF_PARAM)

} LV_ITEM;

其中lParam成员可用来存储与项相关的数据,这在有些情况下是很有用的.state和stateMask的值如表6.28所示,stateMask用来说明要获取或设置哪些状态.

 

表6.28 列表视图的状态

状态

对应的状态屏蔽

含义

LVIS_CUT

同左

项被选择用来进行剪切和粘贴操作.

LVIS_DROPHILITED

同左

项成为拖动操作的目标.

LVIS_FOCUSED

同左

项具有输入焦点.

LVIS_SELECTED

同左

项被选中.

NM_LISTVIEW结构.该结构用于存储列表视图的通知消息的有关信息,大部分列表视图的通知消息都会附带指向该结构的指针.NM_LISTVIEW的定义为

typedef struct tagNM_LISTVIEW {

NMHDR hdr; //标准的NMHDR结构

int iItem; //表项的索引,若为-1则无效

int iSubItem; //子项的索引,若为0则无效

UINT uNewState; //项的新状态

UINT uOldState; //项原来的状态

UINT uChanged; /*取值与LV_ITEM的mask成员相同,用来表明哪些状态发生了变化*/

POINT ptAction; //事件发生时鼠标的客户区坐标

LPARAM lParam; //32位的附加数据

} NM_LISTVIEW;

LV_DISPINFO结构.该结构包含了与项的显示有关的信息,其定义为

typedef struct tagLV_DISPINFO {

NMHDR hdr;

LV_ITEM item;

} LV_DISPINFO;

LV_KEYDOWN结构.该结构包含一些与键盘有关的信息,其定义为

typedef struct tagLV_KEYDOWN {

NMHDR hdr;

WORD wVKey; //虚拟键盘码

UINT flags; //总为0

} LV_KEYDOWN;

MFC的CListCtrl类封装了列表视图控件.该类的Create函数负责创建控件,函数的声明为

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

 

其中参数dwStyle是如表6.29所示的控件风格的组合.

表6.29 列表视图的风格

控件风格

含义

LVS_ALIGNLEFT

当显示格式是大图标或小图标时,标题放在图标的左边.缺省情况下标题放在图标的下面.

LVS_ALIGNTOP

当显示格式是大图标或小图标时,标题放在图标的上边.

LVS_AUTOARRANGE

当显示格式是大图标或小图标时,自动排列控件中的表项.

LVS_EDITLABELS

用户可以修改标题.

LVS_ICON

指定大图标显示格式.

LVS_LIST

指定列表显示格式.

LVS_NOCOLUMNHEADER

在报告格式中不显示列的表头.

LVS_NOLABELWRAP

当显示格式是大图标时,使标题单行显示.缺省时是多行显示.

LVS_NOSCROLL

列表视图无滚动条.

LVS_NOSORTHEADER

报告列表视图的表头不能作为排序按钮使用.

LVS_OWNERDRAWFIXED

由控件的拥有者负责绘制表项.

LVS_REPORT

指定报告显示格式.

LVS_SHAREIMAGELISTS

使列表视图共享图像序列.

LVS_SHOWSELALWAYS

即使控件失去输入焦点,仍显示出项的选择状态.

LVS_SINGLESEL

指定一个单选择列表视图.缺省时可以多项选择.

LVS_SMALLICON

指定小图标显示格式.

LVS_SORTASCENDING

按升序排列表项.

LVS_SORTDESCENDING

按降序排列表项.

 

除上表的风格外,一般还要指定WS_CHILD和WS_VISIBLE窗口风格.风格组合WS_CHILD| WS_VISIBLE|LVS_REPORT|LVS_AUTOARRANGE|LVS_EDITLABLES|LVS_SINGLESEL将指定一个自动排列的、可编辑标题的、单选择报告式列表视图控件.要指定大图标、小图标或列表式的列表视图控件,则应该把LVS_REPORT换成LVS_ICON、LVS_SMALLICON或LVS_LIST.

对于用对话框模板创建的列表视图控件,可以在控件的属性对话框中指定上表中列出的控件风格。例如,在属性对话框的Styles页的View栏中选择Icon,相当于指定了LVS_ICON风格.

CListCtrl类提供了大量的成员函数.在这里,我们结合实际应用来介绍一些常用的函数:

列的插入和删除.在以报告格式显示列表视图时,一般会显示一列表项和多列子项.在初始化列表视图时,先要调用InsertColumn插入各个列,该函数的声明为

int InsertColumn( int nCol, const LV_COLUMN* pColumn );

其中参数nCol是新列的索引,参数pColumn指向一个LV_COLUMN结构,函数根据该结构来创建新的列.若插入成功,函数返回新列的索引,否则返回-1.

要删除某列,应调用DeleteColumn函数,其声明为

BOOL DeleteColumn( int nCol );

表项的插入.要插入新的表项,应调用InsertItem.如果要显示图标,则应该先创建一个CImageList对象并使该对象包含用作显示图标的位图序列.然后调用SetImageList来为列表视图设置位图序列.函数的声明为

int InsertItem( const LV_ITEM* pItem );

参数pItem指向一个LV_ITEM结构,该结构提供了对表项的描述.若插入成功则函数返回新表项的索引,否则返回-1.

CImageList* SetImageList( CImageList* pImageList, int nImageList );

参数pImageList指向一个CImageList对象,参数nImageList用来指定图标的类型,若其值为LVSIL_NORMAL,则位图序列用作显示大图标,若值为LVSIL_SMALL,则位图序列用作显示小图标.可用该函数同时指定一套大图标和一套小图标.

要删除某表项,应调用DeleteItem,要删除所有的项,应调用DeleteAllItems.一旦表项被删除,其子项也被删除.函数的声明为

BOOL DeleteItem( int nItem );

BOOL DeleteAllItems( );

调用GetItemText和SetItemText来查询和设置表项及子项显示的正文.SetItemText的一个重要用途是对子项进行初始化.函数的声明为

int GetItemText( int nItem, int nSubItem, LPTSTR lpszText, int nLen ) const;

CString GetItemText( int nItem, int nSubItem ) const;

BOOL SetItemText( int nItem, int nSubItem, LPTSTR lpszText );

其中参数nItem是表项的索引(行索引),nSubItem是子项的索引(列索引),若nSubItem为0则说明函数是针对表项的.参数lpszText指向正文缓冲区,参数nLen说明了缓冲区的大小.第二个版本的GetItemText返回一个含有项的正文的Cstring对象.

调用GetItem和SetItem来查询和设置.用这两个功能强大的函数,几乎可以查询和设置指定项的所有属性,包括正文、图标及选择状态.函数的声明为

BOOL GetItem( LV_ITEM* pItem ) const;

BOOL SetItem( const LV_ITEM* pItem );

参数pItem是指向LV_ITEM结构的指针,函数是通过该结构来查询或设置指定项的,在调用函数前应该使该结构的iItem或iSubItem成员有效以指定表项或子项.CListCtrl还提供了一系列函数可完成GetItem和SetItem的部分功能,其中GetItemState、GetItemText和GetItemData函数用于查询,SetItemState、SetItemText和SetItemData函数用于设置.

要查询表项的数目,应该调用GetItemCount,其声明为 int GetItemCount( );

要寻找与指定表项项相关的表项,或寻找具有某种状态的表项,应该调用GetNextItem.该函数的一个重要用处是搜索被选择的表项.函数的声明为

int GetNextItem( int nItem, int nFlags ) const;

参数nItem是指定项的索引,参数nFlags是如表6.30所示的标志,用来指定查询的关系.函数返回搜索到的表项的索引,若未找到则返回-1.

表6.30 关系标志

标志

含义

LVNI_ABOVE

返回位于指定表项上方的表项.

LVNI_ALL

缺省标志,返回指定表项的下一个表项(以索引为序).

LVNI_BELOW

返回位于指定表项下方的表项.

LVNI_TOLEFT

返回位于指定表项左边的表项.

LVNI_TORIGHT

返回位于指定表项右边的表项.

LVNI_DROPHILITED

返回拖动操作的目标表项.

LVNI_FOCUSED

返回具有输入焦点的表项.

LVNI_SELECTED

返回被选择的表项.

 

 

要对表项进行排列、排序和搜索,可分别调用Arrange、SortItems和FindItems函数来完成.

有时需要在列表视图创建后动态地改变其显示格式,例如,资源管理器中的列表视图就可以在四中显示格式之间切换.改变显示格式其实就是改变列表视图的风格,要改变控件的风格,应先调用::GetWindowLong获取控件原来的风格,并对其进行修改,然后调用::SetWindowLong设置新的风格.这两个函数不是成员函数,而是Windows API函数,用来查询和设置窗口的风格.

 

除了表6.22列出的控件消息外,列表视图控件还会发送自己特有的通知消息,其中常用的有下面这几个:

LVN_ITEMCHANGING和LVN_ITEMCHANGED.当列表视图的状态发生变化时,会发送这两个通知消息.例如,当用户选择了新的表项时,程序就会收到这两个消息.消息会附带一个指向NM_LISTVIEW结构的指针,消息处理函数可从该结构中获得状态信息.两个消息的不同之处在于,前者的消息处理函数如果返回TRUE,那么就阻止选择的改变,如果返回FALSE,则允许改变.

LVN_KEYDOWN.该消息表明了一个键盘事件.消息会附带一个指向LV_KEYDOWN结构的指针,通过该结构程序可以获得按键的信息.

LVN_BEGINLABELEDIT和LVN_ENDLABELEDIT.分别在用户开始编辑和结束编辑标题时发送.消息会附带一个指向LV_DISPINFO结构的指针.在前者的消息处理函数中,可以调用GetEditControl成员函数返回一个指向用于编辑标题的编辑框的指针,如果处理函数返回FALSE,则允许编辑,如果返回TRUE,则禁止编辑.在后者的消息处理函数中,LV_DISPINFO结构中的item.pszText指向编辑后的新标题,如果pszText为NULL,那么说明用户放弃了编辑,否则,程序应负责更新表项的标题,这可以由SetItem或SetItemText函数来完成.

 

列表视图控件还可以支持拖放操作,这里就不详细介绍了.

6.2.7 测试新型Win32控件的一个例子

本小节将向读者提供一个测试Win32控件的例子.测试程序名为Ctrl32,其界面如图6.6所示,该程序对前面介绍的五种Win32控件均进行了测试:

·   对树形视图的测试着重演示了如何在各个层次上加入表项以及如何使表项显示图象,表项在平常状态下和选中状态下将显示不同的图象.

·   对列表视图的测试包括如何生成一个报告式列表视图,如何在四个显示格式间切换以及如何使表项显示图标.读者可以在列表视图下面的下拉列表式组合框选择不同的显示格式.

·   轨道条的测试包括初始化及WM_HSCROLL消息的处理.进度条的进度将会随着滑尺位置的改变而改变.

·   演示了如何为旋转按钮指定伙伴控件以及旋转按钮的初始化.

图6.6 Ctrl32测试程序

 

请读者按下列步骤操作:

用AppWizard建立一个基于对话框的MFC应用程序,程序名为Ctrl32.

插入两个位图(Bitmap)资源,其ID分别是IDB_BITMAP1和IDB_BITMAP2,树形视图和列表视图将这用两幅位图来为表项显示图象.两个位图的尺寸分别为64×16和128×32.每个位图都包含4个子图,每个子图中都有一个颜色不同的矩形或圆,请按图6.7和表6.31绘制.两个位图的子图都是一样的,只不过大小不同,一个是16×16,一个是32×32.

图6.7 用于树形视图和列表视图的位图

表6.31

子图的形状

前景色

背景色

矩形

蓝色

黄色

矩形

红色

淡蓝色

蓝色

黄色

红色

淡蓝色

打开IDD_CTRL32_DIALOG对话框模板资源,清除OK按钮外的所有控件并把OK按钮的标题改为C&lose.将对话框的大小调整为300×150,然后按图6.6和表6.32加入控件,并按表6.32用ClassWizard为CCtrl32Dlg类加入成员变量,注意,这些成员变量都是控件对象.

 

表6.32

控件类型

控件ID

需设置的属性

对应的控件对象名

树形视图

缺省

选择Has buttons、Has lines、Lines at root和Edit labels.

m_Tree

列表视图

缺省

选择Report格式、Auto arrange、Edit lables

m_List

轨道条

缺省

选择Tick marks和Autoticks.

m_Slider

进度条

缺省

缺省

m_Progress

组合框

缺省

选择Drop List类型,不选择Sort.加入4个表项:Icon、Small icon、List和Report.

m_ListBox

编辑框

缺省

缺省.要注意其tab顺序比旋转按钮小1.

 

旋转按钮

缺省

选择Auto buddy和Set buddy integer

m_Spin

 

 

在CCtrl32Dlg类的定义处为改类加入下面两个成员,这两个CImageList对象用来向树形视图和列表视图提供位图序列.

CImageList m_SmallImageList;

CImageList m_LargeImageList;

用ClassWizard为CCtrl32Dlg类加入如表6.33所示的消息处理函数.其中OnHScroll函数用来处理轨道条的通知消息,OnSelchangeCombo用来切换列表视图的显示格式.

 

表6.33 CCtrl32Dlg类的控件通知消息处理函数

Object IDS

Messages

Member functions

CCtrl32Dlg

WM_HSCROLL

OnHScroll

IDC_COMBO1

CBN_SELCHANGE

OnSelchangeCombo

 

 

最后,请读者按清单6.2修改程序.

 

清单6.2 CCtrl32Dlg类的部分源代码

// Ctrl32Dlg.cpp : implementation file

 

. . . . . .

 

char *szLabel[2]={"Rectangle","Circle"};

char *szColumn[3]={"Shape","Fore color","Back color"};

char *szData[4][3]=

{{"Rectangle","Blue","Yellow"},

{"Rectangle","Red","Blue"},

{"Circle","Blue","Yellow"},

{"Circle","Red","Blue"}};

DWORD nStyle[4]={LVS_ICON,LVS_SMALLICON,LVS_LIST,LVS_REPORT};

 

BOOL CCtrl32Dlg::OnInitDialog()

{

CDialog::OnInitDialog();

. . . . . .

// TODO: Add extra initialization here

 

//初始化旋转按钮

m_Spin.SetRange(0,200);

m_Spin.SetPos(0);

//初始化轨道条

m_Slider.SetRange(0,20);

m_Slider.SetTicFreq(2);

m_Slider.SetLineSize(2);

m_Slider.SetPageSize(4);

m_Slider.SetPos(0);

//初始化进度条

m_Progress.SetRange(0,20);

m_Progress.SetPos(0);

//创建位图序列,用于树形视图和列表视图显示图像

m_SmallImageList.Create(IDB_BITMAP1,16,0,FALSE); //16*16的位图序列

m_LargeImageList.Create(IDB_BITMAP2,32,0,FALSE); //32*32的位图序列

//初始化树形视图

TV_INSERTSTRUCT tvInsert;

HTREEITEM hItem;

int i,j;

char buffer[20];

 

m_Tree.SetImageList(&m_SmallImageList,TVSIL_NORMAL);

 

tvInsert.item.mask=TVIF_TEXT|TVIF_IMAGE|TVIF_SELECTEDIMAGE;

for(i=0;i<2;i++)

{

tvInsert.hParent=NULL; //指定该项位于最高层

tvInsert.hInsertAfter=TVI_LAST;

tvInsert.item.pszText=szLabel[i];

tvInsert.item.iImage=i*2; //指定表项显示的图象

tvInsert.item.iSelectedImage=i*2+1; //指定选择状态下应显示的图象

hItem=m_Tree.InsertItem(&tvInsert);

for(j=0;j<3;j++)

{

tvInsert.hParent=hItem; //指定该项为子项

tvInsert.hInsertAfter=TVI_SORT;

sprintf(buffer,"%s%d",szLabel[i],j);

tvInsert.item.pszText=buffer;

m_Tree.InsertItem(&tvInsert);

}

}

//初始化列表视图

LV_COLUMN lvc;

LV_ITEM lvi;

 

m_List.SetImageList(&m_SmallImageList,LVSIL_SMALL);

m_List.SetImageList(&m_LargeImageList,LVSIL_NORMAL);

m_ComboBox.SelectString(-1,"Report");

 

lvc.mask=LVCF_FMT|LVCF_WIDTH|LVCF_TEXT|LVCF_SUBITEM;

lvc.fmt=LVCFMT_LEFT;

for(i=0;i<3;i++) //插入各列

{

lvc.pszText=szColumn[i];

if(i==0)

lvc.cx=m_List.GetStringWidth(szColumn[0])+50;

else

lvc.cx=m_List.GetStringWidth(szColumn[i])+15;

lvc.iSubItem=i;

m_List.InsertColumn(i,&lvc);

}

lvi.mask=LVIF_TEXT|LVIF_IMAGE;

lvi.iSubItem=0;

for(i=0;i<4;i++) //插入表项

{

lvi.pszText=szData[i][0];

lvi.iItem=i;

lvi.iImage=i;

m_List.InsertItem(&lvi);

for(j=1;j<3;j++)

m_List.SetItemText(i,j,szData[i][j]);

}

 

return TRUE; // return TRUE unless you set the focus to a control

}

 

void CCtrl32Dlg::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)

{

// TODO: Add your message handler code here and/or call default

 

CSliderCtrl* pSlider=(CSliderCtrl*)pScrollBar;

//判断是否是m_Slider轨道条发送的消息

if(&m_Slider!=pSlider) return;

m_Progress.SetPos(m_Slider.GetPos());

CDialog::OnHScroll(nSBCode, nPos, pScrollBar);

}

 

void CCtrl32Dlg::OnSelchangeCombo()

{

// TODO: Add your control notification handler code here

 

long lStyle;

lStyle=GetWindowLong(m_List.GetSafeHwnd(),GWL_STYLE);

//清除所有与显示格式有关的风格标志

lStyle&=~(LVS_ICON|LVS_SMALLICON|LVS_LIST|LVS_REPORT);

lStyle|=nStyle[m_ComboBox.GetCurSel()];

//设置新的风格

SetWindowLong(m_List.GetSafeHwnd(),GWL_STYLE,lStyle);

m_List.Invalidate(); //刷新

}

  对控件的初始化工作在OnInitDialog中完成.函数中使用的各种结构和函数在前面均介绍过,并不难懂.唯一需要说明的是CImageList对象的使用.CImageList对象用来存储多个大小相同的图象,如果程序中要用到大量的尺寸相同的位图或图标,可以用CImageList对象把它们组织成图象序列来使用,通过指定序列的索引,可以获得序列中的图象.树形视图和列表视图均使用CImageList对象来设置图象序列.在OnInitDialog函数中,调用了CImageList::Create来创建一个图象序列.该函数的声明为

BOOL Create( UINT nBitmapID, int cx, int nGrow, COLORREF crMask );

  参数nBitmapID是位图资源的ID,在该位图中包含了一些尺寸相同的子图,参数cx说明了序列中每幅图象的宽度(以像素为单位),参数nGrow说明了当对象包含的图象序列增大时应预留的空位个数,参数crMask如果为TRUE,则说明图象序列中包含屏蔽图象.

  在函数中调用了CTreeCtrl::SetImageList和CListCtrl::SetImageList来为树形视图和列表视图设置图象序列.注意列表视图对象m_List设置了两个图象序列,分别用于小图标和大图标显示格式.在插入表项时,只要指定了图象序列的索引,表项就可以显示相应的图象.

  OnHScroll函数负责处理轨道条发出的通知消息,函数根据轨道条的当前位置设置进度条的进度.OnSelchangeCombo函数响应用户对下拉列表式组合框的选择,函数先调用CWnd::GetWindowLong获取列表视图原来的风格,然后调用CWnd::SetWindowLong设置新的风格(注意调用CWnd::GetSafeHwnd可以获得窗口的句柄),最后调用CWnd::Invalidate刷新列表视图.

  Ctrl32程序只是对常用的Win 32控件进行了一些基本的测试。Visual C++提供了一个全面测试Win 32控件的MFC例子cmnctrls(在samples \ mfc \ general \ cmnctrls目录下),有兴趣的读者可以研究一下。

6.3 技术总结

  在上一章和本章中,读者已经接触和使用了各种控件。这些控件虽然五花八门,但它们却具有一些共同的特点。本节的目的就是讨论这些共同点,以使读者能在概念上更好地理解控件。

6.3.1 所有的控件都是窗口

  确切地说,所有的控件都是子窗口。控件窗口都具有WS_CHILD风格,它们总是依附于某一个父窗口。所有MFC的控件类都是基本窗口类CWnd的直接或间接派生类,这意味着可以调用CWnd类的某些成员函数来查询和设置控件。常用于控件的CWnd成员函数在表6.34列出,这些函数对所有的控件均适用。

表6.34 常用于控件的CWnd成员函数

函数名

用途

ShowWindow

调用ShowWindow(SW_SHOW)显示窗口,调用ShowWindow(SW_HIDE)则隐藏窗口.

EnableWindow

调用EnableWindow(TRUE)允许窗口,调用EnableWindow(FALSE)则禁止窗口.一个禁止的窗口呈灰色显示且不能接受用户输入.

DestroyWindow

删除窗口.

MoveWindow

改变窗口的位置和尺寸.

SetFocus

使窗口具有输入焦点.

  例如,如果想把一个编辑框控件隐藏起来,可以用下面这行代码完成.

m_MyEdit.ShowWindow(SW_HIDE);

6.3.2 控件的创建方法

  控件的创建有自动和手工两种常用方法.

  控件的自动创建是通过向对话框模板中添加控件实现的.到目前为止,读者所使用的控件都是用这种方法创建的.当调用对话框类的DoModal和Create显示对话框时,框架会根据对话框模板资源提供的控件信息自动地创建控件.这种方法的优点是方便直观,用户可以在对话框模板编辑器的控件面板中选择控件,可以在对话框模板中调整控件的位置和大小,还可以通过属性对话框设置控件的风格.

  手工创建控件是一种比较专业的方法,包括下面两步:

构建一个控件对象.通常的做法是把控件对象嵌入到父窗口(如对话框)对象中,即以成员变量的形式定义一个控件对象.这样,在构建父窗口对象时,控件对象会被自动构建.程序也可以用new操作符创建控件对象,但要注意MFC的控件对象不具有自动清除的功能,因此需要在关闭父窗口时用delete操作符删除控件对象(参见5.4.2).

调用控件对象的Create成员函数创建控件.一般来说,如果要在对话框中创建控件,那么应该在OnInitDialog函数中调用Create,如果要在非对话框窗口中创建控件,则应该在OnCreate函数中调用Create.

  清单6.3是一个手工创建控件的实例.

清单6.3 控件的手工创建

#define ID_EXTRA_EDIT 100

 

class CMyDialog : public CDialog

{

protected:

CEdit m_edit; // Embedded edit object

public:

virtual BOOL OnInitDialog();

};

. . . . . .

BOOL CMyDialog::OnInitDialog()

{

CDialog::OnInitDialog();

CRect rect(85, 110, 180, 210);

 

m_edit.Create(WS_CHILD | WS_VISIBLE | WS_TABSTOP |

ES_AUTOHSCROLL | WS_BORDER, rect, this, ID_EXTRA_EDIT);

m_edit.SetFocus();

return FALSE;

}

  不难看出,控件的手工创建是在程序中通过控件对象完成的,与对话框模板无关.在Create函数中,需要提供控件的风格,控件的尺寸和位置,控件的ID等信息.手工创建实际上是一种动态创建过程,程序可以在任何时侯根据需要来创建,不一定非要在OnInitDialog中进行.

  控件并不是对话框所专有的,某些非对话框窗口也可以拥有控件.如果要在象工具条或状态栏这样的非对话框窗口中创建控件,就必需用手工创建方法.自动创建由于要依赖对话框模板,因此只适用于对话框.

6.3.3 访问控件的方法

  控件是一种交互的工具,应用程序需要通过某种方法来访问控件以对其进行查询和设置.访问控件有四种方法:

  1. 利用对话框的数据交换功能访问控件.这种方法适用于自动创建的控件.先用ClassWizard为对话框类加入与控件对应的数据成员变量,然后在适当的时侯调用UpdateData,就可以实现对话框和控件的数据交换.这种方法只能交换数据,不能对控件进行全面的查询和设置,而且该方法不是针对某个控件,而是针对所有参与数据交换的控件.另外,对于新型的Win32控件,不能用ClassWizard创建数据成员变量.因此,该方法有较大的局限性.
  2. 通过控件对象来访问控件.控件对象对控件进行了封装,它拥有功能齐全的成员函数,用来查询和设置控件的各种属性.通过控件对象来访问控件无疑是最能发挥控件功能的一种方法,但这要求程序必需创建控件对象并使该对象与某一控件相连.对于自动创建的控件,可利用ClassWizard方便地创建与控件对应的控件对象.对于手工创建的控件,因为控件本身就是通过控件对象创建的,所以不存在这一问题.
  3. 利用CWnd类的一些用于管理控件的成员函数来访问控件.这些函数已在表5.5列出.只要向这些函数提供控件的ID,就可以对该控件进行访问.使用这些函数的好处是无需创建控件对象,就可以对控件的某些常用属性进行查询和设置.该方法对自动和手工创建的控件均适用.
  4. 用CWnd::GetDlgItem访问控件.该函数根据参数说明的控件ID,返回指定控件的一个CWnd型指针,程序可以把该指针强制转换成相应的控件类指针,然后通过该指针来访问控件.该方法对自动和手工创建的控件均适用.在上一章中就曾经使用过这种方法,读者可参见5.3.6.其实该方法与通过控件对象来访问控件的方法在本质上是一样的,在表5.5中亦包括GetDlgItem函数,但为了强调其重要性,这里把它单独列为一种方法.

 

6.3.4 控件及控件对象的删除

  当关闭父窗口时,控件会被自动删除,因此在一般情况下不必操心删除问题.如果由于某种需要想手工删除控件,可以调用CWnd::DestroyWindow来完成.

  对于控件对象的删除,有两种情况.若控件对象是以成员变量的形式创建的,那么该对象将会随着父窗口对象的删除而被删除,因此在程序中无需操心.若控件对象是用new操作符在堆中创建的,则必需在关闭父窗口时用delete操作符删除对象,这是因为所有MFC的控件类都是非自动清除的(参见5.4.2).

6.3.5 控件通知消息

  传统控件和Win32 控件采用了不同的通知消息机制,请参见6.1.1和6.2.1。

6.4 在非对话框窗口中使用控件

  控件并不是对话框独有的,事实上,很多非对话框窗口都可以使用控件.比较典型的应用是在表单视图、工具条和状态栏中使用控件.

6.4.1 在表单视图中使用控件

  MFC提供了一个名为CFormView的特殊视图类,我们称其为表单视图.表单视图是指用控件来输入和输出数据的视图,用户可以方便地在表单视图中使用控件.表单视图具有对话框和滚动视图的特性,它使程序看起来象是一个具有滚动条的对话框.在有些情况下,用表单视图比用普通视图更符合用户的需要,例如,在向数据库输入数据时,显然用表单的形式可以更习惯些.

  用AppWizard可以方便地创建基于表单视图的应用程序,只要在MFC AppWizard对话框的第六步先选择CView,然后在Base class栏中选择CFormView,AppWizard就会创建一个基于CFormView的应用程序.

  读者可以按上述方法建立一个名为Test的应用程序.在Test工程的资源中,读者会发现一个ID为IDD_TEST_DIALOG的对话框模板,该对话框模板可供用户放置和安排控件.在程序运行时,框架根据该对话框模板创建CFormView对象,并根据模板的信息在表单视图中自动创建控件.与设计对话框类相类似,用户可以用ClassWizard为表单视图类加入与控件对应的成员变量,可以调用UpdateData在控件和成员变量之间交换数据,但对控件的初始化工作是在OnInitialUpdate函数而不是在OnInitDialog函数中进行的.

  基于表单视图的应用程序与基于对话框的应用程序都是在应用程序中直接使用控件,但二者有很多不同之处.基于对话框的应用程序是用一个对话框来作为程序的主窗口的,因而程序的主窗口的特性与对话框类似,如窗口的大小不能改变,程序没有菜单条、工具条和状态栏等.基于表单视图的应用程序仍然是基于Doc/View框架结构的(见七、八、九章),只是视图被换成了表单视图,也就是说,应用程序的窗口可以改变大小,程序有菜单条、工具条和状态栏,且程序仍然可以Dov/View运行机制来处理文档.

  表单视图比较简单,这里就不举例说明了.在第十章,读者会看到使用表单视图的例子.

6.4.2 在工具条和状态栏中使用控件

  一个专业的程序常常会在工具条和状态栏中加入一些控件以方便用户的使用.例如,在Developer Studio的工具条中就有不少组合框,而在状态栏中则常常会显示一个进度条来表明工作的进度.

  如果读者想在自己程序的工具条和状态栏中加入控件,则需要掌握一些技巧.在本小节,我们将结合一个具体实例来演示这些技巧.例程的名为CtrlInBar,其界面如图6.8所示.可以看出,该程序在工具条中创建了一个组合框,在状态栏中创建了一个进度条.

图6.8 CtrlInBar程序

  现在让我们开始工作.首先,请读者用AppWizard建立一个名为CtrlInBar的单文挡MFC应用程序,然后,请按清单6.4修改源代码.注意在程序中编写了一个CToolBar类的派生类CMyToolBar,以及一个CStatusBar的派生类CMyStatusBar,这两个类与CMainFrame类在同一模块中.

清单6.4 在工具条和状态栏中创建控件的有关代码

// MainFrm.h : interface of the CMainFrame class

//

/////////////////////////////////////////////////////////////////////////////

#define IDC_MYCOMBO 100

class CMyToolBar : public CToolBar

{

public:

CComboBox m_ComboBox;

BOOL CreateComboBox(int nIndex);

};

 

#define ID_INDICATOR_PROGRESS 100

class CMyStatusBar : public CStatusBar

{

public:

CProgressCtrl m_Progress;

int m_nProgressPane;

BOOL CreateProgressCtrl(int nPane);

 

afx_msg void OnSize(UINT nType, int cx, int cy);

DECLARE_MESSAGE_MAP()

};

 

class CMainFrame : public CFrameWnd

{

 

. . . . . .

protected: // control bar embedded members

CMyStatusBar m_wndStatusBar;

CMyToolBar m_wndToolBar;

 

. . . . . .

};

 

// MainFrm.cpp : implementation of the CMainFrame class

//

 

. . . . . .

static UINT indicators[] =

{

ID_SEPARATOR, // status line indicator

 

ID_SEPARATOR

};

 

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)

{

if (CFrameWnd::OnCreate(lpCreateStruct) == -1)

return -1;

 

if (!m_wndToolBar.Create(this) ||

!m_wndToolBar.LoadToolBar(IDR_MAINFRAME))

{

TRACE0("Failed to create toolbar\n");

return -1; // fail to create

}

 

if (!m_wndStatusBar.Create(this) ||

!m_wndStatusBar.SetIndicators(indicators,

sizeof(indicators)/sizeof(UINT)))

{

TRACE0("Failed to create status bar\n");

return -1; // fail to create

}

 

// TODO: Remove this if you don't want tool tips or a resizeable toolbar

m_wndToolBar.SetBarStyle(m_wndToolBar.GetBarStyle() |

CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC);

 

 

m_wndToolBar.CreateComboBox(0); //在工具条的最左边创建组合框

m_wndStatusBar.CreateProgressCtrl(1); //在第二个窗格创建进度条

 

m_wndToolBar.m_ComboBox.AddString("Item1");

m_wndToolBar.m_ComboBox.AddString("Item2");

m_wndToolBar.m_ComboBox.AddString("Item3");

m_wndToolBar.m_ComboBox.AddString("Item4");

m_wndStatusBar.m_Progress.SetRange(0,200);

m_wndStatusBar.m_Progress.SetPos(100);

 

 

. . . . . .

}

 

 

/////////////////////////////////////////////////////////////////////////////

//CMyToolBar

 

//参数nIndex是按钮的索引,函数将在该按钮的左侧创建组合框

BOOL CMyToolBar::CreateComboBox(int nIndex)

{

if(m_ComboBox.GetSafeHwnd()) //防止重复创建

return FALSE;

 

CToolBarCtrl &ToolBarCtrl=GetToolBarCtrl();

TBBUTTON button;

CRect rect;

button.fsStyle=TBSTYLE_SEP;

ToolBarCtrl.InsertButton(nIndex,&button); //插入空位

ToolBarCtrl.InsertButton(nIndex,&button);

ToolBarCtrl.InsertButton(nIndex,&button);

//设置空位的宽度(处于中间的空位用来容纳组合框)

SetButtonInfo(nIndex+1,IDC_MYCOMBO,TBBS_SEPARATOR,100);

SetButtonInfo(nIndex, ID_SEPARATOR, TBBS_SEPARATOR, 12);

SetButtonInfo(nIndex+2, ID_SEPARATOR, TBBS_SEPARATOR, 12);

GetItemRect(nIndex+1, &rect); //获取中间空位的坐标

rect.top = 3;

rect.bottom = rect.top + 100;

if (!m_ComboBox.Create(CBS_DROPDOWNLIST|WS_VISIBLE|WS_TABSTOP,

rect, this, IDC_MYCOMBO))

return FALSE;

m_ComboBox.SetItemHeight(-1,15); //设置编辑框组件的高度

return TRUE;

}

 

 

/////////////////////////////////////////////////////////////////////////////

//CMyStatusBar

 

 

BEGIN_MESSAGE_MAP(CMyStatusBar, CStatusBar)

ON_WM_SIZE()

END_MESSAGE_MAP()

 

//参数nPane是窗格的索引,函数将在该窗格内创建进度条控件

BOOL CMyStatusBar::CreateProgressCtrl(int nPane)

{

if(m_Progress.GetSafeHwnd()) //防止重复创建

return FALSE;

 

//设置该窗格的宽度为200

SetPaneInfo(nPane,GetItemID(nPane),SBPS_NORMAL,200);

CRect rect(0,0,1,1);

if(!m_Progress.Create(WS_CHILD|WS_VISIBLE,rect,this,

ID_INDICATOR_PROGRESS))

return FALSE;

m_nProgressPane=nPane;

return TRUE;

}

 

void CMyStatusBar::OnSize(UINT nType, int cx, int cy)

{

CStatusBar::OnSize(nType, cx, cy);

if(m_Progress.GetSafeHwnd()==NULL) return;

 

CRect rect;

GetItemRect(m_nProgressPane,&rect);

m_Progress.MoveWindow(rect); //调整控件的位置和尺寸

}

  CMyToolBar类可以在工具条中指定按钮的左边放置一个下拉列表式组合框,并在组合框的两端留出空位.该类的CreateComboBox成员负责创建组合框,参数nIndex是工具条按钮的索引,需注意的是工具条的一个空位也要占有一个索引.在CreateComboBox中,主要调用了下列函数:

调用CToolBar::GetToolBarCtrl返回一个CToolBarCtrl对象.从4.0版开始,CToolBar类是在新控件类CToolBarCtrl类的基础上实现的,后者具有更强大的功能.例如CToolBarCtrl提供了CToolBar没有的InsertButton成员函数.

调用CToolBarCtrl::InsertButton在nIndex索引处插入三个空位.

调用CToolBar::SetButtonInfo设置空位的宽度,其中中间的空位有100像素宽,用来容纳组合框.

调用CToolBar::GetItemRect获得中间空位的坐标.

调用CComboBox::Create函数创建组合框.注意rect对象说明的是包括列表框在内的组合框的尺寸.

调用CComboBox::SetItemHeight设置编辑框组件的高度.

 

  CMyStatusBar类可以在指定的状态栏窗格中放置一个进度条.该类的CreateProgressCtrl成员负责创建进度条,参数nPane是窗格的索引.在该函数中主要调用了下列函数:

调用CStatusBar::SetPaneInfo设置窗格的宽度为200.在调用该函数时,先调用CStatusBar::GetItemID返回窗格的ID.

调用CProgressCtrl::Create创建控件.

  大家可能会奇怪,CProgressCtrl::Create创建的控件只有1×1大小.这是由于在调用该函数创建控件时,状态栏的大小往往并未确定.这时如果调用CStatusBar::GetItemRect,只能得到0坐标,而不能得到正确的窗格坐标,所以程序只好先创建一个1×1的控件.

  工具条中按钮和控件的尺寸及其相对于工具条的位置不会随外界因素发生变化,而状态栏则不同,当用户改变了框架窗口的宽度时,状态栏的宽度也会随之改变,并且它会重新调整各窗格的大小和位置,此时如果不及时调整进度条的坐标,那么进度条与所在窗格之间将发生错位.调整进度条的大小和位置的工作由CMyStatusBar::OnSize函数完成.当窗口的尺寸发生改变后,窗口会收到WM_SIZE消息,OnSize是WM_SIZE消息的处理函数.在CMyStatusBar::OnSize函数中,先调用CStatusBar::GetItemRect获得进度条所在窗格的坐标,然后调用CWnd::MoveWindow来调整进度条控件的坐标.

  在窗口形成时,也会收到WM_SIZE消息,这时OnSize函数可以及时调整进度条的大小和位置.

6.5 设计新的控件类

  虽然Windows提供了大量的控件,但不一定总能满足用户的需要.有时,用户需要一些有特殊功能的控件.例如,有时希望编辑框控件只能接受数字输入,当用户输入非数字字符时,编辑框控件会发出声响来提醒用户.在这种情况下,标准的CEdit类就无能为力了.

  当控件无法满足需要时,用户可以从原来的控件类派生一个新类.通过合理地设计派生类,可以修改控件的行为和属性以达到用户的要求.利用ClassWizard的强大功能,读者可以方便地创建和设计控件类的派生类.

6.5.1 创建标准控件类的派生类

  这个任务可以用ClassWizard完成,其具体步骤如下:

按Ctrl+W进入ClassWizard.

单击Add Class按钮并选择New...菜单项,则打开Create New Class对话框.

在Create New Class对话框的Name栏中输入派生类的名字,在Base class栏中选择一个标准的控件类做为基类,然后按Create按钮即可.

6.5.2 利用MFC的控件通知消息反射机制完善派生类的功能

  创建好派生类后,接下来的任务就是修改新类的代码以完善其功能.例如,为新类添加必要的成员变量,提供新的成员函数以及消息处理函数等等.其中为控件添加消息处理函数是最重要的,因为这直接关系着控件新功能的实现.

  与控件有关的消息包括控件本身接收的消息和发给父窗口的通知消息两种.利用ClassWizard可以方便地为派生类创建这两类消息的处理函数.读者也许会感到奇怪,控件通知消息不是由父窗口处理的吗,难道控件本身也有机会处理通知消息?

  答案是肯定的.从4.0版开始,MFC提供了一种消息反射机制(Message Reflection),可以把控件通知消息反射回控件.具体地讲,当父窗口收到控件通知消息时,如果父窗口有该消息的处理函数,那么就由父窗口处理该消息,如果父窗口不处理该消息,则框架会把该消息反射给控件,这样控件就有机会处理该消息了.由此可见,新的消息反射机制并不破坏原来的通知消息处理机制.

  消息反射机制为控件提供了处理通知消息的机会,这在有些情况下是很有用的.例如,如果派生类想改变控件的背景色,就需要处理WM_CTLCOLOR通知消息.大多数控件在需要重绘时,会向父窗口发送WM_CTLCOLOR消息,父窗口在处理该消息时会返回一个刷子用来画控件的背景.如果按传统的方法,由父窗口来处理这个消息,则加重了控件对象对父窗口的依赖程度,每当使用这样一个新控件时,都要在父窗口中提供控件的WM_CTLCOLOR消息处理函数,这显然违背了面向对象的原则.若由控件自己处理WM_CTLCOLOR消息,则使得控件对象具有更大的独立性,而父窗口也可以省去一些不必要的工作.

  读者可以在自己的程序中用ClassWizard创建一个CEdit类的派生类试试.在派生类的消息列表中,在有些消息前面有一个"="符号,这表明这些消息是可以反射的通知消息.读者可以按照通常的方法创建反射消息的处理函数.

6.5.3 利用SubclassDlgItem函数动态连接控件和控件对象

  要在程序中创建新设计的控件,显然不能用自动创建的办法,因为对话框模板对新控件的特性一无所知.程序可以用手工方法创建控件,在调用派生类的Create函数时,派生类会调用基类的Create函数创建控件.用Create函数创建控件是一件比较麻烦的工作,程序需要为函数指定一大堆的控件风格以及控件的坐标和ID.特别是控件的坐标,没有经验的程序员很难确切地安排控件的位置和大小,往往需要反复调整.利用MFC的CWnd::SubclassDlgItem提供的动态连接功能,可以避免Create函数的许多麻烦,该函数大大简化了在对话框中创建派生控件的过程.

  大家知道,在用手工方法创建控件时,先要构建一个控件对象,然后再用Create函数在屏幕上创建控件窗口,也就是说,控件的创建工作是由控件对象完成的.动态连接的思路则不同,SubclassDlgItem可以把对话框中已有的控件与某个窗口对象动态连接起来,该窗口对象将接管控件的消息处理,从而使控件具有新的特性.SubclassDlgItem函数的声明为

BOOL SubclassDlgItem( UINT nID, CWnd* pParent );

 

  参数nID是控件的ID,pParent是指向父窗口的指针.若连接成功则函数返回TRUE,否则返回FALSE.

  综上所述,要在程序中使用派生控件,应该按下面两步进行:

在对话框模板中放置好基类控件.

在对话框类中嵌入派生控件类的对象.

在OnInitDialog中调用SubclassDlgItem将派生类的控件对象与对话框中的基类控件相连接,则这个基类控件变成了派生控件.

 

  例如,如果要在对话框中使用新设计的编辑框控件,应先在对话框模板中的合适位置放置一个普通的编辑框,然后,在OnInitDialog函数中按下面的方式调用SubclassDlgItem即可:

BOOL CMyDialog::OnInitDialog()

{

CDialog::OnInitDialog();

m_MyEdit.SubclassDlgItem(IDC_MYEDIT, this);

return TRUE;

}

  评论这张
 
阅读(3541)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017