MFC通用控件使用详解

更新时间:2024-06-22 18:57:01 阅读量: 综合文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

PWIN98/95下演练VC5中的COMMON一族 辽宁省铁岭市委办公室(112000)宋立波

中文PWIN98/95之所以深受广大计算机用户的青睐,除其在32位多任务、多线程和系统稳固性等核心技术的重要改进之外,更主要的还是其优秀的用户交互界面。这就给程序设计人员提出了新的课题,如何在自己开发的应用程序中充分利用WIN98/95支持的各种高级通用控制和视,这也是学习VC编程所必须掌握的基本内容,本文将通过实例来具体演练VC5中的这些控件,其中常用的COMMON一族主要成员包括:

1.表头控制 Header Control

2.图像列表控制 Image List

3.列表控制和视 List Control&List View 4.树控制和视 Tree Control&Tree View 5.标签控制和视 Tab Control&Tab View 6.工具条控制 Toolbar Control 7.进度条控制 Progress Control 8.滑动条控制 Slider Control 9.旋转按钮控制 Spin Button Control 10.状态条控制 Status Bar Control

其中,绝大部分通用控制在MFC类库中都存在两种封装形式,即控制类和视类,控制类主要是供直接使用控制而提供的,而视类则是通过间接使用控制而提供的,视类可以更好地把控制类集成到MFC结构中,并且使用视类可以利用引用直接对嵌套在其中的控制进行各种操作。两者在使用方法上没有太大区别,就拿列表控制类和视类来说,当创建列表视后可通过CListCtrl& ctrlList = GetListCtrl()成员函数取得视类引用ctrlList之后,就可以利用列表视的视函数对视进行各项操作(ctrlList.Add等)。

一、PWIN98/95下演练CHeadCtrl

表头控制(CHeaderCtrl)通常应用在窗口中的文本或数据的列表之上。一般为数据列的标题,可以包括多个部分,用户可以拖动每个部分并可以控制每列的宽度。表头控制类提供了普通表头控制的基本方法,只有在WINDOWS95以后版本系统中才提供,其方法包含在afxcmn.h文件中,一般与标签控制(CTabCtrl)和列表控制(CListCtrl)组合使用。

(一)表头控制的对象结构 1、表头控制对象的建立方法

CHeaderCtrl &cheaderCtrl 建立表头控制对象 Create 建立表头并绑定对象

CHeaderCtrl::Create的格式如下:BOOL Create( DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID ); 其返回值非零时初始化成功,否则失败。

参数dwStyle用来确定表头控制类型;rect用来确定表头控制的大小和位置;ParentWnd用来确定表头控制的父窗口;nID用来表示表头控制的标志。

表头控制风格包括:

HDS_BUTTONS 表示表头控制外观类似按钮; HDS_HORZ 表示表头控制为水平排列;

HDS_VERT 表示表头控制为垂直排列; HDS_HIDDEN 表示表头控制为隐藏模式。

它也可以使用普通类控制风格,包括:

CCS_BOTTOM 设置控制位置在父窗口的底部并与父窗口同样宽度; CCS_NODIVIDER 在控制顶部形成两个像素的高亮区; CCS_NOHILITE 在控制顶部形成一个像素的高亮区; CCS_NOMOVEY 在响应WM_SIZE消息时重置大小并水平排列; CCS_NOPARENTALIGN 使控制自动靠近父窗口的顶部或底部;

CCS_NORESIZE 设置初始大小或新值时使控制使用默认宽度和高度;

CCS_TOP 设置在父窗口客户区域的顶部并与父窗口同样宽度;

同样表头控制也可以使用窗口控制风格,包括:

WS_CHILD 建立一个子窗口,不能用于WS_POPUP窗口类型; WS_VISIBLE 建立一个初始时不可见的窗口; WS_DISABLED 建立一个初始时无效的窗口; WS_GROUP 确定可用光标移动的控制群组; WS_TABSTOP 确定可用TAB控制移动站点;

表头控制一般分为两个步骤,首先确定表头控制的数据结构,然后建立表头控制并绑定对象。

2、表头控制的属性

表头控制的属性包括取得表头控制中项目的数量GetItemCount、取得表头控制中某一项目的内容GetItem和设置表头控制中某一项目的内容SetItem。 3、表头控制的操作方法

表头控制的操作方法包括向表头控制中插入一个新项目InsertItem、从表头控制中删除一个项目DeleteItem和绘制表头中给定的项目DrawItem等。 (二)表头控制的数据结构

在使用表头控制时,首先必须建立一个数据结构HD_ITEM,其结构定义如下: typedef struct _HD_ITEM

{ UINT mask; //结构成员有效控制位 int cxy; //表头项目的宽度 LPSTR pszText; //表头项目内容 HBITMAP hbm; //表头项目的位置句柄 int cchTextMax; //表头内容字符串长度 int fmt; //表头项目的格式

LPARAM lParam; //应用程序定义的32位数据

} HD_ITEM;

屏蔽控制位说明了数据结构成员中包含的有效数据,可以是下面标志的组合: HDI_BITMAP hbm成员有效 HDI_FORMAT fmt 成员有效 HDI_LPARAM lParam成员有效

HDI_TEXT pszText 和cchTextMax 成员有效 HDI_WIDTH cxy 成员有效并确定项目宽度值 格式标志位fmt可以是以下标志的组合: HDF_CENTER 表头项目居中 HDF_LEFT 表头项目左对齐 HDF_RIGHT 表头项目右对齐 HDF_BITMAP 表头显示一个位图 HDF_OWNERDRAW 由主窗口自绘表头项目 HDF_STRING 表头项目为一个字符串 (三)表头控制的应用技巧

由于表头控制无法单独使用,其主要是配合列表控制和标签控制,并多以文字表头应用多见,InsertItem、SetItem和GetItem是常用的方法,如在列表控制时利用InsertColumn属性就可以增加一个表列的文本标题,具体用法和技巧见列表控制和标签控制。下面以在列表控制中的增加表列的方法来具体说明:

lvcol.pszText=\品 名\设置第一列表头名 lvcol.iSubItem=i; //表列序号 lvcol.cx=70; //表列宽度

m_ListCtrl.InsertColumn(i++,&lvcol);//插入一个表列 lvcol.pszText=\数 量\设置第二列表头名 lvcol.iSubItem=i; lvcol.cx=70;

m_ListCtrl.InsertColumn(i++,&lvcol);//插入一个表列 ......//其它代码

二、PWIN98/95下演练CImageList

图像列表控制(CImageList)是相同大小图像的一个集合,每个集合中均以0为图像的索引序号基数,图像列表通常由大图标或位图构成,其中包含透明位图模式。可以利用WINDOWS32位应用程序接口函数API来绘制、建立和删除图像,并能实现增加、删除、替换和拖动图像等操作。图像列表控制提供了控制图像列表的基本方法,这些方法在WINDOWS95及以后版本才能实现。 (一)图像控制的对象结构 1、图像控制的数据成员

m_hImageList 连接图像对象的控制句柄 2、图像控制的建立方法

CimageList&imageList建立图像控制对象结构 Create 初始化图像列表并绑定对象 图像控制的建立方法如下:

BOOL Create( int cx, int cy, UINT nFlags, int nInitial, int nGrow ); BOOL Create( UINT nBitmapID, int cx, int nGrow, COLORREF crMask ); BOOL Create( LPCTSTR lpszBitmapID, int cx, int nGrow, COLORREF crMask );

BOOL Create( CImageList& imagelist1, int nImage1, CImageList& imagelist2 ,int nImage2,int dx, int dy );

其中各项参数的含义为:cx定义图像的宽度,单位为象素;cy定义图象的高度,单位为象素;nFlags确定建立图像列表的类型,可以是以下值的组合:ILC_COLOR、ILC_COLOR4、ILC_COLOR8、ILC_COLOR16、ILC_COLOR24、ILC_COLOR32、ILC_COLORDDB和ILC_MASK;nInitial用来确定图像列表包含的图像数量;nGrow用来确定图像列表可控制的图像数量。

NbitmapID 用来确定图像列表联系的位图标志值;crMask表示颜色屏蔽位; LpszBitmapID 用来确定包含位图资源的标识串;

imagelist1 指向图像列表控制对象的一个指针;nImage1图像列表1中包含的图像数 量;imagelist2指向图像列表控制对象的一个指针;nImage2图像列表2中包含的图像数量;dx表示以象素为单位的图像宽度;dy表示以象素为单位的图像高度。

同样,图像控制的建立也包括两个步骤,首先建立图像列表结构,然后建立图像列表控制。 3、图像控制的属性类

图像控制的属性类包括返回m_hImageList.控制句柄GetSafeHandle、取得图像列表中的图像数量GetImageCount、设置图像列表的背景颜色SetBkColor、取得图像列表的背景颜色SetBkColor和取得图像的有关信息SetBkColor。 4、图像控制的操作方法

图像控制的操作方法包括将一个图像列表绑定到一个对象上Attach、将对象上的图像列表解除绑定并返回句柄Detach、删除一个图像列表DeleteImageList、将一个图像增加到图像列表中Add和将一个图像从图像列表中删除Remove等。 (二)图像控制的应用技巧

对于图像控制,同样不能单独使用,必须与列表控制、树控制和标签控制相互结合应用,下面分别介绍其具体应用技巧。

1、图像控制在列表控制中的应用技巧

①设置图像控制CListCtrl::SetImageList的调用格式如下:

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

其返回值是指向前一个图像列表控制的一个指针,如果不存在前一个图像列表则为NULL;其中参数pImageList是指向图像列表的标识,nImageList是图像列表的类型,可以是如下值: LVSIL_NORMAL 用大图标方式进行图像列表; LVSIL_SMALL 用小图标方式进行图像列表; LVSIL_STATE 以图像状态进行图像列表;

②取得图像控制CListCtrl::GetImageList的调用格式如下: CImageList* GetImageList( int nImageList ) const;

其返回值为指向图像列表控制的指针,其中nImageList用来确定取得返回值的图像列表的 值,其取值与设置图像列表函数相同。 ③图像控制在列表控制中的应用示例

CImageList Cil1,Cil2; //定义大小图标像列表

CVCLISTApp *pApp=(CVCLISTApp *)AfxGetApp();//取得列表控制程序 Cil1.Create(32,32,TRUE,2,2); //建立32位图像控制 Cil1.Add(pApp->LoadIcon(IDI_GJ));//增加选中状态图像 Cil1.Add(pApp->LoadIcon(IDI_XS));//增加非选中状态图像 Cil2.Create(16,16,TRUE,2,2); //建立16位图像控制 Cil2.Add(pApp->LoadIcon(IDI_GJ));//增加选中状态图像 Cil2.Add(pApp->LoadIcon(IDI_XS));//增加非选中状态图像 m_ListCtrl.SetImageList(&Cil1,LVSIL_NORMAL);//设置大图标控制 m_ListCtrl.SetImageList(&Cil2,LVSIL_SMALL);//设置小图标控制 2、图像控制在树控制中的应用技巧

①设置图像控制CTreeCtrl::SetImageList的调用格式如下:

CImageList* SetImageList( CImageList * pImageList, int nImageListType ); 其返回值为指向前前一个图像列表的指针,否则为NULL;参数pImageList为指向图像列表的标识,如果pImageList为NULL则所有的图像都将从树控制中被清除;nImageListType为图像列表设置的类型,可以是如下值之一:

TVSIL_NORMAL 设置正常图像列表,其中包括选中和非选中两种图标; TVSIL_STATE 设置图像列表状态,指用户自定义状态; ②取得图像控制CTreeCtrl::GetImageList的调用格式如下: CImageList* GetImageList( UINT nImage );

如果调用成功则返回图像列表控制指针,否则为NULL;nImage为取得返回值的图像列表类型,其取值和取得图像列表控制完全相同。 ③图像控制在树控制中的应用示例

CImageList Cil1,Cil2;//定义大小图标像列表

CVCTREEApp *pApp=(CVCTREEApp *)AfxGetApp();//获取应用程序指针 Cil1.Create(16,16,ILC_COLOR,2,2);//建立图像控制 Cil1.Add(pApp->LoadIcon(IDI_PM));//增加选中状态图像 Cil1.Add(pApp->LoadIcon(IDI_CJ));//增加非选中状态图像

m_TreeCtrl.SetImageList(&Cil1,TVSIL_NORMAL);//设置图像控制列表 然后在树控制的结构定义中进行如下设置:

TCItem.item.iImage=0; //设置未选中图像索引号 TCItem.item.iSelectedImage=1;//设置选中时图像引号

3、图像控制在标签控制中的应用技巧

①设置图像控制CTabCtrl::SetImageList的调用格式 CImageList * SetImageList( CImageList * pImageList );

其返回值为指向前一个图像列表的指针,如果不存在前一个图像列表则为NULL;pImageList为标识TAB控制的图像列表指针。

②取得图像控制CTabCtrl::GetImageList的调用格式 HIMAGELIST GetImageList() const;

其返回值为指向TAB控制的图像列表指针,如果调用不成功则为NULL。 其应用技巧较前两种更加简单,这里不再赘述。 三、PWIN98/95下演练CList (一)列表控制的主要功能

列表控制和视(List Control&View)主要用来以各种方式显示一组数据记录供用户进行各种操作,Windows98/95中资源管理器中的“查看”标签下的“大图标|小图标|列表|详细资源”就是一个非常好的典型应用。列表中的记录可以包括多个数据项,也可以包括表示数据内容的大小图标,用来表示数据记录的各种属性。

列表控制提供了对Windows列表功能操作的基本方法,而使用列表视的视函数可以对列表视进行各种操作,通过调用视成员GetListCtrl获取嵌在列表视内列表控制的引用(GetListCtrl& ctrlList = GetListCtrl()),就可以和列表控制一样进行各种操作。操作一个列表控制和视的基本方法为:创建列表控制;创建列表控制所需要的图像列表;向列表控制添加表列和表项;对列表进行各种控制,主要包括查找、排序、删除、显示方式、排列方式以及各种消息处理功能等;最后撤消列表控制。

对于一个列表控制,其最典型最常用的显示控制方式为:大图标方式(LVS_ICON)、小图标方式(LVS_SMALLICON)、列表显示方式(LVS_LIST)和详细资料(即报告LVS_REPORT)显示方式。这可以通过设置其显示方式属性来实现。要控制列表所在窗口的风格,可通过功能函数

GetWindowLong和SetWindowLong来实现,要控制列表图标的对齐方式,可通过设置列表窗口的风格LVS_ALIGNTOP或LVS_ALIGNLEFT来实现, (二)列表控制的对象结构 1、列表控制的建立方法

CListCtrl&listCtrl 定义列表对象的结构 Create 建立列表控制并绑定对象 列表控制CListCtrl::Create的调用格式如下:

BOOL Create( DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID ); 其中参数dwStyle用来确定列表控制的风格;rect用来确定列表控制的大小和位置;pParentWnd用来确定列表控制的父窗口,通常是一个对话框;nID用来确定列表控制的标识。其中列表控制的风格可以是下列值的组合:

LVS_ALIGNLEFT 用来确定表项的大小图标以左对齐方式显示; LVS_ALIGNTOP 用来确定表项的大小图标以顶对齐方式显示;

LVS_AUTOARRANGE 用来确定表项的大小图标以自动排列方式显示;

LVS_EDITLABELS 设置表项文本可以编辑,父窗口必须设有LVN_ENDLABELEDIT风格; 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 用来确定表项排序时是基于表项文本的降序方式; 2、列表控制的属性类

列表控制的属性类包括取得列表控制的背景色GetBkColor、设置列表控制的背景色

SetBkColor、取得列表控制的图像列表GetImageList、设置列表控制的图像列表SetImageList、取得列表项数目GetItemCount、取得列表控制的属性GetItem、取得与表项相关的数据

GetItemData、设置表项的属性SetItem、设置与表项相关的数值SetItemData、取得相关联的下一个表项GetNextItem、设置列表控制的文本颜色SetTextColor、取得列表控制的文本背景颜色GetTextBkColor、设置表项的最大数目SetItemCount和取得被选中表项的数目GetSelectedCount等。

3、列表控制的操作方法

列表控制的操作方法包括插入一个新的表项InsertItem、删除一个表项DeleteItem、排序表项SortItems、测试列表的位置HitTest、重绘表项RedrawItems、插入一个表列InsertColumn、删除一个表列DeleteColumn、编辑一个表项文本EditLabel和重绘一个表项DrawItem等。 (三)列表控制的数据结构

列表控制中包含两个非常重要的数据结构LV_ITEM和LV_COLUMN。LV_ITEM用于定义列表控制的一个表项,LV_COLUMN用于定义列表控制的一个表列,其定义格式分别为:

typedef struct _LV_ITEM {

UINT mask; //结构成员屏蔽位 int iItem; //表项索引号

int iSubItem; //子表项索引号 UINT state; //表项状态

UINT stateMask; //状态有效性屏蔽位

LPTSTR pszText; //表项名文本 int cchTextMax; //表项名最大长度 int iImage; // 表项图标的索引号 LPARAM lParam; // 与表项相关的32位数 } LV_ITEM;

typedef struct _LV_COLUMN {

UINT mask; //结构成员有效性屏蔽位 int fmt; //表列对齐方式 int cx; //表列的象素宽度 LPTSTR pszText; //表列的表头名 int cchTextMax; //表列名的文本长度 int iSubItem; //与表列关联的子表项索引号

} LV_COLUMN; 其中fmt可以取如下值:

LVCFMT_CENTER 表列居中对齐 LVCFMT_LEFT 表列左对齐 (四)列表控制的应用技巧示例

本文给出具体实例演示列表控制及前面的表头控制和图像列表的应用技巧。步骤如下:

1、通过“FILE->NEW->PROJECTS->MFC AppWizard(EXE)”建立名为VCLIST的工程,在建立过程中选择基于对话框(Dialog based)的应用;将对话框中的默认控件删除,并将所有对话框属性中的Language域设置为Chinese(P.R.C.),以使应用程序支持中文;

2、 建立两个图标IDI_GJ和IDI_XS,用来表示图标的选中和非选中状态,对于每个图标都应

建立32X32和16X16两种大小,以保证程序的需要;

3、在对话框窗口中设计组合框(Group Box),组合框中设置四个无线按钮(Radio)“大图标|小图标|列表|资料”,同时设置排序、删除和关闭三个控制按钮(Button),并在对话框中设置大小合适的列表控制(List Ctrl),其对应标识分别如下: 控制名称 标题名称 标识符号 列表控制 IDC_LISTCTRL 组合框 方式 无线按钮 大图标

IDC_STATIC

IDC_STDICON

小图标 IDC_SMLICON

列 表 IDC_LIST

资 料 IDC_REPORT 按钮 排 序

IDC_SORT

删 除 IDC_DEL 关 闭 IDOK

4、在设置无线按钮时,需要注意的是只有大图标的Group属性为选中状态,而其它无线按钮

的状态均为默认值。

5、选中列表控制控件,选择“VIEW->ClassWizard->Memory Variables”,并利用IDC_ LISTCTRL引入成员变量,其变量类型为:

变量名 种类 变量类型 m_ListCtrl Control ClistCtrl

同时利用“MESSAGES MAP”为各无线按钮和命令按钮增加控制功能。 6、然后在包含文件和代码文件中分别加入如下代码: (1)在VCLISTDlg.h中增加数据结构和定义 typedef struct tagSPS { //定义结构 char szPm[10]; int Lx; char szSl[10]; char szDj[10]; char szJe[10]; } SPS;

int CALLBACK CompareFunc(LPARAM lParam1,LPARAM lParam2,LPARAM lParamSort); (2)在VCLISTDlg.CPP中的起始处增加初始化数据和程序定义 //在文件开始处增加数据结构初始化 SPS Sps[]={//信息

{\红梅\ {\黄梅\ {\绿梅\ {\青梅\ {\白梅\ {\红梅\ {\黄梅\ {\绿梅\ {\青梅\ {\白梅\ CImageList Cil1,Cil2;//大小图像列表

(3)在程序初始化处增加表头、图像和列表控制建立代码 BOOL CVCLISTDlg::OnInitDialog() {CDialog::OnInitDialog(); //......//其它代码

// TODO: Add extra initialization here此处增加代码 LV_ITEM lvitem; LV_COLUMN lvcol;

//品名 //0-GJ 1-XS //数量 //单价 //金额

int i,iPos,iItemNum;

CVCLISTApp *pApp=(CVCLISTApp *)AfxGetApp();//创建图象列表 Cil1.Create(32,32,TRUE,2,2); Cil1.Add(pApp->LoadIcon(IDI_GJ)); Cil1.Add(pApp->LoadIcon(IDI_XS)); Cil2.Create(16,16,TRUE,2,2); Cil2.Add(pApp->LoadIcon(IDI_GJ));

Cil2.Add(pApp->LoadIcon(IDI_XS));//设置图象列表 m_ListCtrl.SetImageList(&Cil1,LVSIL_NORMAL);

m_ListCtrl.SetImageList(&Cil2,LVSIL_SMALL);//向列表控制中添加表列 lvcol.mask=LVCF_FMT|LVCF_SUBITEM|LVCF_TEXT|LVCF_WIDTH; lvcol.fmt=LVCFMT_CENTER;//居中 i=0;

lvcol.pszText=\品 名\ lvcol.iSubItem=i; lvcol.cx=70;

m_ListCtrl.InsertColumn(i++,&lvcol); lvcol.pszText=\数 量\ lvcol.iSubItem=i; lvcol.cx=70;

m_ListCtrl.InsertColumn(i++,&lvcol); lvcol.pszText=\单 价\ lvcol.iSubItem=i; lvcol.cx=70;

m_ListCtrl.InsertColumn(i++,&lvcol); lvcol.pszText=\金 额\ lvcol.iSubItem=i; lvcol.cx=70;

m_ListCtrl.InsertColumn(i++,&lvcol); //向列表控制中添加表项

iItemNum=sizeof(Sps)/sizeof(SPS); for(i=0;i

lvitem.mask=LVIF_TEXT|LVIF_IMAGE|LVIF_PARAM; lvitem.iItem=i; lvitem.iSubItem=0;

lvitem.pszText=Sps[i].szPm; lvitem.iImage=Sps[i].Lx;

}

lvitem.lParam=i;

iPos=m_ListCtrl.InsertItem(&lvitem);//返回表项插入后的索引号 lvitem.mask=LVIF_TEXT; lvitem.iItem=iPos; lvitem.iSubItem=1;

lvitem.pszText=Sps[i].szSl; m_ListCtrl.SetItem(&lvitem); lvitem.iSubItem=2;

lvitem.pszText=Sps[i].szDj; m_ListCtrl.SetItem(&lvitem); lvitem.iSubItem=3;

lvitem.pszText=Sps[i].szJe; m_ListCtrl.SetItem(&lvitem);

CheckRadioButton(IDC_STDICON,IDC_REPORT,IDC_STDICON);

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

(4)完善列表显示方式代码

在利用Classwizard类向导创建各功能按钮显示功能函数之后,必须依次完善这些功能函数的代码,这些功能函数如下:

void CVCLISTDlg::OnStdicon()//设置大图标显示方式

{ // TODO: Add your control notification handler code here LONG lStyle;

lStyle=GetWindowLong(m_ListCtrl.m_hWnd,GWL_STYLE);//获取当前窗口类型 lStyle&=~LVS_TYPEMASK; //清除显示方式位 lStyle|=LVS_ICON; //设置显示方式

SetWindowLong(m_ListCtrl.m_hWnd,GWL_STYLE,lStyle);//设置窗口类型 }

void CVCLISTDlg::OnSmlicon() //设置小图标显示方式

{ // TODO: Add your control notification handler code here LONG lStyle;

lStyle=GetWindowLong(m_ListCtrl.m_hWnd,GWL_STYLE);//获取当前窗口类型 lStyle&=~LVS_TYPEMASK; //清除显示方式位 lStyle|=LVS_SMALLICON; //设置显示方式

SetWindowLong(m_ListCtrl.m_hWnd,GWL_STYLE,lStyle);//设置窗口类型 }

void CVCLISTDlg::OnList() //设置列表显示方式

{ // TODO: Add your control notification handler code here LONG lStyle;

lStyle=GetWindowLong(m_ListCtrl.m_hWnd,GWL_STYLE);//获取当前窗口类型 lStyle&=~LVS_TYPEMASK; //清除显示方式位 lStyle|=LVS_LIST; //设置显示方式

SetWindowLong(m_ListCtrl.m_hWnd,GWL_STYLE,lStyle);//设置窗口类型 }

void CVCLISTDlg::OnReport() //详细资料显示方式

{ // TODO: Add your control notification handler code here LONG lStyle;

lStyle=GetWindowLong(m_ListCtrl.m_hWnd,GWL_STYLE);//获取当前窗口类型 lStyle&=~LVS_TYPEMASK; //清除显示方式位 lStyle|=LVS_REPORT; //设置显示方式

SetWindowLong(m_ListCtrl.m_hWnd,GWL_STYLE,lStyle);//设置窗口类型 }

(5)删除功能的实现

要实现删除功能,必须取得选中表项的数和表项总数,并且需要从后向前进行依次删除,其原因是每个表项被删除后,其后各表项的索引号均会发生递减变化,如果采取从前向后删除的方法,就会造成无法正常删除选中的表项,其功能代码如下: void CVCLISTDlg::OnDel() //删除按钮功能

{ // TODO: Add your control notification handler code here int i,iState;

int nItemSelected=m_ListCtrl.GetSelectedCount();//所选表项数 int nItemCount=m_ListCtrl.GetItemCount();//表项总数 if(nItemSelected<1) return; for(i=nItemCount-1;i>=0;i--){ } }

(6)排序功能的实现

列表控制有一个特殊的功能,当以详细资料方式显示时,列表顶部的表头可以当作按钮来使用,这可以通过列表控制创建时的风格来控制。当鼠标点击列表头名称时,列表控制就会向其父窗口发送一个LNV_COLUMNCLICK消息,利用类导向中列表控制IDC_LISTCTRL对应的LNV_COLUMNCLICK消息加入相应处理函数,就可将表列按照特定顺序进行排列。其函数使用方法见程序,其中iSort为排序的表列索引号,(PFNLVCOMPARE)CompareFunc为进行具体排序的回调函数,也就是说,通过鼠标点击表头实现的排序过程是由第三方开发的专用排序函数来实现的,排序函数只是实现表项的

iState=m_ListCtrl.GetItemState(i,LVIS_SELECTED); if(iState!=0) m_ListCtrl.DeleteItem(i);

具体比较操作,而整个排序过程是由SortItemS属性通过不断调用这个函数来实现的。正常的排序过程是升序方式,通过调换排序函数中的参数值,就可实现降序排列,即将PARAM1与PARAM2调换位置。这个回调函数的前两个参数为表列中表项的索引号,第三个参数为排序的表列索引号。 void CVCLISTDlg::OnColumnclickListctrl(NMHDR* pNMHDR, LRESULT* pResult) { //鼠标左键单击表头处理函数

NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;

// TODO: Add your control notification handler code here static int iSorted=-1;//排列序号

if (pNMListView->iSubItem==iSorted) return; iSorted=pNMListView->iSubItem;

m_ListCtrl.SortItems((PFNLVCOMPARE)CompareFunc,iSorted); *pResult = 0; }

//排序时比较表项的回调函数

int CALLBACK CompareFunc(LPARAM lParam1, LPARAM lParam2,LPARAM lParamSort) { char *text1,*text2; switch (lParamSort){

case 0L:text1=Sps[lParam1].szPm; text2=Sps[lParam2].szPm;break; case 1L:text1=Sps[lParam1].szSl; text2=Sps[lParam2].szSl;break; case 2L:text1=Sps[lParam1].szDj; text2=Sps[lParam2].szDj;break; case 3L:text1=Sps[lParam1].szJe; text2=Sps[lParam2].szJe;break; }

return (strcmp(text1,text2));//结果为>0 =0 <0 }

同样,也可以通过专用按钮来实现排序功能,如本文的排序按钮对应的功能代码如下: void CVCLISTDlg::OnSort()

{ // TODO: Add your control notification handler code here m_ListCtrl.SortItems((PFNLVCOMPARE)CompareFunc,0);} 7、列表视的演练技巧

在使用列表视时,其方法与列表控制基本相同,只不过列表视是在窗口中来实现的而列表控制是在对话框中实现,列表视的各种功能是通过菜单来实现的而列表控制是通过按钮等方式来实现的,列表控制需要在对话框中创建列表控制控件而列表视直接占据整个窗口,在设计过程中只要将按钮和列表控制设计过程变为菜单设计,并注意在功能增加是在类向导中是通过菜单命令来

操作,同时在每个功能函数前面增加取得列表视引用的命令( CListCtrl& ListCtrl =

GetListCtrl()),而其余数据结构和代码均不需要修改,实现起来比较容易。

笔者实现的列表控制和视程序的运行结果如下:

列表控制演练示例结果

列表视演练示例结果 四、PWIN98/95下演练CTree (一)树控制的主要功能

树控制和视(Tree Control&View)主要用来显示具有一定层次结构的数据项,如资源管理器中的磁盘目录等,以供用户在其中进行各种选择。树控制中的每个数据项包括数据项名称的文本字符串和用于表示该数据项的图像,每个数据项下面均可包含各种子项,整个结构就象目录树一样。对于包含各种子项的数据项,可通过鼠标双击来展开或合拢,这可以通过控制树的不同风格来实现树控制的不同显示形态。这些风格主要包括:

TVS_HASLINES表示用连线来连接父项和它下面的各个子项,这可以使树的显示层次结构更加清晰,但在无父项的各子项之间并没有连线;

TVS_LINESATROOT表示在无父项的各子项即根下面的各子项之间存在连线;

TVS_HASBUTTONS表示在带有子项的父项前面增加一个带“+”或“-”的按钮,这使得用户也可以通过单击这个小按钮来实现子项的展开和合拢,当存在子项时,按钮的初始状态为“+”,当子项被展开时,按小按钮由“+”变为“-”号,当子项合拢时,小按钮由“-”变为“+”号,这一风格同样对于根项无效,如果需要可通过组合TVS_LINESATROOT风格来实现; TVS_EDITLABELS表示允许让用户单击具有输入焦点的数据项来修改其名称。

对于树控制,MFC中也以两种形式来封装,即树控制(CTREECTRL)和树视(CTREEVIEW),来满足用户的不同需求,对于一般要求的用户如在对话框中应用,使用树控制比较方便,而对于具有较高要求的用户,在使用树视时还具有视窗口的各种方便特性,可以更好地满足文档/视结构的要求。当在窗口中使用树视时,树视会占满两个窗口的客户区域并自动随窗口的框架结构的调整而调整,并能够很好地处理诸如菜单、加速键和工具条中的各种命令消息。在使用树视时只要利用其成员函数CtreeView取得其一个引用,就可以象树控制一样方便地应用:CtreeCtrl &treeCtrl = GetTreeCtrl()。

(二)树控制的对象结构 1、树控制的建立方法

CtreeCtrl&treeCtrl 建立树控制对象结构 Create 建立树控制并绑定对象 树控制CTreeCtrl::Create的调用格式如下:

BOOL Create( DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID ); 其中参数dwStyle用来确定树控制的类型;rect用来确定树控制的大小和位置;pParentWnd用来确定树控制的父窗口,通用是一个对话框并且不能为NULL;nID用来确定树控制的标识。树控制的风格可以是下列值的组合:

TVS_HASLINES 表示树控制在各子项之间存在连线; TVS_LINESATROOT 表示树控制在根项之间存在连线;

TVS_HASBUTTONS 表示树控制视在父项左侧存在展开合拢控制按钮; TVS_EDITLABELS 表示可以控制鼠标单击修改树项的名称;

TVS_SHOWSELALWAYS 表示选中项即使在窗口失去输入焦点时仍然保持选中状态; TVS_DISABLEDRAGDROP表示禁止树控制发送TVN_BEGINDRAG消息 2、树控制的属性类

树控制属性类包括取得树控制中项数GetCount、取得树控制中项相对于父项的偏移值GetIndent、取得树控制图像列表控制句柄GetImageList、设置树控制图像列表控制句柄

SetImageList、取得匹配下一个树项GetNextItem、判断给定树项是否包含子项ItemHasChildren、取得树项子项GetChildItem、取得下一个同属树项GetNextSiblingItem、取得前一个同属树项GetPrevSiblingItem、取得父树项GetParentItem、取得第一个可视树项GetFirstVisibleItem、取得下一个可视树项GetNextVisible Item、取得前一个可视的树项GetPrevVisibleItem、取得被选中的树项GetSelectedItem、取得根树项GetRootItem、取得树项的属性GetItem、设置树项的属性SetItem、取得树项的状态GetItemState、设置树项的状态SetItemState、取得与树项关联图

像GetItemImage、设置与树项关联图像SetItemImage、取得树项文本GetItemText、设置树项文本SetItemText和取得树项编辑控制句柄GetEditControl等。 3、树控制的操作方法

树控制的操作方法包括插入一个树项InsertItem、删除一个树项DeleteItem、删除所有树项DeleteAllItems、展开或合拢树项的子项Expand、选中特定树项SelectItem、选择一个树项作为第一个可视树项SelectSetFirstVisible、编辑一个可视的树项EditLabel和排序给定父树项的子树项SortChildren等。 (三)树控制的数据结构

在使用树控制时需要了解两个个非常重要的数据结构TV_ITEM和TV_INSERTSTRUCT,前一个数据结构是用来表示树控制的树项信息,后一个数据结构是用来定义将树项增加到数据控制中所需要的数据内容。另外,还需要NM_TREEVIEW、TV_DISPINFO和TV_HITTESTINFO三个数据结构,这几个数据结构的定义方法如下: ①基本数据项结构

typedef struct _TV_ITEM {

UINT mask; //结构成员有效性屏蔽位 HTREEITEM hItem; //数据项控制句柄 UINT state; //数据项状态 UINT stateMask; //状态有效性屏蔽位 LPSTR pszText; //数据项名称字符串 int cchTextMax; //数据项名称的最大长度 int iImage; //数据项图标索引号 int iSelectedImage;//选中数据项图标索引号 int cChildren; //子项标识

LPARAM lParam; //程序定义的32位数据 } TV_ITEM, FAR *LPTV_ITEM; ②插入树项结构

typedef struct _TV_INSER TSTRUCT { HTREEITEM hParent; //父项控制句柄 HTREEITEM hInsertAfter; //插入树项的位置 TV_ITEM item; //数据项的结构 } TV_INSERTSTRUCT, FAR *LPTV_INSERTSTRUCT;

其中插入的位置如果是TVI_FIRST 或TVI_LAST ,则分别插入到树控制的最前面或最后面,如果是TVI_SORT ,则插入的树项自动插入到合适的位置。 ③树控制通知消息结构

typedef struct _NM_TREEVIEW { NMHDR hdr; //通知消息句柄 UINT action; //通知消息标志

TV_ITEM itemOld; //原来的数据结构 TV_ITEM itemNew; //新的数据结构 POINT ptDrag; //拖动指针 } NM_TREEVIEW; ④取得或设置数据结构

typedef struct _TV_DISPINFO { tvdi NMHDR hdr; //通知消息控制句柄 TV_ITEM item; //数据项结构 } TV_DISPINFO; ⑤指针测试数据结构

typedef struct _TVHITTESTINFO { tvhtst POINT pt; //客户区域屏幕坐标指针 UINT flags; //存放测试结果的变量 HTREEITEM hItem; //测试的数据项结构 } TV_HITTESTINFO, FAR *LPTV_HITTESTINFO; 其中flags测试结果可以是如下值: TVHT_ABOVE 在客户区域上面 TVHT_BELOW 在客户区域下面

TVHT_NOWHERE 在客户区域中并在最后一项下面 TVHT_ONITEM 在与树项关联的位图或标签内 TVHT_ONITEMBUTTON 在与树项关联的按钮上 TVHT_ONITEMICON 在与树项关联的位图上 TVHT_ONITEMINDENT 在与树项关联的联线上 TVHT_ONITEMLABEL 在与树项关联的标签上 TVHT_ONITEMRIGHT 在树项的右侧区域中 TVHT_ONITEMSTATEICON 在用户定义的状态图标上 TVHT_TOLEFT 在客户区域的左侧 TVHT_TORIGHT 在客户区域的右侧 (四)树控制的应用技巧示例

这里仍以基于对话框演示实例来具体介绍树控制及其和图像列表相结构的应用技巧: 通过“FILE->NEW->PROJECTS->MFC AppWizard(EXE)”建立名为VCTREE的工程,在建立过程中选择基于对话框(Dialog based)的应用;将对话框中的默认控件删除,并将所有对话框属性中的Language域设置为Chinese(P.R.C.),以使应用程序支持中文;建立两个图标IDI_PM和IDI_CJ,用来表示图标的选中和非选中状态,对于每个图标都应建立32X32和16X16两种大小,以保证程序的需要;在对话框窗口中添加树控制对象(TREE CONTROL),并设置五个按钮“增加|删除|查看|排序|关闭”,其对应标识分别如下:

控制名称 标题名称 标识符号

树控制 IDC_TREECTRL 按钮 增 加 IDC_ADD 删 除 IDC_DEL 查 看 IDC_VIEW 排 序 IDC_SORT 关 闭 IDOK

5、选中树控制控件,选择“VIEW->ClassWizard->Memory Variables。骺刂艻DC_TREECTRL 引入成员变量,其变量类型为:

变量名 种类 变量类型 m_TreeCtrl Control CTreeCtrl

同时利用“MESSAGES MAP”为各命令按钮增加控制功能函数。 6、然后在代码文件VCTREEDlg.CPP中分别加入如下控制代码: (1)在文件开始处增加图像列表定义 CImageList Cil1,Cil2;//大小图标像列表 (2)在初始化文件开始处增加代码 BOOL CVCTREEDlg::OnInitDialog() { CDialog::OnInitDialog(); ......//原来其它代码

// TODO: Add extra initialization here // 此处开始增加代码

CVCTREEApp *pApp=(CVCTREEApp *)AfxGetApp();//创建图象列表 Cil1.Create(16,16,ILC_COLOR,2,2); Cil1.Add(pApp->LoadIcon(IDI_PM)); Cil1.Add(pApp->LoadIcon(IDI_CJ));

m_TreeCtrl.SetImageList(&Cil1,TVSIL_NORMAL); //设置图象列表

DWORD dwStyles=GetWindowLong(m_TreeCtrl.m_hWnd,GWL_STYLE);//获取树控制原风格 dwStyles|=TVS_EDITLABELS|TVS_HASBUTTONS|TVS_HASLINES|TVS_LINESATROOT; SetWindowLong(m_TreeCtrl.m_hWnd,GWL_STYLE,dwStyles);//设置风格

char * CJ[4]={\玉溪卷烟厂\云南卷烟厂\沈阳卷烟厂\成都卷烟厂\根数据名称 char * PM[4][5]={

{\红梅一\红梅二\红梅三\红梅四\红梅五\产品数据项 {\白梅一\白梅二\白梅三\白梅四\白梅五\{\绿梅一\绿梅二\绿梅三\绿梅四\绿梅五\{\青梅一\青梅二\青梅三\青梅四\青梅五\

int i,j;

HTREEITEM hRoot,hCur;//树控制项目句柄 TV_INSERTSTRUCT TCItem;//插入数据项数据结构

TCItem.hParent=TVI_ROOT;//增加根项

TCItem.hInsertAfter=TVI_LAST;//在最后项之后

TCItem.item.mask=TVIF_TEXT|TVIF_PARAM|TVIF_IMAGE|TVIF_SELECTEDIMAGE;//设屏蔽 TCItem.item.pszText=\数据选择\ TCItem.item.lParam=0;//序号 TCItem.item.iImage=0;//正常图标

TCItem.item.iSelectedImage=1;//选中时图标

hRoot=m_TreeCtrl.InsertItem(&TCItem);//返回根项句柄 for(i=0;i<4;i++){//增加各厂家

TCItem.hParent=hRoot; TCItem.item.pszText=CJ[i];

TCItem.item.lParam=(i+1)*10;//子项序号 hCur=m_TreeCtrl.InsertItem(&TCItem); for(j=0;j<5;j++){//增加各产品 }

TCItem.hParent=hCur;

TCItem.item.pszText=PM[i][j];

TCItem.item.lParam=(i+1)*10+(j+1);//子项序号 m_TreeCtrl.InsertItem(&TCItem);

m_TreeCtrl.Expand(hCur,TVE_EXPAND);//展开树 }

m_TreeCtrl.Expand(hRoot,TVE_EXPAND);//展开上一级树

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

(3)增加树项功能的实现

在增加树项功能时,除了需要定义和设置插入树项的数据结构之外,还需要注意的是新增树项的名称初始时均为“新增数据”,增加后允许用户给数据项设置自定义名称。在编程时应特别注意m_TreeCtrl.EditLabel(hInsert);后面不能跟任何其它程序命令,否则这条编辑指令无效。 void CVCTREEDlg::OnAdd() { //增加子项功能函数

HTREEITEM hSel=m_TreeCtrl.GetSelectedItem();//取得选择项句柄 if(hSel==NULL) return;//无任何选项则返回 static int nAddNo=100;//编号大于100为新增数据 TV_INSERTSTRUCT TCItem;//定义插入项数据结构 TCItem.hParent=hSel; //设置父项句柄 TCItem.hInsertAfter=TVI_LAST;//在最后增加

TCItem.item.mask=TVIF_TEXT|TVIF_PARAM|TVIF_IMAGE|TVIF_SELECTEDIMAGE;//设屏蔽

TCItem.item.pszText=\新增数据\

TCItem.item.lParam=nAddNo++;//索引号增加 TCItem.item.iImage=0;//正常图标

TCItem.item.iSelectedImage=1;//选中时图标

HTREEITEM hInsert=m_TreeCtrl.InsertItem(&TCItem);//增加 m_TreeCtrl.Expand(hSel,TVE_EXPAND);

m_TreeCtrl.EditLabel(hInsert);//修改增加的数据 }

(4)删除树项功能的实现

在实现删除功能时,应对存在子项的树项进行提示,以警告用户是否连同其子项一起删除。 void CVCTREEDlg::OnDel() { //删除子项功能函数

HTREEITEM hSel=m_TreeCtrl.GetSelectedItem();//取得选项句柄; if(hSel==NULL) return;//无任何选项则返回

if(m_TreeCtrl.ItemHasChildren(hSel))//判断是否有子项

if(MessageBox(\厂家下存在品名,一同删除?\警告\ }

(5)排序功能的实现

排序功能是对所选中的树项的所有子项按字符中顺序进行排序,如果想要按照其它规则进行排序,应利用SortChildrenItemBC()函数进行自行开发排序程序,这个自行开发的函数与列表控制中实现的函数基本相同,可兴趣的读可以试验。 void CVCTREEDlg::OnSort() { //排序子项功能函数

HTREEITEM hSel=m_TreeCtrl.GetSelectedItem();//取得选项句柄; if(hSel==NULL) return;//无任何选项则返回 m_TreeCtrl.SortChildren(hSel); }

(6)查看功能的实现

查看功能用来查看选中树项的有关信息,函数中中显示了树项的文本名称和标识号,可以将这两个信息作为查找关键字,来查看其它更详细的信息。 void CVCTREEDlg::OnView() { //查看选中项功能函数

HTREEITEM hSel=m_TreeCtrl.GetSelectedItem();//取得选项句柄; if(hSel==NULL) return;//无任何选项则返回

CString cText=m_TreeCtrl.GetItemText(hSel);//取得数据项名 LONG IDs=m_TreeCtrl.GetItemData(hSel);//取得数据项序号

m_TreeCtrl.DeleteItem(hSel);

char temp[100];

wsprintf(temp,\厂家:%s 编号:d\ MessageBox(temp,\选择信息\ }

(7)修改功能的实现

如果不进行其它处理,当修改树项的文本名称后,就会发现其未被修改,这是因为程序中没有对修改结果进行保存处理,这就要利用TV_DISPINFO结构和SetItemText函数对TVN_ENDLABELEDIT进行处理,这样就可以正确地实现修改功能。

void CVCTREEDlg::OnEndlabeleditTree(NMHDR* pNMHDR, LRESULT* pResult) { TV_DISPINFO* pTVDispInfo = (TV_DISPINFO*)pNMHDR;

// TODO: Add your control notification handler code here

if(pTVDispInfo->item.pszText==0) return;//用户取消修改操作 m_TreeCtrl.SetItemText(pTVDispInfo->item.hItem, }

7、树视的演练技巧

树视的应用技巧在使用树视时,其方法与树控制基本相同,只不过树视是在窗口中来实现的而树控制是在对话框中实现,树视的各种功能是通过菜单来实现的而树控制是通过按钮等方式来实现的,树控制需要在对话框中创建树控制控件而树视直接占据整个窗口,在设计过程中只要将按钮和树控制设计过程变为菜单设计,并注意在功能函数是在类向导中是通过菜单命令来操作,同时在每个功能函数前面增加取得列表视引用的命令(CTreeCtrl& TreeCtrl = GetTreeCtrl()),而其余数据结构和代码均不需要修改,实现起来比较容易。笔者实现的树控制和视程序的运行结果如下:

pTVDispInfo->item.pszText);//设置新数据 *pResult = 0;

树控制的演练示例结果

树视演练结果示例 五、PWIN98/95下演练CTab (一)标签控制的主要功能

标签控制(Tab Control)是用来在一个窗口如对话框等中的同一用户区域控制多组显示信息或控制信息,由顶部的一组标签来控制不同的信息提示,标签即可以是文本说明也可以是一个代表文本含义的图标,或是两者的组合。针对不同的选择标签,都会有一组提示信息或控制信息与之相对应,供用户进行交互操作,这在WINDOWS98的属性表中最常见。另外还存在一种特殊风格的标签,即TBS_BUTTONS风格的标签,这种标签外观类似按钮,通过鼠标点击改变状态,一般用来执行一些功能而不是用来显示或控制信息。

提到标签,最快想到的应该是属性表对话(Property Sheet),这两者的配合应用更是随处可见。属性表对话框有时也称为多页对话框(Multiple-Page Dialog)或是标签对话框(Table Dialog),最多可设置24个属性页(Property Page),通过顶部的标签来选择不同的属性页。另外还有一种特殊的属性表对话框,就象VC++5.0中的类向导AppWizard一样,其不存在供用户选择的标签,而是按照顺序依次控制属性页的显示,并且还有一般属性页中不存在的“确认”、“上一步”、“下一步”、“完成”和“帮助”等按钮。

标签控制在MFC中只存在一种封装形式,即控制类CtabCtrl。在使用标签时即可以在对话框中直接添加,也可以在窗口中作为子窗口来使用,只不过这样应用时需要选创建标签。 (二)标签控制的对象结构 1、标签控制的建立方法

CTabCtrl&tabCtrl 建立标签控制对象结构 Create 建立标签控制并绑定对象 标签控制CTabCtrl::Create的调用格式如下:

BOOL Create( DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID ); 其中参数dwStyle用来确定标签控制的风格;rect用来控制标签的大小和位置;pParentWnd用

来确定标签控制的父窗口句柄;nID用来确定标签控制的标识符。 标签控制的风格可以是如下值的组合:

TCS_BUTTONS 表示将标签外观定义成类似按钮 TCS_FIXEDWIDTH 使所有标签具有相同的宽度 TCS_FOCUSNEVER 使特定标签永远不接收输入焦点

TCS_FOCUSONBUTTONDOWN 当标签被鼠标点击时接收输入焦点,其仅与TCS_BUTTONS合用 TCS_FORCEICONLEFT 强制图标在左面,剩余部分使标签居中 TCS_FORCELABELLEFT 使图标和标签均左对齐 TCS_MULTILINE 允许标签控制显示多行标签 TCS_OWNERDRAWFIXED 允许父窗口自绘标签 TCS_RIGHTJUSTIFY 使标签右对齐

TCS_SHAREIMAGELISTS 当控制被撤消时标签控制的图像不被撤消 TCS_TOOLTIPS 允许标签控制存在工具提示控制 TCS_TABS 标签正常显示,为默认状态 TCS_SINGLELINE 将标签只显示在一行上,默认状态 TCS_RAGGEDRIGHT 不使标签自动填满控制区域,默认状态 同样,标签控制还可以使用窗口的一些控制风格:

WS_CHILD 为标签控制创建子窗口,只能与WS_POPUP风格一起使用 WS_VISIBLE 建立一个初始可视的标签控制 WS_DISABLED 建立一个初始不可视的标签控制 WS_GROUP 建立标签控制群组的第一个控制 WS_TABSTOP 建立可用TAB键移动的标签控制 2、标签控制的属性类

标签控制的属性类包括取得与标签控制相关联的图像列表GetImageList、设置标签控制的图像列表SetImageList、取得标签控制中标签的总数GetItemCount、取得标签控制中特定标答的相关信息GetItem、设置标签的部分或全部属性SetItem、检测当前被选中的标签GetCurSel、将一个标签设置为选中状态SetCurSel和取得具有当前输入焦点的标签SetCurSel等。 3、标签控制的操作方法

标签控制的操作方法包括在标签控制中插入一个标签InsertItem、删除一个标签 DeleteItem、从标签控制中删除所有项目DeleteAllItems、从标签控制中删除一个图像列表RemoveImage和绘制标签控制中的特定一项DrawItem等。 (三)标签控制的数据结构

在使用标签控制时,必须使用的函数就是在标签控制中插入标签。函数InsertItem的原形如下:

BOOL InsertItem(int nItem,TC_ITEM * pTabCtrlItem);

该函数中的TC_ITEM为添加标签时所使用信息的数据结构,其数据成员的定义方法及含义如下:

typedef struct _TC_ITEM {

UINT mask; // 确定结构成员的屏蔽或设置位 UINT lpReserved1; // 保留未用 UINT lpReserved2; // 保留未用 LPSTR pszText; // 标签名称字符串

int cchTextMax; // 标签名称字符串缓冲区大小 int iImage; // 标签控制的图像索引号 LPARAM lParam; // 应用程序定义的相关32位数据 } TC_ITEM;

当鼠标点击标签控制中的标签时,标签控制就会向其父窗口发送相关的通知消息,通过处理这些通知消息,程序可以实现各种功能。 (四)属性表和属性页的基本用法

在标签控制过程中,属性表对话框和属性页是必不可少的。在MFC类库中,属性表对话框类CpropertySheet是由CWnd类派生而来的,而属性页类CpropertyPage是由Cdialog类派生而来的,它们的用法基本相同:

1、创建所有的属性页。创建属性页的方法与创建一般对话框资源的方法一样,利用对话框编辑器可以为每个属性页创建一个对话框模板,其区别在于,当利用类向导ClassWizard为属性页生成类时应选择属性页类CpropertyPage作为基类,而不是将一般的对话框类Cdialog作为基类; 2、创建属性表对话框,并将事先创建好的各属性页添加进去,两者的创建顺序可以互换,但在创建完之后将属性页添加到属性表对话框中去这一步是必须要做的;

3、显示属性表对话框。虽然属性表对话框类CpropertySheet不是由对话框类Cdialog派生而来的,但两者的操作非常类似,调用DoModal()函数就会显示一个模态属性表对话框,而调用Create()操作就会显示一个非模态的属性表对话框;

4、对数据交换的处理。和对话框类似,属性表对话框与对象之间的数据交换也是通过数据成员2来实现的,只是属性表本身不带数据成员,而实际进行数据交换的是属性页中的数据成员; 5、对向导对话框的处理。如果要显示一个向导对话框,在显示之前应首先调用SetWizardMode()函数对向导对话框进行特殊处理,对于存在按钮的向导对话框,还应调用SetWizardButtons()来对向导对话框的按钮功能进行定制,在用户操作结束时还应调用SetFinishText()函数将“完成”按钮设置为有效状态。 (五)标签控制的应用技巧示例程序

本文给出一个基于文档的标签应用实例。实例程序中通过简单设置菜单、标签和属性表来演示标签控制的实际应用技巧,程序通过选择菜单选项弹出设置正文颜色、字体和修饰等属性表对话框来和用户进行简单交互。其实现步骤如下:

1、利用应用程序向导AppWizard创建一个基于文档的工程TAB,在选择工程类型时应选择单文档;

2、利用资源中的菜单生成器,删除无用菜单,并增加如下菜单结构 菜单名 标识符

设置(S) (弹出菜单名) 背景设置(B) IDM_BKGRND 前景设置(F) IDM_FRGRND

3、利用对话框设计器设置属性表对话框所需要的四个属性页,注意在选择基类时应将属性 页类CpropertyPage作为基类,并将对话框及菜单等控件的所有属均改为中文。四个属性页及其包括的控件内容分别为:(1)文字属性对话框包括一个输入文字的文本输入框,用于输入和修改在窗口上显示的文字;(2)字体属性对话框包括三个选中框,用来确定显示的字体修饰;(3)字间距属性对话框包括一个用于显示提示信息的标签和用于输入字间距大小的文本输入框;(4)颜色属性对话框包括一个成组框和三个单选圆钮;(5)窗口中设置一个用于显示输入文字的标签。 以上控制的设置参数如下:

控制名称 标题名称 标识符串 标签控制 IDC_TABCTRL 表态文本 字间距(10-100) IDC_STATIC1 编辑框 IDC_LIST 成组框 颜色 IDC_STATIC2 单选按钮 黑色 IDC_BLACK 红色 IDC_RED 蓝色 IDC_BLUE 文本框(编辑框) IDC_TEXT 设置字体(复选按钮)粗体 IDC_BOLD 斜体 IDC_ITALIC 下划线 IDC_UNDERLINE 按 钮 确认 IDOK 取消 IDCANCEL

利用类向导ClassWizard在属性表对话框CtabDlg、属性页对话框CtextPage和CstylePage中分别加入如下数据成员:

标识符串 类型 数据成员

IDC_TABCTRL CtabCtrl m_tabCtrl IDC_DIST int m_nDist IDC_BLACK int m_nColor IDC_TEXT Cstring m_cText IDC_BOLD BOOL m_bBold IDC_ITALIC BOOL m_bItalic IDC_UNDERLINE BOOL m_bUnderline

以上数据成员也可以在TABDlg.h、StylePage.h和TextPage.h中利用手工方法增加。 4、将要显示的数据成员加入到视类中去,来和对话框之间进行数据交换,并且将其在初始化函数中进行数据初始化。

(1)在TabView.h中增加如下代码: #include \#include \#include \class CTabView : public CView {public:

int nDist;//数值 int nColor;//颜色

CString cText;//中文字符串

BOOL bBold,bItalic,bUnderline;//字体属性 }

(2)在TabView.cpp中对数据成员进行如下初始化。 CTabView::CTabView() { nDist=20; nColor=1;

cText=CString(\标签控制演示实例\ bBold=bItalic=bUnderline=FALSE; }

(3)在TabDlg.cpp中向控制中增加标签,来实现背景设置功能。 BOOL CTabDlg::OnInitDialog() { CDialog::OnInitDialog(); TC_ITEM tcItem;//添加标签 tcItem.mask=TCIF_TEXT; tcItem.pszText=\字 间 距\ m_tabCtrl.InsertItem(0,&tcItem); tcItem.pszText=\颜色设置\ m_tabCtrl.InsertItem(1,&tcItem); m_tabCtrl.SetCurSel(1); return TRUE; }

当标签切换时,标签控制会自动向对话框窗口发送TCN_SELCHANGE通知消息,这时需要根据所选择的标签索引号对属性页的显示和隐藏进行切换控制,应完善OnSelchangeTabctrl()函数: void CTabDlg::OnSelchangeTabctrl(NMHDR* pNMHDR, LRESULT* pResult) { int iPage=m_tabCtrl.GetCurSel();//所选标签号 switch(iPage){ case 0://字间距

GetDlgItem(IDC_STATIC2)->ShowWindow(SW_HIDE);//隐藏选择按钮

GetDlgItem(IDC_BLACK)->ShowWindow(SW_HIDE);//隐藏选择按钮 GetDlgItem(IDC_RED)->ShowWindow(SW_HIDE);//隐藏选择按钮 GetDlgItem(IDC_BLUE)->ShowWindow(SW_HIDE);//隐藏选择按钮 GetDlgItem(IDC_STATIC1)->ShowWindow(SW_SHOW);//显示输入项数 GetDlgItem(IDC_DIST)->ShowWindow(SW_SHOW);//显示输入项数 break;

case 1://颜色设置

GetDlgItem(IDC_STATIC1)->ShowWindow(SW_HIDE);//隐藏项数输入 GetDlgItem(IDC_DIST)->ShowWindow(SW_HIDE);//隐藏项数输入 GetDlgItem(IDC_STATIC2)->ShowWindow(SW_SHOW);//显示选项选择 GetDlgItem(IDC_BLACK)->ShowWindow(SW_SHOW);//显示选项选择 GetDlgItem(IDC_RED)->ShowWindow(SW_SHOW);//显示选项选择 GetDlgItem(IDC_BLUE)->ShowWindow(SW_SHOW);//显示选项选择 break; }

*pResult = 0; }

(4)菜单功能的完善。在执行相应的菜单功能时,必须对类向导增加的相应功能函数进行代码完善,这就要处理TabView.cpp文件,背景设置功能函数如下: void CTabView::OnBkgrnd() { CTabDlg ctd; ctd.m_nDist=nDist; ctd.m_nColor=nColor;

if(ctd.DoModal()==IDCANCEL) return; nDist=ctd.m_nDist; nColor=ctd.m_nColor; Invalidate();//重新绘制窗口 }

同样,也要对前景设置功能函数进行完善: void CTabView::OnFrgrnd()

{ CPropertySheet cps(\前景设置\创建属性表对象 CTextPage ctp; //显示文字属性页 CStylePage csp;//显示字体属性页 ctp.m_cText=cText; csp.m_bBold=bBold; csp.m_bItalic=bItalic; csp.m_bUnderline=bUnderline;

cps.AddPage(&ctp);//添加属性页 cps.AddPage(&csp);

if(cps.DoModal()==IDCANCEL) return; cText=ctp.m_cText; bBold=csp.m_bBold; bItalic=csp.m_bItalic; bUnderline=csp.m_bUnderline; Invalidate();//重新绘制窗口 }

(5)为了充分演示标签控制与各属性页之间的数据交换功能,应该实现标签控制各属性页与用户之间数据交换结束后的窗口显示功能,笔者实现的功能函数显示了由属性页中输入的字体及背景网格功能,TabView.cpp中的对应函数代码如下: void CTabView::OnDraw(CDC* pDC) { CTabDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc);

// TODO: add draw code for native data here RECT rc;

GetClientRect(&rc); int i,j,k;

CPen pen,*pOldPen; COLORREF color; switch (nColor){

case 0:color=RGB(0,0,0); //设置黑色 break;

case 1:color=RGB(0xff,0,0);//设置红色 break;

case 2:color=RGB(0,0,0xff);//设置蓝色 break; }

pen.CreatePen(PS_SOLID,1,color);

pOldPen=pDC->SelectObject(&pen);//绘制背景网格 j=rc.right/nDist+1; k=rc.bottom/nDist+1; for(i=0;iMoveTo(i*nDist,0); pDC->LineTo(0,i*nDist); if(i

pDC->MoveTo(i*nDist,0);

pDC->LineTo(rc.right,(j-i)*nDist); } else {

pDC->MoveTo(0,(i-j)*nDist); pDC->LineTo(rc.right,i*nDist); } }

pDC->SelectObject(&pOldPen); CFont font,*pOldFont;

font.CreateFont(50,0,0,0,bBold?1000:200, bItalic,bUnderline,0,ANSI_CHARSET, OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,DEFAULT_PITCH,NULL); pOldFont=pDC->SelectObject(&font); pDC->TextOut(20,20,cText); pDC->SelectObject(pOldFont); }

标签控制的整个实现过程虽然比较繁锁,但只要掌握其实现的本质,设计一个优秀的标签控制界面也并非很困难的事情。

笔者实现的标签控制的演练示例结果如下:

标签控制演练示例结果

六、PWIN98/95下演练CToolBar

(一)工具条控制的主要功能

所谓工具条就是具有位图和分隔符组成的一组命令按钮,位图按钮部分可以是下推按钮、检查盒按钮、无线按钮等。工具条对象类派生于主窗口架框类CframeWnd或CMDIFrameWnd,其类控制CToolBar::GetToolBarCtrl是MFC类库中封装的一个成员函数,允许使用类库中提供的一般控制和附加功能,CtoolBar类控制成员控制提供了Windows一般控制的所有功能,然而,通过调用 GetToolBarCtrl成员函数取得引用后,可以使工具条具有更强的特性。

工具条的创建具有四个步聚:首先是建立工具条资源;然后建立工具条对象结构;其次通过调用建立函数建立工具条对象并绑定;最后调用LoadToolBar调入工具条资源。

另外,还可以通过直接加载位图的方法来建立,步骤如下:首先建立工具条对象;然后通过调用建立函数建立工具条并绑定对象;其次调入包含按钮的位图;最后利用SetButtons 函数设置按钮的风格并与位图建立联系。

其中,所有按钮位图均存放在一个位图文件中,按钮位图的大小相同,默认为16点宽、15点高,位图必须从左至右存放。设置按钮函数具有指向一组控制标识符ID的指针和索引值,用来确定每个按钮的位置,如果存在分隔符ID_SEPARATOR, 那么该图像就不存在索引值。正常情况下工具条中的按钮都是单排从左至右排列的,可以通过SetButtonInfo函数改变排序规则。 工具条中最终形成的按钮大小相同,均为24 x 22 象素,每个按钮只对象一幅图像。工具条中的按钮默认为下推按钮,通过设置TBBS_CHECKBOX风格可以实现检查盒按钮,通过调用SetRadio成员函数可以实现无线按钮。

(二)工具条控制的对象结构 1、工具条的对象结构 (1)工具条的建立方法

CToolBar &ToolBar 建立工具条对象结构 Create 建立工具条对象并绑定 工具条类CToolBar::Create 的调用格式如下:

BOOL Create( CWnd* pParentWnd, DWORD dwStyle = WS_CHILD | WS_VISIBLE | CBRS_TOP, UINT nID = AFX_IDW_TOOLBAR );

其中参数pParentWnd用来确定指向工具条父窗口的指针;参数dwStyle用来确定工具条的风格,其取值如下;参数nID用来确定工具条子窗口的标识符。 CBRS_TOP 表示工具条在框架窗口的顶部 CBRS_BOTTOM 表示工具条在框架窗口的底部

CBRS_NOALIGN 表示工具条在父窗口改变大小时不响应 CBRS_TOOLTIPS 表示工具条具有动态提示功能 CBRS_SIZE_DYNAMIC 表示工具条是静态的不能改变 CBRS_SIZE_FIXED 表示工具条是动态的可以改变 CBRS_FLOATING 表示工具条是浮动的

CBRS_FLYBY 表示状态条上显示工具条中按钮的信息

CBRS_HIDE_INPLACE 表示工具条隐藏

除以上函数外,还包括设置按钮和位图的大小SetSizes、设置工具条的高度SetHeight、调入工具条资源LoadToolBar、调入工具条按钮位图LoadBitmap、设置工具条按钮位图SetBitmap、设置工具条中位图按钮的风格和索引值SetButtons等控制函数。 (2)工具条的类属性

工具条控制类的属性包括取得标识符ID对象按钮索引CommandToIndex、取得索引对应的命令标识符ID或分隔符GetItemID、取得索引对应的矩形区域GetItemRect、取得按钮风格 GetButtonStyle、设置按钮风格SetButtonStyle、取得按钮的ID标识-风格-图象数GetButtonInfo、设置按钮ID标识-风格-图象数SetButtonInfo、取得按钮提示文本

GetButtonText、设置按钮提示文本SetButtonText和取得工具条直接存取控制GetToolBarCtrl等。 2、工具条控制的对象结构 (1)工具条控制的建立方法

CToolBarCtrl &ToolBarCtrl 建立工具条控制对象结构 Create 建立工具条控制对象并绑定 工具条控制类CToolBarCtrl::Create的调用格式如下:

BOOL Create( DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID ); 其中参数dwStyle用来确定工具条控制的风格,必须存在WS_CHILD风格;参数rect用来确定工具条控制的大小和位置;参数pParentWnd用来确定工具条控制的父窗口指针,不能为NULL;参数nID用来确定工具条控制的标识符。

可以利用WS_CHILD、WS_VISIBLE和WS_DISABLED来设置工具条窗口的风格,但必须合理设置如下控制风格:

CCS_ADJUSTABLE 允许用户处理工具条窗口大小,如果存在工具条窗口必须处理相应信 CCS_BOTTOM 使控制处于父窗口客户区域底部并与窗口同样宽 CCS_NODIVIDER 禁止在控制的顶部绘制2个象素的高亮条 CCS_NOHILITE 禁止在控制的顶部绘制1个象素的高亮条

CCS_NOMOVEY 使控制改变大小和移动时自动水平对齐,垂直对齐必须处理WM_SIZE消息

如果CCS_NORESIZE风格有效,则该风格无效

CCS_NOPARENTALIGN禁止控制自动移到父窗口顶部或底部,如果CCS_TOP或 CCS_BOTTOM风格

有效,则高度调整为默认而宽度可以改变

CCS_NORESIZE 禁止设置新的大小或无效值时使用默认宽度和高度值,而使用建立值 CCS_TOP 使控制自动停靠在父窗口客户区域顶部并与父窗口同样宽度 最后,还必须利用下面的风格来控制工具条 TBSTYLE_TOOLTIPS 使工具条建立并管理动态提示控制 TBSTYLE_WRAPABLE 使工具条控制按钮具有多行排列格式 (2)工具条控制中的数据结构

工具条控制中最常用的数据结构为TBBUTTON,其具体结构如下: typedef struct _TBBUTTON {

int iBitmap; // 基于0的位图索引值 int idCommand; // 按钮按下时发送的命令值 BYTE fsState; // 按钮的状态 BYTE fsStyle; // 按钮的风格

DWORD dwData; // 应用程序定义的数据

int iString; // 基于0的按钮标签字符串索引值 } TBBUTTON;

其中按钮状态fsState的值如下:

TBSTATE_CHECKED 表示按钮具有TBSTYLE_CHECKED风格并且被按下 TBSTATE_ENABLED 表示按钮允许接受输入,否则变灰不接受任何输入 TBSTATE_HIDDEN 表示按钮不可见并且不接受任何输入 TBSTATE_INDETERMINATE 表示按钮是变灰的 TBSTATE_PRESSED 表示按钮正被按下

TBSTATE_WRAP 表示按钮具有换行特性,该按钮必须具有TBSTATE_ENABLED状态 按钮风格style可以是下列值的组合: TBSTYLE_BUTTON 表示建立标准下推按钮 TBSTYLE_CHECK 表示建立检查状态按钮 TBSTYLE_CHECKGROUP表示建立检查按钮群 TBSTYLE_GROUP 表示建立按下状态按钮群 TBSTYLE_SEP 表示建立按钮分隔符 (3)工具条控制的类属性

工具条控制的类属性必然的联系判断按钮使能状态IsButtonEnabled、判断按钮检查状态 IsButtonChecked、判断按钮按下状态IsButtonPressed、判断按钮是否隐藏IsButtonHidden、判断按钮变灰状态IsButtonIndeterminate、设置按钮状态SetState、取得按钮状态GetState、取得按钮有关信息GetButton、取得按钮总数GetButtonCount、取得按钮矩形区域GetItemRect、设置按钮结构大小SetButtonStructSize、设置按钮大小SetButtonSize、设置按钮位图大小SetBitmapSize、取得按钮提示控制GetToolTips、设置按钮提示控制SetToolTips等。 (4)工具条控制类的操作方法

工具条控制类的操作方法包括使能按钮EnableButton、检查按钮CheckButton、按下按钮PressButton、隐藏按钮HideButton、变灰按钮Indeterminate、增加按钮AddButtons、插入按钮InsertButton、删除按钮DeleteButton、取得控制符ID对应的索引CommandToIndex、恢复工具条状态RestoreState、保存工具条状态SaveState和重新确定工具条大小AutoSize等。 (三)工具条控制的应用技巧

可以这样说,工具条和上述常用控制是应用程序中不可缺少的功能元素,它的优劣会直接影响程序的基本功能和操作特性。所以这里将对工具条的建立技巧、状态保存与恢复、平面特性、停靠位置、排序方法、消息映射、状态更新、控制使用和属性控制等方面,全面阐述工具条的使用技巧。

1、工具条的建立技巧 (1)普通工具条的建立方法

如果应用程序在建立时就具有工具条,则只需对工具条中的按钮图标进行简单的增加、修改和删除等操作就可满足要求。如果未建立或者想增加其它工具条,则应按步骤追加建立。 首先打开已建立好的基于单文档的框架工程文件CTool并选择\选项,插入工具条资源并设置资源标识符;然后编辑工具栏中的按钮图标和相应的按钮标识符,并利用类向导ClassWizard 为按钮消息增加COMMAND和UPDATE_COMMAND_UI两种处理函数;在资源文件中增加和修改工具条图标的动态提示等内容;打开MainFrm.h包含文件在\m_wndMainToolBar\后增加\等来创建增加的工具条对象;在MainFrm.h 中设置建立函数所需的成员变量,如颜色变量为m_bColor、动态提示功能变量为m_bToolTips 等,注意成员变量名与其获取的参数应完全对应以便使用;最后在MainFrm.cpp中的OnCreate()建立函数中按下述示例规则增加控制代码,其实现具体步骤如下: ①在MainFrm.h中增加工具条对象控制和成员变量 #define TOOLLEFT 18

class CMainFrame:public CFrameWnd ......//其它代码 public:

BOOL m_bToolTips;//工具条提示功能 ......//其它代码

protected://工具条控制成员变量

CStatusBar m_wndStatusBar; //框架程序的状态条 CTestToolBar m_wndMainToolBar;//框架程序的工具条 CTestToolBar m_wndTestToolBar;//新增工具条 CTestToolBar m_wndDockToolBar;//浮动工具条 CTestToolBar m_wndDockNextBar;//浮动工具条 ......//其它代码 }

框架程序中工具条的控制类正常应为CToolBar,可以是自己设计的派生类CtestToolBar(为笔者扩充平面特性等功能后的新工具条控制类名)等,具体根据实际需要而定。利用CDialogBar类和CStyleBar 类还可以建立扩展类型的工具条,详见后面工具条中控制应用技巧,但在该文件头处必须 包含如下命令:

#ifndef __AFXEXT_H__

#include //直接存取CToolBar和CStatusBar #endif

②在MainFrm.cpp中完善窗口建立函数

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)

{ if (CFrameWnd::OnCreate(lpCreateStruct) == -1) return -1;

WINDOWPLACEMENT wp;//保存主窗口及工具栏窗口位置状态 if (ReadWindowPlacement(&wp))//读取位置状态信息 SetWindowPlacement(&wp); //设置位置状态信息 m_bToolTips=(AfxGetApp()->GetProfileInt(//读提示功能 _T(\默认值为1 m_wndMainToolBar.SetState(TOOLLEFT,TRUE);//设置初始状态 EnableDocking(CBRS_ALIGN_ANY);//停靠位置,必须提前位置 if (!m_wndMainToolBar.Create(this,WS_CHILD|WS_VISIBLE |CBRS_SIZE_DYNAMIC|CBRS_TOP|((m_bToolTips)? (CBRS_TOOLTIPS|CBRS_FLYBY):0),IDR_MAINFRAME)|| !m_wndMainToolBar.LoadToolBar(IDR_MAINFRAME)) { //CBRS_SIZE_DYNAMIC为锁定位置风格 TRACE0(\主工具条MAINFRAME建立失败\\n\ return -1;} // 建立失败处理 ......//建立其它工具条代码,基本相同 if (!m_wndStatusBar.Create(this)||

!m_wndStatusBar.SetIndicators(indicators, sizeof(indicators)/sizeof(UINT))) { //建立状态条

TRACE0(\ return -1;} // fail to create

m_wndMainToolBar.SetWindowText(_T(\主工具栏\设置标题 m_wndMainToolBar.EnableDocking(CBRS_ALIGN_ANY);//停靠位置 //m_wndMainToolBar.ModifyStyle(0,TBSTYLE_FLAT);//平面特性 ......//设置其它工具条位置代码,基本相同 DockControlBar(&m_wndMainToolBar, AFX_IDW_DOCKBAR_TOP);//锁定位置 DockControlBarLeftOf(&m_wndTestToolBar, &m_wndMainToolBar);//连接工具条

DockControlBar(&m_wndDockToolBar,AFX_IDW_DOCKBAR_RIGHT); m_wndDockToolBar.SetColumns(AfxGetApp()->GetProfileInt( _T(\恢复列格式,默认为3 DockControlBarLeftOf(&m_wndDockNextBar,&m_wndDockToolBar); m_wndDockNextBar.SetColumns(AfxGetApp()->GetProfileInt( _T(\

LoadBarState(_T(\恢复保存的状态和位置 return 0; }

以上建立过程除工具条建立和资源调用函数外,还涉及到了窗口和工具条的状态保存和恢复函数、注册表参数读取函数、工具条停靠位置函数、工具条标题修改函数、工具条连接函数、工具条列格式控制函数和工具条风格修改函数,其中工具条建立函数中的风格设置很重要,如果建立的工具条需要重新设置多行多列的排序功能,除正确设置工具条停靠位置参数外,还必须设置CBRS_SIZE_FIXED 风格,即允许程序改变工具条窗口的尺寸,如果工具条不需要重新排序,则必须设置为CBRS_SIZE_DYNAMIC 风格,否则工具栏不但不能进行重新排序和正确停靠到理想的位置,而且也无法正确保存和恢复工具条的位置和状态,这一点应引起编程者高度重视。其余函数以后分别介绍。

(2)浮动工具条的建立方法

如果要建立浮动工具条,必须使用如下工具条的控制方法:

Cpoint pt(GetSystemMetrics(SM_CXSCREEN)-100,GetSystemMetrics(SM_CYSCREEN)/3); FloatControlBar(&m_wndPaletteBar,pt);//浮动工具条 (3)多位图工具条的建立方法

如果工具条存在多幅按钮位图,如单色和彩色等,则必须将工具条按钮存在在位图资源文件中而不是工具条资源中,并如下建立:

if(!m_wndDockToolBar.Create(this,WS_CHILD|WS_VISIBLE| CBRS_SIZE_FIXED|CBRS_TOP|CBRS_TOOLTIPS,ID_PALETTEBAR)|| !m_wndDockToolBar.LoadBitmap(IDR_DOCKTOOLBAR)|| !m_wndDockToolBar.SetButtons(DockTool, sizeof(DockTool)/sizeof(UINT)))

其中DockTool为按钮IDs数据结构,其定义方法如下: static UINT BASED_CODE DockTool[]= { ID_SEPARATOR, ID_STYLE_LEFT, ID_STYLE_CENTERED, ID_STYLE_RIGHT, ID_STYLE_JUSTIFIED, };

上述建立过程中的EnableDocking 函数必须放在所有工具条建立函数之前,否则可能出现很难发现的错误,如特殊工具条初始位置控制等。工具条的所有特性均在上述建立函数中确定,所以其建立过程是实现理想工具条的关键环节。 2、工具条状态保存和恢复

很多应用程序中都具有保存和恢复应用程序及其工具条等状态的功能,即下次启动应用程序后进入上次的运行状态,这种功能只需进行一次界面布局便可永久保存,极大方便用户。

要正确保存和恢复应用程序界面状态,必须对应用程序窗口和工具条窗口等均进行保存和恢复,这需要完善应用程序的建立和关闭过程。具体步骤如下:

(1)首先利用类向导ClassWizard为应用程序增加窗口关闭WM_CLOSE消息处理功能OnClose();

(2)在MainFrm.cpp中为应用程序状态设置成员变量 static TCHAR BASED_CODE szSection[]=_T(\ static TCHAR BASED_CODE szWindowPos[]=_T(\ static TCHAR szFormat[]=_T(\ (3)编制窗口位置状态读取和写入函数

static BOOL PASCAL NEAR ReadWindowPlacement(LPWINDOWPLACEMENT pwp) { //窗口位置状态读取函数,从INI文件中

CString strBuffer=AfxGetApp()->GetProfileString(szSection,szWindowPos); if (strBuffer.IsEmpty()) return FALSE; WINDOWPLACEMENT wp;//窗口位置数据结构 int nRead=_stscanf(strBuffer,szFormat, &wp.flags,&wp.showCmd,//为数据结构读取数值 &wp.ptMinPosition.x,&wp.ptMinPosition.y, &wp.ptMaxPosition.x,&wp.ptMaxPosition.y,

&wp.rcNormalPosition.left,&wp.rcNormalPosition.top, &wp.rcNormalPosition.right,&wp.rcNormalPosition.bottom); if (nRead!=10) return FALSE; wp.length=sizeof wp;//结构大小 *pwp=wp; //结构指针 return TRUE; }

static void PASCAL NEAR WriteWindowPlacement( LPWINDOWPLACEMENT pwp)

{ //窗口位置状态写入函数,写到INI文件

TCHAR szBuffer[sizeof(\ wsprintf(szBuffer,szFormat,//将参数值转换为字符串 pwp->flags,pwp->showCmd,

pwp->ptMinPosition.x,pwp->ptMinPosition.y, pwp->ptMaxPosition.x,pwp->ptMaxPosition.y,

pwp->rcNormalPosition.left,pwp->rcNormalPosition.top, pwp->rcNormalPosition.right,pwp->rcNormalPosition.bottom); AfxGetApp()->WriteProfileString(szSection,szWindowPos,szBuffer); }

(4)在应用程序建立函数OnCreate()中增加状态读取和设置功能 WINDOWPLACEMENT wp;//保存主窗口及工具条窗口位置状态 if (ReadWindowPlacement(&wp))//读取位置状态信息 SetWindowPlacement(&wp); //设置位置状态信息 (5)在应用程序建立函数OnCreate()中增加工具条状态恢复功能 m_wndDockToolBar.SetColumns(AfxGetApp()->GetProfileInt( _T(\恢复列格式,默认为3 m_wndDockNextBar.SetColumns(AfxGetApp()->GetProfileInt( _T(\

LoadBarState(_T(\恢复保存的状态和位置 (6)在应用程序关闭函数OnClose()中完善状态保存功能 void CMainFrame::OnClose() { //保存工具条等的状态

SaveBarState(_T(\保存工具条状态

AfxGetApp()->WriteProfileInt(_T(\写入列数 _T(\ AfxGetApp()->WriteProfileInt(_T(\ _T(\写入提示功能 WINDOWPLACEMENT wp; wp.length=sizeof wp;

if (GetWindowPlacement(&wp)){ wp.flags=0;

if (IsZoomed()) wp.flags|=WPF_RESTORETOMAXIMIZED; //如果窗口被放大,则保存为最大化状态 WriteWindowPlacement(&wp); }

CFrameWnd::OnClose(); }

虽然SaveBarState()和LoadBarState()函数保存和恢复了工具条的所有默认位置状态,但在实际自己实现的功能参数部分并不能被保存,所以应单独编写这些参数的保存代码,如工具栏的排列格式列参数值、颜色状态标志和是否存在动态提示功能标志等,在实际编程时一定要注意。 3、工具条的平面特性

工具条的平面特性给人耳目一新之感,很多大型应用程序中的工具条都采用这一特性,并取得了巨大成功。利用VC++5中的COMCTL32.DLL动态链接库可以实现平面式工具条,其主要解决问题包括:由于MFC使用风格控制位来控制工具条的外观,所以在建立工具条时不能直接设置这种风格,必须在建立后利用SetFlatLookStyle()函数来修改;工具条控制本身也不在各级按钮之间绘制分隔线,其另一个任务就是截取WM_PAINT消息,并在相应的位置处增加分隔线;工具条控制也不绘

制左边的把手(gripper) ,最后的任务就是调整客户区域并绘制并绘制相应的gripper。 显然,实际工作中需要动态链接库COMCTL32.DLL支持的上述方法很不方便。尽管最简便的方法是利用VC++ 5中的未公开工具栏风格TBSTYLE_FLAT,可以得到工具条的平面特性,只需在工具条建立后简单地增加一条代码\,但笔者经试验发现这种方法存在两个严重错误:其一是所建立的平面工具条在移动时,不能自动清除移动前的按钮图标,使工具条画面杂乱无章;其二是当建立的平面工具条具有浮动特性时,只要鼠标指针移动到浮动工具条上,整个应用程序窗口就会自动消失。所以第二种方法根本不可行。实现平面工具条的最好方法是在派生类中自己来完成,虽然这一过程比较复杂普通用户很难做到,但如果存在一个完美的平面工具条控制类,在自己的应用程序中增加相应控制类就是一件很容易的事了。下面是笔者实现完美平面工具条派生类的步骤:

(1)首先利用类向导ClassWizard为工具条控制类派生一个新类CTESTTOOLBAR ,并设置相应的派生类实现文件名。由于新类的基类无法直接选择CTOOLBAR,所以在选择新类的基类时先选择CTOOLBARCTRL为基类,当派生类生成后再将实现文件中的所有CTOOLBARCTRL类名修改为CTOOLBAR控制类,并利用ClassWizard 为新类增加消息WM_PAINT、WM_NCPAINT、WM_MOUSEMOVE、WM_LBUTTONDOWN和WM_LBUTTONUP消息处理功能函数,以便实现新类中平面工具条的各种特性。同时,要在MainFrm.cpp中增加包含文件TestToolBar.h。 (2)完善派生类实现文件TestToolBar.h内容 class CTestToolBar : public CToolBar {......//其它代码 public:

CTestToolBar(); //新类构造函数

UINT GetColumns() { return m_nColumns;};//取得列数 void SetState(UINT nLeft,BOOL nStated);//设置列数和状态 void OnDrawBorder(int index,CDC &dc,int flag);//画边框 void OnEraseBorder(int index,CDC &dc);//删除边框 void OnDrawBorders();//画平面特性

void OnDrawSep(int index,CDC &dc);//画分隔线 void OnDrawGrapper();//画把手 ......//其它代码

#ifdef _DEBUG //增加插入控制

virtual void AssertValid() const;

virtual void Dump(CDumpContext& dc) const; #endif

protected: //增加成员变量

UINT m_nColumns; //工具栏按钮列数 UINT m_nFlags; //鼠标按键标志 int m_nIndex; //按下的按钮号

int m_nFlagl; //左键按下标志 UINT m_nStated; //工具栏状态 CRect rt; //关闭按钮矩形区域 ......//其它代码 }

(3)完善派生类实现文件TestToolBar.cpp内容 ......//其它代码 #define TOOLLEFT 18 #define LBUTTONDOWN 1 #define LBUTTONUP 2 ......//其它代码

CTestToolBar::CTestToolBar() { //在构造函数中初始化变量

m_nColumns=0; //工具栏按钮列数 m_cxLeftBorder=16; //左边界 m_cxRightBorder=3; //右边界 m_cyTopBorder=3; //顶边界 m_cyBottomBorder=3;//底边界

m_nFlags=0; //按键标志成员变量 m_nIndex=0xffff; //按下的按钮号 m_nFlagl=0; //左键按下标志 m_nStated=TRUE; //工具栏状态 }

......//其它代码

#ifdef _DEBUG//插入代码完善

void CTestToolBar::AssertValid() const { CToolBar::AssertValid(); }

void CTestToolBar::Dump(CDumpContext& dc) const { CToolBar::Dump(dc); } #endif //_DEBUG ......//其它代码

虽然需要实现的函数比较多,但总起来说不过是取得客户区域或窗口所有区域的文本设备、建立画笔和绘图函数的集合,所以这里只给出了画按钮凸凹边线的函数,其它函数可仿造实现。 void CTestToolBar::OnDrawBorder(int index,CDC &dc,int flag) { //画按钮边线flag=0凸=1凹 CRect rect;

GetItemRect(index,&rect);//取得客户区域

rect.right--;rect.bottom--; CPen *oldpen; UINT color1,color2;

if (flag==0){//两种状态的颜色处理

color1=COLOR_BTNHILIGHT;//按钮高度颜色 color2=COLOR_BTNSHADOW; //按钮阴影颜色 } else {

color1=COLOR_BTNSHADOW; color2=COLOR_BTNHILIGHT; }

CPen pen1(PS_SOLID,1,::GetSysColor(color1)); CPen pen2(PS_SOLID,1,::GetSysColor(color2)); dc.SelectStockObject(NULL_BRUSH); oldpen=dc.SelectObject(&pen1);

dc.MoveTo(rect.right,rect.top);//画按钮边亮线 dc.LineTo(rect.left,rect.top); dc.LineTo(rect.left,rect.bottom);

dc.SelectObject(&pen2); //画按钮边暗线 dc.MoveTo(rect.right,rect.top); dc.LineTo(rect.right,rect.bottom); dc.LineTo(rect.left,rect.bottom);

//dc.SelectStockObject(BLACK_PEN);//画按钮边黑线 //dc.MoveTo(rect.right+1,rect.top); //dc.LineTo(rect.right+1,rect.bottom+1); //dc.LineTo(rect.left,rect.bottom+1); dc.SelectObject(oldpen); DeleteObject(pen1); DeleteObject(pen2); }

void CTestToolBar::OnDrawBorders() { //实现平面工具条 CRect rect; CPoint pt;

GetCursorPos(&pt); //取得鼠标指针 ScreenToClient(&pt);//变成窗口坐标 int index;

int count=GetCount();//工具条按钮总数

本文来源:https://www.bwwdw.com/article/n0f3.html

Top