演练VC中的COMMON一族

来源:互联网 发布:多线程并发处理数据 编辑:程序博客网 时间:2024/05/31 15:18
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等)。

 

第1章 演练CHeadCtrl
    表头控制(CHeaderCtrl)通常应用在窗口中的文本或数据的列表之上。一般为数据列的标题,可以包括多个部分,用户可以拖动每个部分并可以控制每列的宽度。表头控制类提供了普通表头控制的基本方法,只有在WINDOWS95以后版本系统中才提供,其方法包含在afxcmn.h文件中,一般与标签控制(CTabCtrl)和列表控制(CListCtrl)组合使用。
1.1 表头控制的对象结构
1.1.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控制移动站点;
    表头控制一般分为两个步骤,首先确定表头控制的数据结构,然后建立表头控制并绑定对象。
1.1.2 表头控制的属性
    表头控制的属性包括取得表头控制中项目的数量GetItemCount、取得表头控制中某一项目的内容GetItem和设置表头控制中某一项目的内容SetItem。
1.1.3 表头控制的操作方法
    表头控制的操作方法包括向表头控制中插入一个新项目InsertItem、从表头控制中删除一个项目DeleteItem和绘制表头中给定的项目DrawItem等。
1.2 表头控制的数据结构
    在使用表头控制时,首先必须建立一个数据结构HD_ITEM,其结构定义如下:
    typedef struct _HD_ITEM 
    { UINT    mask;        file://结构成员有效控制位
      int     cxy;         file://表头项目的宽度
      LPSTR   pszText;     file://表头项目内容
      HBITMAP hbm;         file://表头项目的位置句柄
      int     cchTextMax;  file://表头内容字符串长度
      int     fmt;         file://表头项目的格式
      LPARAM  lParam;      file://应用程序定义的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        表头项目为一个字符串
1.3 表头控制的应用技巧
    由于表头控制无法单独使用,其主要是配合列表控制和标签控制,并多以文字表头应用多见,InsertItem、SetItem和GetItem是常用的方法,如在列表控制时利用InsertColumn属性就可以增加一个表列的文本标题,具体用法和技巧见列表控制和标签控制。下面以在列表控制中的增加表列的方法来具体说明:
    lvcol.pszText="品  名";//设置第一列表头名
    lvcol.iSubItem=i;      file://表列序号
    lvcol.cx=70;           file://表列宽度
    m_ListCtrl.InsertColumn(i++,&lvcol);//插入一个表列
    lvcol.pszText="数  量";//设置第二列表头名
    lvcol.iSubItem=i;
    lvcol.cx=70;
    m_ListCtrl.InsertColumn(i++,&lvcol);//插入一个表列
    ......//其它代码


第2章 演练CImageList
    图像列表控制(CImageList)是相同大小图像的一个集合,每个集合中均以0为图像的索引序号基数,图像列表通常由大图标或位图构成,其中包含透明位图模式。可以利用WINDOWS32位应用程序接口函数API来绘制、建立和删除图像,并能实现增加、删除、替换和拖动图像等操作。图像列表控制提供了控制图像列表的基本方法,这些方法在WINDOWS95及以后版本才能实现。
2.1 图像控制的对象结构
2.1.1 图像控制的数据成员
    m_hImageList         连接图像对象的控制句柄
2.1.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表示以象素为单位的图像高度。
同样,图像控制的建立也包括两个步骤,首先建立图像列表结构,然后建立图像列表控制。
2.1.3 图像控制的属性类
    图像控制的属性类包括返回m_hImageList.控制句柄GetSafeHandle、取得图像列表中的图像数量GetImageCount、设置图像列表的背景颜色SetBkColor、取得图像列表的背景颜色SetBkColor和取得图像的有关信息SetBkColor。
2.1.4 图像控制的操作方法
    图像控制的操作方法包括将一个图像列表绑定到一个对象上Attach、将对象上的图像列表解除绑定并返回句柄Detach、删除一个图像列表DeleteImageList、将一个图像增加到图像列表中Add和将一个图像从图像列表中删除Remove等。
2.2 图像控制的应用技巧
    对于图像控制,同样不能单独使用,必须与列表控制、树控制和标签控制相互结合应用,下面分别介绍其具体应用技巧。
2.2.1 图像控制在列表控制中的应用技巧
2.2.1.1 设置图像控制CListCtrl::SetImageList的调用格式如下:
    CImageList* SetImageList( CImageList* pImageList, int nImageList );
    其返回值是指向前一个图像列表控制的一个指针,如果不存在前一个图像列表则为NULL;其中参数pImageList是指向图像列表的标识,nImageList是图像列表的类型,可以是如下值:
    LVSIL_NORMAL     用大图标方式进行图像列表;
    LVSIL_SMALL     用小图标方式进行图像列表;
    LVSIL_STATE     以图像状态进行图像列表;
2.2.1.2 取得图像控制CListCtrl::GetImageList的调用格式如下:
    CImageList* GetImageList( int nImageList ) const;
    其返回值为指向图像列表控制的指针,其中nImageList用来确定取得返回值的图像列表的    值,其取值与设置图像列表函数相同。
    ③图像控制在列表控制中的应用示例
    CImageList Cil1,Cil2;            file://定义大小图标像列表
    CVCLISTApp *pApp=(CVCLISTApp *)AfxGetApp();//取得列表控制程序
    Cil1.Create(32,32,TRUE,2,2);     file://建立32位图像控制
    Cil1.Add(pApp->LoadIcon(IDI_GJ));//增加选中状态图像
    Cil1.Add(pApp->LoadIcon(IDI_XS));//增加非选中状态图像
    Cil2.Create(16,16,TRUE,2,2);     file://建立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.2.2 图像控制在树控制中的应用技巧
2.2.2.1 设置图像控制CTreeCtrl::SetImageList的调用格式如下:
    CImageList* SetImageList( CImageList * pImageList, int nImageListType );
    其返回值为指向前前一个图像列表的指针,否则为NULL;参数pImageList为指向图像列表的标识,如果pImageList为NULL则所有的图像都将从树控制中被清除;nImageListType为图像列表设置的类型,可以是如下值之一:
    TVSIL_NORMAL   设置正常图像列表,其中包括选中和非选中两种图标; 
    TVSIL_STATE   设置图像列表状态,指用户自定义状态;
2.2.2.2 取得图像控制CTreeCtrl::GetImageList的调用格式如下:
    CImageList* GetImageList( UINT nImage );
    如果调用成功则返回图像列表控制指针,否则为NULL;nImage为取得返回值的图像列表类型,其取值和取得图像列表控制完全相同。
2.2.2.3 图像控制在树控制中的应用示例
    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;        file://设置未选中图像索引号
    TCItem.item.iSelectedImage=1;//设置选中时图像引号
2.2.3 图像控制在标2.2.4 签控制中的应用技巧
2.2.4.1 设置图像控制CTabCtrl::SetImageList的调用格式
    CImageList * SetImageList( CImageList * pImageList );
    其返回值为指向前一个图像列表的指针,如果不存在前一个图像列表则为NULL;pImageList为标识TAB控制的图像列表指针。
2.2.4.2 取得图像控制CTabCtrl::GetImageList的调用格式
    HIMAGELIST GetImageList() const;
    其返回值为指向TAB控制的图像列表指针,如果调用不成功则为NULL。
    其应用技巧较前两种更加简单,这里不再赘述。


第3章 演练CList
3.1 列表控制的主要功能
    列表控制和视(List Control&View)主要用来以各种方式显示一组数据记录供用户进行各种操作,Windows98/95中资源管理器中的“查看”标签下的“大图标|小图标|列表|详细资源”就是一个非常好的典型应用。列表中的记录可以包括多个数据项,也可以包括表示数据内容的大小图标,用来表示数据记录的各种属性。
    列表控制提供了对Windows列表功能操作的基本方法,而使用列表视的视函数可以对列表视进行各种操作,通过调用视成员GetListCtrl获取嵌在列表视内列表控制的引用(GetListCtrl&  ctrlList  =  GetListCtrl()),就可以和列表控制一样进行各种操作。操作一个列表控制和视的基本方法为:创建列表控制;创建列表控制所需要的图像列表;向列表控制添加表列和表项;对列表进行各种控制,主要包括查找、排序、删除、显示方式、排列方式以及各种消息处理功能等;最后撤消列表控制。
    对于一个列表控制,其最典型最常用的显示控制方式为:大图标方式(LVS_ICON)、小图标方式(LVS_SMALLICON)、列表显示方式(LVS_LIST)和详细资料(即报告LVS_REPORT)显示方式。这可以通过设置其显示方式属性来实现。要控制列表所在窗口的风格,可通过功能函数GetWindowLong和SetWindowLong来实现,要控制列表图标的对齐方式,可通过设置列表窗口的风格LVS_ALIGNTOP或LVS_ALIGNLEFT来实现,
3.2 列表控制的对象结构
3.2.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 用来确定表项排序时是基于表项文本的降序方式;
3.2.2 列表控制的属性类
列表控制的属性类包括取得列表控制的背景色GetBkColor、设置列表控制的背景色SetBkColor、取得列表控制的图像列表GetImageList、设置列表控制的图像列表SetImageList、取得列表项数目GetItemCount、取得列表控制的属性GetItem、取得与表项相关的数据GetItemData、设置表项的属性SetItem、设置与表项相关的数值SetItemData、取得相关联的下一个表项GetNextItem、设置列表控制的文本颜色SetTextColor、取得列表控制的文本背景颜色GetTextBkColor、设置表项的最大数目SetItemCount和取得被选中表项的数目GetSelectedCount等。
3.2.3 列表控制的操作方法
    列表控制的操作方法包括插入一个新的表项InsertItem、删除一个表项DeleteItem、排序表项SortItems、测试列表的位置HitTest、重绘表项RedrawItems、插入一个表列InsertColumn、删除一个表列DeleteColumn、编辑一个表项文本EditLabel和重绘一个表项DrawItem等。
3.3 列表控制的数据结构
列表控制中包含两个非常重要的数据结构LV_ITEM和LV_COLUMN。LV_ITEM用于定义列表控制的一个表项,LV_COLUMN用于定义列表控制的一个表列,其定义格式分别为:
typedef struct _LV_ITEM { 
   UINT   mask;       file://结构成员屏蔽位
     int    iItem;      file://表项索引号
int    iSubItem;   file://子表项索引号
UINT   state;      file://表项状态
UINT   stateMask;  file://状态有效性屏蔽位 
LPTSTR  pszText;   file://表项名文本
int    cchTextMax; file://表项名最大长度
int    iImage;     // 表项图标的索引号
LPARAM lParam;     // 与表项相关的32位数
} LV_ITEM; 
    typedef struct _LV_COLUMN { 
        UINT mask;         file://结构成员有效性屏蔽位
        int fmt;           file://表列对齐方式
        int cx;            file://表列的象素宽度
        LPTSTR pszText;    file://表列的表头名
        int cchTextMax;    file://表列名的文本长度
        int iSubItem;      file://与表列关联的子表项索引号
} LV_COLUMN; 
其中fmt可以取如下值:
LVCFMT_CENTER        表列居中对齐 
LVCFMT_LEFT          表列左对齐
3.4 列表控制的应用技巧示例
    本文给出具体实例演示列表控制及前面的表头控制和图像列表的应用技巧。步骤如下:
1、通过“FILE->NEW->PROJECTS->MFC AppWizard(EXE)”建立名为VCLIST的工程,在建立过程中选择基于对话框(Dialog based)的应用;将对话框中的默认控件删除,并将所有对话框属性中的Language域设置为Chinese(P.R.C.),以使应用程序支持中文;
建立两个图标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 { file://定义结构
   char szPm[10];     file://品名
   int  Lx;           file://0-GJ 1-XS
   char szSl[10];     file://数量
   char szDj[10];     file://单价
   char szJe[10];     file://金额
    } SPS;
    int CALLBACK CompareFunc(LPARAM lParam1,LPARAM lParam2,LPARAM lParamSort);
   (2)在VCLISTDlg.CPP中的起始处增加初始化数据和程序定义
    file://在文件开始处增加数据结构初始化
    SPS Sps[]={//信息
        {"红梅",0,"1000","30","30000"},
        {"黄梅",0,"1000","29","29000"},
        {"绿梅",0,"1000","28","28000"},
        {"青梅",0,"1000","27","27000"},
        {"白梅",0,"1000","31","31000"},
        {"红梅",1,"1000","30","30000"},
        {"黄梅",1,"1000","29","29000"},
        {"绿梅",1,"1000","28","28000"},
        {"青梅",1,"1000","27","27000"},
       {"白梅",1,"1000","31","31000"}};
    CImageList Cil1,Cil2;//大小图像列表
    (3)在程序初始化处增加表头、图像和列表控制建立代码
    BOOL CVCLISTDlg::OnInitDialog()
    {CDialog::OnInitDialog();
    file://......//其它代码
    // TODO: Add extra initialization here此处增加代码
    LV_ITEM lvitem;
    LV_COLUMN lvcol;
    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);
    file://向列表控制中添加表项
    iItemNum=sizeof(Sps)/sizeof(SPS);
    for(i=0;i<iItemNum;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; file://清除显示方式位
 lStyle|=LVS_ICON;       file://设置显示方式
 SetWindowLong(m_ListCtrl.m_hWnd,GWL_STYLE,lStyle);//设置窗口类型
}
void CVCLISTDlg::OnSmlicon() file://设置小图标显示方式
{ // TODO: Add your control notification handler code here
 LONG lStyle;
 lStyle=GetWindowLong(m_ListCtrl.m_hWnd,GWL_STYLE);//获取当前窗口类型
 lStyle&=~LVS_TYPEMASK; file://清除显示方式位
 lStyle|=LVS_SMALLICON;  file://设置显示方式
 SetWindowLong(m_ListCtrl.m_hWnd,GWL_STYLE,lStyle);//设置窗口类型
}
void CVCLISTDlg::OnList() file://设置列表显示方式
{ // TODO: Add your control notification handler code here
 LONG lStyle;
 lStyle=GetWindowLong(m_ListCtrl.m_hWnd,GWL_STYLE);//获取当前窗口类型
 lStyle&=~LVS_TYPEMASK; file://清除显示方式位
 lStyle|=LVS_LIST;       file://设置显示方式
 SetWindowLong(m_ListCtrl.m_hWnd,GWL_STYLE,lStyle);//设置窗口类型
}
void CVCLISTDlg::OnReport() file://详细资料显示方式
{ // TODO: Add your control notification handler code here
 LONG lStyle;
 lStyle=GetWindowLong(m_ListCtrl.m_hWnd,GWL_STYLE);//获取当前窗口类型
 lStyle&=~LVS_TYPEMASK; file://清除显示方式位
 lStyle|=LVS_REPORT;     file://设置显示方式
 SetWindowLong(m_ListCtrl.m_hWnd,GWL_STYLE,lStyle);//设置窗口类型
}
    (5)删除功能的实现
    要实现删除功能,必须取得选中表项的数和表项总数,并且需要从后向前进行依次删除,其原因是每个表项被删除后,其后各表项的索引号均会发生递减变化,如果采取从前向后删除的方法,就会造成无法正常删除选中的表项,其功能代码如下:
void CVCLISTDlg::OnDel() file://删除按钮功能
{ // 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--){
  iState=m_ListCtrl.GetItemState(i,LVIS_SELECTED);
  if(iState!=0) m_ListCtrl.DeleteItem(i);
 }
}
    (6)排序功能的实现
    列表控制有一个特殊的功能,当以详细资料方式显示时,列表顶部的表头可以当作按钮来使用,这可以通过列表控制创建时的风格来控制。当鼠标点击列表头名称时,列表控制就会向其父窗口发送一个LNV_COLUMNCLICK消息,利用类导向中列表控制IDC_LISTCTRL对应的LNV_COLUMNCLICK消息加入相应处理函数,就可将表列按照特定顺序进行排列。其函数使用方法见程序,其中iSort为排序的表列索引号,(PFNLVCOMPARE)CompareFunc为进行具体排序的回调函数,也就是说,通过鼠标点击表头实现的排序过程是由第三方开发的专用排序函数来实现的,排序函数只是实现表项的具体比较操作,而整个排序过程是由SortItemS属性通过不断调用这个函数来实现的。正常的排序过程是升序方式,通过调换排序函数中的参数值,就可实现降序排列,即将PARAM1与PARAM2调换位置。这个回调函数的前两个参数为表列中表项的索引号,第三个参数为排序的表列索引号。
void CVCLISTDlg::OnColumnclickListctrl(NMHDR* pNMHDR, LRESULT* pResult) 
{   file://鼠标左键单击表头处理函数
 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;
}
    file://排序时比较表项的回调函数
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()),而其余数据结构和代码均不需要修改,实现起来比较容易。
笔者实现的列表控制和视程序的运行结果如下:
                    列表控制演练示例结果

                  列表视演练示例结果

 

 
第4章 演练CTree
4.1 树控制的主要功能
    树控制和视(Tree Control&View)主要用来显示具有一定层次结构的数据项,如资源管理器中的磁盘目录等,以供用户在其中进行各种选择。树控制中的每个数据项包括数据项名称的文本字符串和用于表示该数据项的图像,每个数据项下面均可包含各种子项,整个结构就象目录树一样。对于包含各种子项的数据项,可通过鼠标双击来展开或合拢,这可以通过控制树的不同风格来实现树控制的不同显示形态。这些风格主要包括:
    TVS_HASLINES表示用连线来连接父项和它下面的各个子项,这可以使树的显示层次结构更加清晰,但在无父项的各子项之间并没有连线;
    TVS_LINESATROOT表示在无父项的各子项即根下面的各子项之间存在连线;
    TVS_HASBUTTONS表示在带有子项的父项前面增加一个带“+”或“-”的按钮,这使得用户也可以通过单击这个小按钮来实现子项的展开和合拢,当存在子项时,按钮的初始状态为“+”,当子项被展开时,按小按钮由“+”变为“-”号,当子项合拢时,小按钮由“-”变为“+”号,这一风格同样对于根项无效,如果需要可通过组合TVS_LINESATROOT风格来实现;
    TVS_EDITLABELS表示允许让用户单击具有输入焦点的数据项来修改其名称。
对于树控制,MFC中也以两种形式来封装,即树控制(CTREECTRL)和树视(CTREEVIEW),来满足用户的不同需求,对于一般要求的用户如在对话框中应用,使用树控制比较方便,而对于具有较高要求的用户,在使用树视时还具有视窗口的各种方便特性,可以更好地满足文档/视结构的要求。当在窗口中使用树视时,树视会占满两个窗口的客户区域并自动随窗口的框架结构的调整而调整,并能够很好地处理诸如菜单、加速键和工具条中的各种命令消息。在使用树视时只要利用其成员函数CtreeView取得其一个引用,就可以象树控制一样方便地应用:CtreeCtrl &treeCtrl = GetTreeCtrl()。
4.2 树控制的对象结构
4.2.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消息
4.2.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等。
4.2.3 树控制的操作方法
    树控制的操作方法包括插入一个树项InsertItem、删除一个树项DeleteItem、删除所有树项DeleteAllItems、展开或合拢树项的子项Expand、选中特定树项SelectItem、选择一个树项作为第一个可视树项SelectSetFirstVisible、编辑一个可视的树项EditLabel和排序给定父树项的子树项SortChildren等。
4.3 树控制的数据结构
    在使用树控制时需要了解两个个非常重要的数据结构TV_ITEM和TV_INSERTSTRUCT,前一个数据结构是用来表示树控制的树项信息,后一个数据结构是用来定义将树项增加到数据控制中所需要的数据内容。另外,还需要NM_TREEVIEW、TV_DISPINFO和TV_HITTESTINFO三个数据结构,这几个数据结构的定义方法如下:
4.3.1 基本数据项结构
typedef struct _TV_ITEM {
    UINT       mask;       file://结构成员有效性屏蔽位
    HTREEITEM  hItem;      file://数据项控制句柄
    UINT       state;      file://数据项状态
    UINT       stateMask;  file://状态有效性屏蔽位
    LPSTR      pszText;    file://数据项名称字符串
    int        cchTextMax; file://数据项名称的最大长度
    int        iImage;     file://数据项图标索引号
    int        iSelectedImage;//选中数据项图标索引号
    int        cChildren;  file://子项标识
    LPARAM     lParam;     file://程序定义的32位数据
} TV_ITEM, FAR *LPTV_ITEM; 
4.3.2 插入树项结构
    typedef struct _TV_INSER TSTRUCT {
    HTREEITEM hParent;      file://父项控制句柄
    HTREEITEM hInsertAfter; file://插入树项的位置
    TV_ITEM   item;         file://数据项的结构
    } TV_INSERTSTRUCT, FAR *LPTV_INSERTSTRUCT; 
    其中插入的位置如果是TVI_FIRST 或TVI_LAST ,则分别插入到树控制的最前面或最后面,如果是TVI_SORT ,则插入的树项自动插入到合适的位置。
4.3.3 树控制通知消息结构
    typedef struct _NM_TREEVIEW {
    NMHDR    hdr;     file://通知消息句柄
    UINT     action;  file://通知消息标志
    TV_ITEM  itemOld; file://原来的数据结构
    TV_ITEM  itemNew; file://新的数据结构
    POINT    ptDrag;  file://拖动指针
    } NM_TREEVIEW; 
4.3.4 取得或设置数据结构
    typedef struct _TV_DISPINFO {  tvdi 
    NMHDR   hdr;      file://通知消息控制句柄
    TV_ITEM item;     file://数据项结构
    } TV_DISPINFO;
4.3.5 指4.3.6 针测试数据结构
    typedef struct _TVHITTESTINFO {  tvhtst 
    POINT     pt;     file://客户区域屏幕坐标指针
    UINT      flags;  file://存放测试结果的变量
    HTREEITEM hItem;  file://测试的数据项结构
    } 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      在客户区域的右侧
4.4 树控制的应用技巧示例
    这里仍以基于对话框演示实例来具体介绍树控制及其和图像列表相结构的应用技巧:
    通过“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); file://设置图象列表
    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
    }
<广告一下,呵呵:http://blog.csdn.net/wangjiwei2010>
    (3)增加树项功能的实现
    在增加树项功能时,除了需要定义和设置插入树项的数据结构之外,还需要注意的是新增树项的名称初始时均为“新增数据”,增加后允许用户给数据项设置自定义名称。在编程时应特别注意m_TreeCtrl.EditLabel(hInsert);后面不能跟任何其它程序命令,否则这条编辑指令无效。
    void CVCTREEDlg::OnAdd() 
    { file://增加子项功能函数
    HTREEITEM hSel=m_TreeCtrl.GetSelectedItem();//取得选择项句柄
    if(hSel==NULL) return;//无任何选项则返回
    static int nAddNo=100;//编号大于100为新增数据
    TV_INSERTSTRUCT TCItem;//定义插入项数据结构
    TCItem.hParent=hSel;   file://设置父项句柄
    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() 
    { file://删除子项功能函数
      HTREEITEM hSel=m_TreeCtrl.GetSelectedItem();//取得选项句柄;
     if(hSel==NULL) return;//无任何选项则返回
     if(m_TreeCtrl.ItemHasChildren(hSel))//判断是否有子项
       if(MessageBox("厂家下存在品名,一同删除?","警告",MB_YESNO)==IDNO) return;
   m_TreeCtrl.DeleteItem(hSel);
    }
    (5)排序功能的实现
    排序功能是对所选中的树项的所有子项按字符中顺序进行排序,如果想要按照其它规则进行排序,应利用SortChildrenItemBC()函数进行自行开发排序程序,这个自行开发的函数与列表控制中实现的函数基本相同,可兴趣的读可以试验。
    void CVCTREEDlg::OnSort() 
    { file://排序子项功能函数
    HTREEITEM hSel=m_TreeCtrl.GetSelectedItem();//取得选项句柄;
    if(hSel==NULL) return;//无任何选项则返回
    m_TreeCtrl.SortChildren(hSel);
    }
    (6)查看功能的实现
    查看功能用来查看选中树项的有关信息,函数中中显示了树项的文本名称和标识号,可以将这两个信息作为查找关键字,来查看其它更详细的信息。
    void CVCTREEDlg::OnView() 
    { file://查看选中项功能函数
    HTREEITEM hSel=m_TreeCtrl.GetSelectedItem();//取得选项句柄;
    if(hSel==NULL) return;//无任何选项则返回
    CString cText=m_TreeCtrl.GetItemText(hSel);//取得数据项名
    LONG IDs=m_TreeCtrl.GetItemData(hSel);//取得数据项序号
    char temp[100];
    wsprintf(temp,"厂家:%s 编号:%05d",cText,IDs);
    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,
  pTVDispInfo->item.pszText);//设置新数据
  *pResult = 0;
    }
7、树视的演练技巧
树视的应用技巧在使用树视时,其方法与树控制基本相同,只不过树视是在窗口中来实现的而树控制是在对话框中实现,树视的各种功能是通过菜单来实现的而树控制是通过按钮等方式来实现的,树控制需要在对话框中创建树控制控件而树视直接占据整个窗口,在设计过程中只要将按钮和树控制设计过程变为菜单设计,并注意在功能函数是在类向导中是通过菜单命令来操作,同时在每个功能函数前面增加取得列表视引用的命令(CTreeCtrl& TreeCtrl = GetTreeCtrl()),而其余数据结构和代码均不需要修改,实现起来比较容易。笔者实现的树控制和视程序的运行结果如下:
                    树控制的演练示例结果
              树视演练结果示例
第5章 演练CTab
5.1 标5.2 签控制的主要功能
    标签控制(Tab Control)是用来在一个窗口如对话框等中的同一用户区域控制多组显示信息或控制信息,由顶部的一组标签来控制不同的信息提示,标签即可以是文本说明也可以是一个代表文本含义的图标,或是两者的组合。针对不同的选择标签,都会有一组提示信息或控制信息与之相对应,供用户进行交互操作,这在WINDOWS98的属性表中最常见。另外还存在一种特殊风格的标签,即TBS_BUTTONS风格的标签,这种标签外观类似按钮,通过鼠标点击改变状态,一般用来执行一些功能而不是用来显示或控制信息。
    提到标签,最快想到的应该是属性表对话(Property Sheet),这两者的配合应用更是随处可见。属性表对话框有时也称为多页对话框(Multiple-Page Dialog)或是标签对话框(Table Dialog),最多可设置24个属性页(Property Page),通过顶部的标签来选择不同的属性页。另外还有一种特殊的属性表对话框,就象VC++5.0中的类向导AppWizard一样,其不存在供用户选择的标签,而是按照顺序依次控制属性页的显示,并且还有一般属性页中不存在的“确认”、“上一步”、“下一步”、“完成”和“帮助”等按钮。
    标签控制在MFC中只存在一种封装形式,即控制类CtabCtrl。在使用标签时即可以在对话框中直接添加,也可以在窗口中作为子窗口来使用,只不过这样应用时需要选创建标签。
5.3 标5.4 签控制的对象结构
5.4.1 标5.4.2 签控制的建立方法
    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键移动的标签控制
5.4.3 标5.4.4 签控制的属性类
    标签控制的属性类包括取得与标签控制相关联的图像列表GetImageList、设置标签控制的图像列表SetImageList、取得标签控制中标签的总数GetItemCount、取得标签控制中特定标答的相关信息GetItem、设置标签的部分或全部属性SetItem、检测当前被选中的标签GetCurSel、将一个标签设置为选中状态SetCurSel和取得具有当前输入焦点的标签SetCurSel等。
5.4.5 标5.4.6 签控制的操作方法
    标签控制的操作方法包括在标签控制中插入一个标签InsertItem、删除一个标签      DeleteItem、从标签控制中删除所有项目DeleteAllItems、从标签控制中删除一个图像列表RemoveImage和绘制标签控制中的特定一项DrawItem等。
5.5 标5.6 签控制的数据结构
    在使用标签控制时,必须使用的函数就是在标签控制中插入标签。函数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; 
    当鼠标点击标签控制中的标签时,标签控制就会向其父窗口发送相关的通知消息,通过处理这些通知消息,程序可以实现各种功能。
5.7 属性表和属性页的基本用法
    在标签控制过程中,属性表对话框和属性页是必不可少的。在MFC类库中,属性表对话框类CpropertySheet是由CWnd类派生而来的,而属性页类CpropertyPage是由Cdialog类派生而来的,它们的用法基本相同:
    1、创建所有的属性页。创建属性页的方法与创建一般对话框资源的方法一样,利用对话框编辑器可以为每个属性页创建一个对话框模板,其区别在于,当利用类向导ClassWizard为属性页生成类时应选择属性页类CpropertyPage作为基类,而不是将一般的对话框类Cdialog作为基类;
    2、创建属性表对话框,并将事先创建好的各属性页添加进去,两者的创建顺序可以互换,但在创建完之后将属性页添加到属性表对话框中去这一步是必须要做的;
    3、显示属性表对话框。虽然属性表对话框类CpropertySheet不是由对话框类Cdialog派生而来的,但两者的操作非常类似,调用DoModal()函数就会显示一个模态属性表对话框,而调用Create()操作就会显示一个非模态的属性表对话框;
    4、对数据交换的处理。和对话框类似,属性表对话框与对象之间的数据交换也是通过数据成员2来实现的,只是属性表本身不带数据成员,而实际进行数据交换的是属性页中的数据成员;
    5、对向导对话框的处理。如果要显示一个向导对话框,在显示之前应首先调用SetWizardMode()函数对向导对话框进行特殊处理,对于存在按钮的向导对话框,还应调用SetWizardButtons()来对向导对话框的按钮功能进行定制,在用户操作结束时还应调用SetFinishText()函数将“完成”按钮设置为有效状态。
5.8 标5.9 签控制的应用技巧示例程序
  本文给出一个基于文档的标签应用实例。实例程序中通过简单设置菜单、标签和属性表来演示标签控制的实际应用技巧,程序通过选择菜单选项弹出设置正文颜色、字体和修饰等属性表对话框来和用户进行简单交互。其实现步骤如下:
    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 "TabDlg.h"
#include "TextPage.h"
#include "StylePage.h"
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; file://显示文字属性页
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);   file://设置黑色
 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;i<j+k;i++){
  pDC->MoveTo(i*nDist,0);
  pDC->LineTo(0,i*nDist);
  if(i<j){
    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);
}
标签控制的整个实现过程虽然比较繁锁,但只要掌握其实现的本质,设计一个优秀的标签控制界面也并非很困难的事情。
第6章 演练CToolBar
6.1 工具条控制的主要功能
    所谓工具条就是具有位图和分隔符组成的一组命令按钮,位图按钮部分可以是下推按钮、检查盒按钮、无线按钮等。工具条对象类派生于主窗口架框类CframeWnd或CMDIFrameWnd,其类控制CToolBar::GetToolBarCtrl是MFC类库中封装的一个成员函数,允许使用类库中提供的一般控制和附加功能,CtoolBar类控制成员控制提供了Windows一般控制的所有功能,然而,通过调用 GetToolBarCtrl成员函数取得引用后,可以使工具条具有更强的特性。
    工具条的创建具有四个步聚:首先是建立工具条资源;然后建立工具条对象结构;其次通过调用建立函数建立工具条对象并绑定;最后调用LoadToolBar调入工具条资源。
    另外,还可以通过直接加载位图的方法来建立,步骤如下:首先建立工具条对象;然后通过调用建立函数建立工具条并绑定对象;其次调入包含按钮的位图;最后利用SetButtons 函数设置按钮的风格并与位图建立联系。
    其中,所有按钮位图均存放在一个位图文件中,按钮位图的大小相同,默认为16点宽、15点高,位图必须从左至右存放。设置按钮函数具有指向一组控制标识符ID的指针和索引值,用来确定每个按钮的位置,如果存在分隔符ID_SEPARATOR, 那么该图像就不存在索引值。正常情况下工具条中的按钮都是单排从左至右排列的,可以通过SetButtonInfo函数改变排序规则。 工具条中最终形成的按钮大小相同,均为24 x 22 象素,每个按钮只对象一幅图像。工具条中的按钮默认为下推按钮,通过设置TBBS_CHECKBOX风格可以实现检查盒按钮,通过调用SetRadio成员函数可以实现无线按钮。
6.2 工具条控制的对象结构
6.2.1 工具条的对象结构
6.2.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等控制函数。
6.2.1.2 工具条的类属性
  工具条控制类的属性包括取得标识符ID对象按钮索引CommandToIndex、取得索引对应的命令标识符ID或分隔符GetItemID、取得索引对应的矩形区域GetItemRect、取得按钮风格    GetButtonStyle、设置按钮风格SetButtonStyle、取得按钮的ID标识-风格-图象数GetButtonInfo、设置按钮ID标识-风格-图象数SetButtonInfo、取得按钮提示文本GetButtonText、设置按钮提示文本SetButtonText和取得工具条直接存取控制GetToolBarCtrl等。
6.2.2 工具条控制的对象结构
6.2.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   使工具条控制按钮具有多行排列格式
6.2.2.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      表示建立按钮分隔符
6.2.2.3 工具条控制的类属性
    工具条控制的类属性必然的联系判断按钮使能状态IsButtonEnabled、判断按钮检查状态    IsButtonChecked、判断按钮按下状态IsButtonPressed、判断按钮是否隐藏IsButtonHidden、判断按钮变灰状态IsButtonIndeterminate、设置按钮状态SetState、取得按钮状态GetState、取得按钮有关信息GetButton、取得按钮总数GetButtonCount、取得按钮矩形区域GetItemRect、设置按钮结构大小SetButtonStructSize、设置按钮大小SetButtonSize、设置按钮位图大小SetBitmapSize、取得按钮提示控制GetToolTips、设置按钮提示控制SetToolTips等。
6.2.2.4 工具条控制类的操作方法
    工具条控制类的操作方法包括使能按钮EnableButton、检查按钮CheckButton、按下按钮PressButton、隐藏按钮HideButton、变灰按钮Indeterminate、增加按钮AddButtons、插入按钮InsertButton、删除按钮DeleteButton、取得控制符ID对应的索引CommandToIndex、恢复工具条状态RestoreState、保存工具条状态SaveState和重新确定工具条大小AutoSize等。
6.3 工具条控制的应用技巧
    可以这样说,工具条和上述常用控制是应用程序中不可缺少的功能元素,它的优劣会直接影响程序的基本功能和操作特性。所以这里将对工具条的建立技巧、状态保存与恢复、平面特性、停靠位置、排序方法、消息映射、状态更新、控制使用和属性控制等方面,全面阐述工具条的使用技巧。
6.3.1 工具条的建立技巧
6.3.1.1 普通工具条的建立方法
    如果应用程序在建立时就具有工具条,则只需对工具条中的按钮图标进行简单的增加、修改和删除等操作就可满足要求。如果未建立或者想增加其它工具条,则应按步骤追加建立。
    首先打开已建立好的基于单文档的框架工程文件CTool并选择"Insert->Resource->ToolBar"选项,插入工具条资源并设置资源标识符;然后编辑工具栏中的按钮图标和相应的按钮标识符,并利用类向导ClassWizard 为按钮消息增加COMMAND和UPDATE_COMMAND_UI两种处理函数;在资源文件中增加和修改工具条图标的动态提示等内容;打开MainFrm.h包含文件在"CToolBar m_wndMainToolBar"后增加"CToolBar m_wndTestToolBar" 等来创建增加的工具条对象;在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;  file://框架程序的状态条
        CTestToolBar m_wndMainToolBar;//框架程序的工具条
        CTestToolBar m_wndTestToolBar;//新增工具条
        CTestToolBar m_wndDockToolBar;//浮动工具条
        CTestToolBar m_wndDockNextBar;//浮动工具条
    ......//其它代码
    }
    框架程序中工具条的控制类正常应为CToolBar,可以是自己设计的派生类CtestToolBar(为笔者扩充平面特性等功能后的新工具条控制类名)等,具体根据实际需要而定。利用CDialogBar类和CStyleBar 类还可以建立扩展类型的工具条,详见后面工具条中控制应用技巧,但在该文件头处必须
包含如下命令:
    #ifndef __AFXEXT_H__
    #include <afxext.h>//直接存取CToolBar和CStatusBar
    #endif
    ②在MainFrm.cpp中完善窗口建立函数
    int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
    {  if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
        return -1;
        WINDOWPLACEMENT wp;//保存主窗口及工具栏窗口位置状态
        if (ReadWindowPlacement(&wp))//读取位置状态信息
           SetWindowPlacement(&wp);  file://设置位置状态信息
        m_bToolTips=(AfxGetApp()->GetProfileInt(//读提示功能
           _T("General"),_T("ToolTips"),1)!=0); file://默认值为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))
        {  file://CBRS_SIZE_DYNAMIC为锁定位置风格
           TRACE0("主工具条MAINFRAME建立失败/n");
           return -1;} // 建立失败处理
        ......//建立其它工具条代码,基本相同
        if (!m_wndStatusBar.Create(this)||
           !m_wndStatusBar.SetIndicators(indicators,
           sizeof(indicators)/sizeof(UINT)))
        {  file://建立状态条
           TRACE0("Failed to create status bar/n");
           return -1;} // fail to create
        m_wndMainToolBar.SetWindowText(_T("主工具栏"));//设置标题
        m_wndMainToolBar.EnableDocking(CBRS_ALIGN_ANY);//停靠位置
        file://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("General"),_T("Columns"),3));//恢复列格式,默认为3
        DockControlBarLeftOf(&m_wndDockNextBar,&m_wndDockToolBar);
        m_wndDockNextBar.SetColumns(AfxGetApp()->GetProfileInt(
          _T("General"),_T("Columns"),3));
        LoadBarState(_T("General"));//恢复保存的状态和位置
        return 0;
    }
    以上建立过程除工具条建立和资源调用函数外,还涉及到了窗口和工具条的状态保存和恢复函数、注册表参数读取函数、工具条停靠位置函数、工具条标题修改函数、工具条连接函数、工具条列格式控制函数和工具条风格修改函数,其中工具条建立函数中的风格设置很重要,如果建立的工具条需要重新设置多行多列的排序功能,除正确设置工具条停靠位置参数外,还必须设置CBRS_SIZE_FIXED 风格,即允许程序改变工具条窗口的尺寸,如果工具条不需要重新排序,则必须设置为CBRS_SIZE_DYNAMIC 风格,否则工具栏不但不能进行重新排序和正确停靠到理想的位置,而且也无法正确保存和恢复工具条的位置和状态,这一点应引起编程者高度重视。其余函数以后分别介绍。
6.3.1.2 浮动工具条的建立方法
    如果要建立浮动工具条,必须使用如下工具条的控制方法:
    Cpoint pt(GetSystemMetrics(SM_CXSCREEN)-100,GetSystemMetrics(SM_CYSCREEN)/3);
    FloatControlBar(&m_wndPaletteBar,pt);//浮动工具条
6.3.1.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 函数必须放在所有工具条建立函数之前,否则可能出现很难发现的错误,如特殊工具条初始位置控制等。工具条的所有特性均在上述建立函数中确定,所以其建立过程是实现理想工具条的关键环节。
6.3.2 工具条状态保存和恢复6.3.3 
    很多应用程序中都具有保存和恢复应用程序及其工具条等状态的功能,即下次启动应用程序后进入上次的运行状态,这种功能只需进行一次界面布局便可永久保存,极大方便用户。
    要正确保存和恢复应用程序界面状态,必须对应用程序窗口和工具条窗口等均进行保存和恢复,这需要完善应用程序的建立和关闭过程。具体步骤如下:
    (1)首先利用类向导ClassWizard为应用程序增加窗口关闭WM_CLOSE消息处理功能OnClose();
    (2)在MainFrm.cpp中为应用程序状态设置成员变量
    static TCHAR BASED_CODE szSection[]=_T("Settings");
    static TCHAR BASED_CODE szWindowPos[]=_T("WindowPos");
    static TCHAR szFormat[]=_T("%u,%u,%d,%d,%d,%d,%d,%d,%d,%d");
    (3)编制窗口位置状态读取和写入函数
    static BOOL PASCAL NEAR ReadWindowPlacement(LPWINDOWPLACEMENT pwp)
    {   file://窗口位置状态读取函数,从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;            file://结构指针
        return TRUE;
    }
    static void PASCAL NEAR WriteWindowPlacement(
        LPWINDOWPLACEMENT pwp)
    {   file://窗口位置状态写入函数,写到INI文件
        TCHAR szBuffer[sizeof("-32767")*8+sizeof("65535")*2];
        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);  file://设置位置状态信息
    (5)在应用程序建立函数OnCreate()中增加工具条状态恢复功能
        m_wndDockToolBar.SetColumns(AfxGetApp()->GetProfileInt(
          _T("General"),_T("Columns"),3));//恢复列格式,默认为3
        m_wndDockNextBar.SetColumns(AfxGetApp()->GetProfileInt(
          _T("General"),_T("Columns"),3));
        LoadBarState(_T("General"));//恢复保存的状态和位置
    (6)在应用程序关闭函数OnClose()中完善状态保存功能
    void CMainFrame::OnClose()
    {   file://保存工具条等的状态
        SaveBarState(_T("General"));//保存工具条状态
        AfxGetApp()->WriteProfileInt(_T("General"),//写入列数
          _T("Columns"),m_wndDockToolBar.GetColumns());
        AfxGetApp()->WriteProfileInt(_T("General"),
          _T("ToolTips"),(m_bToolTips!=0));//写入提示功能
        WINDOWPLACEMENT wp;
        wp.length=sizeof wp;
        if (GetWindowPlacement(&wp)){
           wp.flags=0;
           if (IsZoomed()) wp.flags|=WPF_RESTORETOMAXIMIZED;
              file://如果窗口被放大,则保存为最大化状态
              WriteWindowPlacement(&wp);
        }
        CFrameWnd::OnClose();
    }
    虽然SaveBarState()和LoadBarState()函数保存和恢复了工具条的所有默认位置状态,但在实际自己实现的功能参数部分并不能被保存,所以应单独编写这些参数的保存代码,如工具栏的排列格式列参数值、颜色状态标志和是否存在动态提示功能标志等,在实际编程时一定要注意。
6.3.4 工具条的平面特性
    工具条的平面特性给人耳目一新之感,很多大型应用程序中的工具条都采用这一特性,并取得了巨大成功。利用VC++5中的COMCTL32.DLL动态链接库可以实现平面式工具条,其主要解决问题包括:由于MFC使用风格控制位来控制工具条的外观,所以在建立工具条时不能直接设置这种风格,必须在建立后利用SetFlatLookStyle()函数来修改;工具条控制本身也不在各级按钮之间绘制分隔线,其另一个任务就是截取WM_PAINT消息,并在相应的位置处增加分隔线;工具条控制也不绘制左边的把手(gripper) ,最后的任务就是调整客户区域并绘制并绘制相应的gripper。
    显然,实际工作中需要动态链接库COMCTL32.DLL支持的上述方法很不方便。尽管最简便的方法是利用VC++ 5中的未公开工具栏风格TBSTYLE_FLAT,可以得到工具条的平面特性,只需在工具条建立后简单地增加一条代码"m_WndMainToolBar.ModifyStyle(0,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(); file://新类构造函数
        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 file://增加插入控制
        virtual void AssertValid() const;
        virtual void Dump(CDumpContext& dc) const;
    #endif
    protected:    file://增加成员变量
        UINT m_nColumns; file://工具栏按钮列数
        UINT m_nFlags;   file://鼠标按键标志
        int  m_nIndex;   file://按下的按钮号
        int  m_nFlagl;   file://左键按下标志
        UINT m_nStated;  file://工具栏状态
        CRect rt;        file://关闭按钮矩形区域
    ......//其它代码
    }
    (3)完善派生类实现文件TestToolBar.cpp内容
    ......//其它代码
    #define TOOLLEFT    18
    #define LBUTTONDOWN 1
    #define LBUTTONUP   2
    ......//其它代码
    CTestToolBar::CTestToolBar()
    {   file://在构造函数中初始化变量
        m_nColumns=0;      file://工具栏按钮列数
        m_cxLeftBorder=16; file://左边界
        m_cxRightBorder=3; file://右边界
        m_cyTopBorder=3;   file://顶边界
        m_cyBottomBorder=3;//底边界
        m_nFlags=0;        file://按键标志成员变量
        m_nIndex=0xffff;   file://按下的按钮号
        m_nFlagl=0;        file://左键按下标志
        m_nStated=TRUE;    file://工具栏状态
    }
    ......//其它代码
    #ifdef _DEBUG//插入代码完善
    void CTestToolBar::AssertValid() const
    {   CToolBar::AssertValid(); }
    void CTestToolBar::Dump(CDumpContext& dc) const
    {   CToolBar::Dump(dc); }
    #endif file://_DEBUG
......//其它代码
    虽然需要实现的函数比较多,但总起来说不过是取得客户区域或窗口所有区域的文本设备、建立画笔和绘图函数的集合,所以这里只给出了画按钮凸凹边线的函数,其它函数可仿造实现。
    void CTestToolBar::OnDrawBorder(int index,CDC &dc,int flag)
    {   file://画按钮边线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; file://按钮阴影颜色
        } 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);     file://画按钮边暗线
        dc.MoveTo(rect.right,rect.top);
        dc.LineTo(rect.right,rect.bottom);
        dc.LineTo(rect.left,rect.bottom);
        file://dc.SelectStockObject(BLACK_PEN);//画按钮边黑线
        file://dc.MoveTo(rect.right+1,rect.top);
        file://dc.LineTo(rect.right+1,rect.bottom+1);
        file://dc.LineTo(rect.left,rect.bottom+1);
        dc.SelectObject(oldpen);
        DeleteObject(pen1);
        DeleteObject(pen2);
    }
    void CTestToolBar::OnDrawBorders()
    {  file://实现平面工具条
        CRect rect;
        CPoint pt;
        GetCursorPos(&pt);  file://取得鼠标指针
        ScreenToClient(&pt);//变成窗口坐标
        int index;
        int count=GetCount();//工具条按钮总数
        CClientDC dc(this); file://窗口客户区域
        TBBUTTON button;    file://按钮数据结构
        CToolBarCtrl &ToolBarCtrl=GetToolBarCtrl();
        OnDrawGrapper();    file://画把手
        for(index=0;index<count;index++){
            GetItemRect(index,&rect);//取得按钮矩形区域
            rect.left++;rect.top++;
            ToolBarCtrl.GetButton(index,&button);//取得按钮信息
            if(button.fsState&(TBSTATE_CHECKED|TBSTATE_HIDDEN))
              continue;
            if(button.fsStyle&TBSTYLE_SEP){//画分隔线
              if(m_nNew!=0) OnDrawSep(index,dc);
              } else if ((m_nIndex==index)||
                button.fsState&TBSTATE_PRESSED){//凹按钮
                OnEraseBorder(index,dc);//删除按钮边界
                if (rect.PtInRect(pt)) OnDrawBorder(index,dc,1);//绘下凹按钮
                   else OnDrawBorder(index,dc,0);//绘凸出按钮
              } else if (!rect.PtInRect(pt)||m_nFlags==LBUTTONUP||
                  !(button.fsState&TBSTATE_ENABLED)){
                  OnEraseBorder(index,dc);//删除按钮边界
              } else if (m_nFlags!=LBUTTONDOWN){//凸按钮
                  OnEraseBorder(index,dc);//删除按钮边界
                  if(m_nFlagl==0)//鼠标按下防止再次重新出现凸起
                     OnDrawBorder(index,dc,0);//绘按钮边界
              }
            m_nFlags=0;//按下后移动后不正常凸起
        }
        ReleaseDC(&dc);
    }
    void CTestToolBar::OnPaint()
    {   file://完善重绘按钮功能
        CToolBar::OnPaint();
        OnDrawBorders();//处理所有按钮边界
    }
    void CTestToolBar::OnLButtonDown(UINT nFlags, CPoint point)
    {   file://完善鼠标左键按下功能
        m_nFlags=LBUTTONDOWN;//设置鼠标按键标志
        m_nFlagl=1;
        CToolBar::OnLButtonDown(nFlags,point);//调原函数
        int index;
        int count=GetCount();//工具栏按钮总数
        TBBUTTON button;
        CToolBarCtrl &ToolBarCtrl=GetToolBarCtrl();
        for(index=0;index<count;index++){
          ToolBarCtrl.GetButton(index,&button);//取得按钮信息
          if (button.fsState&TBSTATE_PRESSED){ file://记录按下按钮号
            m_nIndex=index;
          }
        }
    }
    void CTestToolBar::OnLButtonUp(UINT nFlags, CPoint point)
    {   file://完善鼠标释放功能
        m_nFlags=LBUTTONUP;//设置鼠标按键标志
        m_nFlagl=0;
        CToolBar::OnLButtonUp(nFlags, point);//调原函数
        CRect rect;
        CPoint pt;
        GetCursorPos(&pt);//取得光标位置
        ScreenToClient(&pt);//变成窗口坐标
        CClientDC dc(this);//窗口客户区域
        if (m_nIndex!=0xffff){//判断按下按钮执行功能时仍下凹
           GetItemRect(m_nIndex,&rect);//取得矩形区域
           rect.left++;rect.top++;
           OnEraseBorder(m_nIndex,dc);//删除按钮边界
           if (rect.PtInRect(pt)) OnDrawBorder(m_nIndex,dc,1);//绘下凹按钮
        }
        m_nIndex=0xffff;
    }
    void CTestToolBar::OnMouseMove(UINT nFlags, CPoint point)
    {   file://完善鼠标移动功能
        CToolBar::OnMouseMove(nFlags, point);
        int index;
        int count=GetCount();//工具栏按钮总数
        CRect rect;
        if (nFlags&MK_LBUTTON) m_nFlagl=1;//防止再次重新出现凸起
        else m_nFlagl=0;
        OnDrawBorders();//绘制所有按钮
        for(index=0;index<count;index++){//判断鼠标在哪个按钮上
           GetItemRect(index,&rect);
           rect.left++;rect.top++;
           if (rect.PtInRect(point)&&//取得移动过程中输入焦点
              !(GetButtonStyle(index)&TBBS_SEPARATOR)){
              SetCapture();//设置鼠标输入焦点
              return;
           }
        }
        if (nFlags&MK_LBUTTON){//防止移出而失去输入焦点
           SetCapture();//设置鼠标输入焦点
           m_nFlagl=1;
           return;
        } else m_nFlagl=0;
        ReleaseCapture();
        return;
    }
    void CTestToolBar::OnNcPaint()
    {   file://背景重画函数
        CToolBar::OnNcPaint();
        OnDrawGrapper();
    }
    void CTestToolBar::SetState(UINT nLeft,BOOL nStated)
    {   file://状态设置函数
        m_cxLeftBorder=nLeft;//左边界
        m_nStated=nStated;   file://工具栏状态
    }
    (4)有关派生类函数几点说明
    ①画按钮凹凸边线函数OnDrawBorder()
    正常工具条中的按钮具有黑色的边线,使按钮凹凸感更强烈,但在平面工具条中的这种按钮并不美观,所以应省略黑色边线部分,并且必须使用系统的API函数GetSysColor函数来取得边线颜色,以便系统改变颜色时按钮边线也随之改变,同时由于凹凸按钮边线画法完全相同,只是颜色相反,所以两者完全可由这个函数来实现;
    ②画分隔线函数OnDrawSep()
    画分隔线时应遍历每个按钮,来取得分隔线的位置,并且利用客户区域文本描述表就可实现,只需画亮暗两条线就可实现;
    ③画把手函数OnDrawGripper()
    画把手时应使用整个窗口的文本描述表,因为客户区域描述表不能在窗口的非客户区域画线,而且还必须判断按钮是否以多行多列方式排列,根据不同的排列方式画水平或垂直把手,同时还要实现画关闭按钮功能,以和VC++5 等界面工具栏功能完全相同,另外还要判断工具栏是否为子窗口状态,以确定是否画把手和关闭按钮;
    ④删除按钮边线函数OnEraseBorder()
    函数用于消除系统所绘按钮凹凸边线,使按钮具有平面效果,也必须利用系统的API函数GetSysColor函数来取得系统颜色,以保证系统改变颜色时能够正常消除按钮边线;
    ⑤实现平面工具栏所有功能函数OnDrawBorders()
    在该函数中应特别注意对按钮分隔符判断、按钮凹凸状态判断、鼠标左键按下后按钮凹凸状态判断、删除系统所画按钮边线判断判断和按下鼠标左键并移动鼠标按钮的凹凸状态判断等,并需要利用工具条控制取得对工具栏的引用;
    ⑥工具条更新功能函数OnPaint()
    在这个函数中应注意对原系统更新功能函数的调用,以实现动态提示和按钮图标的显示等功能;
    ⑦鼠标左键按下功能函数OnLButtonDown()
    该函数中除需要调用原系统鼠标左键按下功能函数,以实现消息的发送等功能外,还需要设置鼠标左键按下标志并记录按下按钮的位置,以便程序正确判断按钮的凹凸状态;
    ⑧鼠标左键释放功能函数OnLButtonDown()
    该函数中除需要调用原系统鼠标左键释放功能函数,以实现按钮执行消息的发送等功能外,还需要设置鼠标左键释放标志,以便程序正确判断按钮的凹凸状态,此外还应重绘按钮凹下状态以使按钮功能执行时按钮应处于凹下状态,来保证工具栏按钮与其它高级应用程序实现的功能完全相同;
    ⑨鼠标移动功能函数OnMouseMove()
    该函数中应记录鼠标左键按下状态标志,并在鼠标移动到按钮上和鼠标左键按下时设置鼠标输入焦点,来保证平面工具条在鼠标移动过程中的正常凸起状态和鼠标点击按钮后对按钮状态的控制,如利用这一点可实现鼠标点击按钮后按钮下凹,不释放鼠标并移动到任何位置时按钮凸起,重新移动到按钮上按钮仍下凹,这些全是控制鼠标焦点的功能,并及时释放鼠标输入焦点;
    ⑩背景重绘等函数OnNcPaint()
    背景重绘函数是用来防止一个工具条被切换显示状态后,另一个工具栏中的把手和关闭按钮等消失,这在鼠标反复双击排序后工具条非客户区域时,另一个排序后工具栏就会出现的现象;另外函数SetState()用来设置工具条左边界和状态,以便为画把手和关闭按钮调整客户区域并提供绘图状态。
    此外,还有鼠标移动到把手上光标改变形状和关闭按钮功能,由于篇幅所限这里从略,有兴趣的读者完全可以自己实现。
6.3.5 工具条的停靠位置
6.3.5.1 标6.3.5.2 准工具条的停靠位置
    工具条类CToolBar是控制条类CControlBar 的派生类,其显示的初始停靠位置是通过调用继承的函数CControlBar::EnableDocking(DWORD dwStyle)来确定的,其参数dwStyle用来指定停靠具体位置,与本文有关的风格如下,其余请参阅VC5的联机帮助:
    CBRS_ALIGN_TOP    工具条停靠在客户区域顶部
    CBRS_ALIGN_BOTTOM 工具条停靠在客户区域底部
    CBRS_ALIGN_LEFT   工具条停靠在客户区域左边
    CBRS_ALIGN_RIGHT  工具条停靠在客户区域右边
    CBRS_ALIGN_ANY    工具条停靠在客户区域任何位置
    利用应用程序向导AppWizard 生成的应用程序,其默认的停靠位置为CBRS_ALIGN_ANY,即允许停靠在客户区域的任何边,正常显示时为靠近客户区域的顶部:EnableDocking(CBRS_ALIGN_ANY) ,详见上述的工具栏建立函数ONCREATE()。
    应用程序的单文档和多文档的窗口框架类均为CFrameWnd 的派生类,其指定工具条的停靠位置均是通过调用继承的函数 CFrameWnd::EnableDocking(DWORD dwDockStyle)来实现的,其可选的参数除上述五种之外,还增加了CBRS_FLOAT_MULTI参数,这个参数主要是为设计浮动工具条而增加的,其用来确定一个框架窗口中允许存在多个浮动工具栏。同样利用应用程序向导AppWizard 生成的应用程序,其默认的停靠位置也是CBRS_ALIGN_ANY,即允许停靠在框架窗口的任何边,正常显示时为靠近框架窗口的顶部,即为EnableDocking(CBRS_ALIGN_ANY),详见上述的工具条建立函数ONCREATE()。
6.3.5.3 浮动工具条的停靠位置
    当一个框架窗口中存在多个浮动工具条时,需要利用函数void DockControlBar(CControlBar *pBar,UINT nDockBarID=0,LPCRECT lpRect= NULL)来确定要控制停靠位置的工具条,它也是CFrameWnd类的成员函数,其中参数pBar用来指向被控制停靠位置的工具条对象,参数nDockBarID用来确定工具条停靠在框架窗口的哪条边上,取值为:
    AFX_IDW_DOCKBAR_TOP    工具条停靠在框架窗口的顶部
    AFX_IDW_DOCKBAR_BOTTOM 工具条停靠在框架窗口的底部
    AFX_IDW_DOCKBAR_LEFT   工具条停靠在框架窗口的左边
    AFX_IDW_DOCKBAR_RIGHT  工具条停靠在框架窗口的右边
    如果参数nDockBarID取值为0,则工具条可以停靠在框架窗口中的任何一个可停靠的边上,其默认位置为顶部。
6.3.5.4 工具条的连接停靠方法
    在很多应用程序中都存在将多个工具条同时停靠在某窗口的某一条边上的同一工具条窗口中的情况,利用上述工具条控制函数DockControlBar的lpRect参数,通过控制工具条的停靠矩形区域来实现这个功能,如笔者实现的函数如下:
    ①在主程序实现文件MainFrm.h中增加函数定义
    public:
      void DockControlBarLeftOf(CToolBar* Bar,CToolBar* LeftOf);
    ②在主程序实现文件MainFrm.cpp中增加如下函数
    void CMainFrame::DockControlBarLeftOf(
         CToolBar* Bar,CToolBar* LeftOf)
    {   file://设置工具条停靠在同一边窗口中
        CRect rect;
        DWORD dw;
        UINT n;
        RecalcLayout();//重新显示
        LeftOf->GetWindowRect(&rect);
        rect.OffsetRect(1,0);//设置偏移值以停靠在同一窗口中
        dw=LeftOf->GetBarStyle();
        n=0;
        n=(dw&CBRS_ALIGN_TOP)?AFX_IDW_DOCKBAR_TOP:n;
        n=(dw&CBRS_ALIGN_BOTTOM&&n==0)?AFX_IDW_DOCKBAR_BOTTOM:n;
        n=(dw&CBRS_ALIGN_LEFT&&n==0)?AFX_IDW_DOCKBAR_LEFT:n;
        n=(dw&CBRS_ALIGN_RIGHT&&n==0)?AFX_IDW_DOCKBAR_RIGHT:n;
        DockControlBar(Bar,n,&rect);
    }
    在这个函数中应注意对RecalcLayout()函数和OffsetRect()函数的调用,前一个函数用来重新显示被调整的客户区和工具条,后一个函数用来重新确定矩形区域,这相当于用鼠标将第二个工具条拖动到前一个工具条上。
    ③修改应用程序建立函数OnCreate()函数中的相应DockControlBar()函数为DoctControlBarOf()函数,并正确设置工具条指针,见工具条的建立技巧中的有关函数。
6.3.5.5 定制工具条的顶部停靠控制
    另一种工具条的停靠位置是定制工具条的停靠位置,如具有通用控制功能工具条的停靠位置,这主要实现左侧定制工具条与顶部工具条之间的位置关系。其实现方法如下:
    ①打开菜单资源增加顶部位置控制菜单项IDD_DLGBARTOP;
    ②在实现文件MainFrm.h中增加成员控制变量m_bDialogTop;
    BOOL m_bDialogTop;
    并在构造函数中为其设置初始值;
    ③利用类向导函数为菜单项设置响应函数;
    ④在实现文件MainFrm.cpp中完善消息映射函数。
    void CMainFrame::OnButtonDlgbartop()
    {   file://定制工具条顶部位置控制函数
        if (m_bDialogTop) m_wndDlgBar.SetWindowPos(
           &m_wndStatusBar,0,0,0,0,SWP_NOSIZE|SWP_NOMOVE);
           file://其它工具条停靠在最顶部,该工具条停靠其下
        else m_wndDlgBar.SetWindowPos(&wndTop,0,0,0,0,
           SWP_NOSIZE|SWP_NOMOVE);//停靠在最顶部
        RecalcLayout(); file://重新显示窗口
        m_bDialogTop=!m_bDialogTop;//改变变量标志
    }
    void CMainFrame::OnUpdateButtonDlgbartop(CCmdUI* pCmdUI)
    {   file://设置菜单项检查状态
        pCmdUI->SetCheck(m_bDialogTop);
    }
6.3.6 工具条按钮的排序方法
    利用应用程序向导AppWizard 生成的应用程序工具条,其按钮均为单行水平排列的,这在实际程序开发时既不美观又不实用,很多大型应用程序等界面中的工具条都采用多行多列的排序方式,要在自己的应用程序中实现这种排列方式,应按下述方法在派生类中控制:
    (1)在TestToolBar.h中增加函数定义和成员变量控制
    class CTestToolBar : public CToolBar
    {......//其它代码
    public:
        CTestToolBar(); file://在构造函数后增加下一行
        void SetColumns(UINT nColumns);//增加列控制
    ......//其它代码
    protected:    file://增加成员变量
        UINT m_nColumns; file://工具条列按钮数
    ......//其它代码
    }
    (2)在TestToolBar.cpp中增加变量初始化和函数
    CTestToolBar::CTestToolBar()
    {   file://在构造函数中初始化变量
        m_nColumns=0;      file://工具条按钮列数
        ......//其它代码
    }
    void CTestToolBar::SetColumns(UINT nColumns)
    {   file://设置按钮排列格式
        m_nColumns=nColumns;//列数
        int nCount=GetToolBarCtrl().GetButtonCount();//按钮数
        for(int i=0;i<nCount;i++){
           UINT nStyle=GetButtonStyle(i);//按钮风格
           BOOL bWrap=(((i+1)%nColumns)==0);
           if(bWrap) nStyle|=TBBS_WRAPPED;//设置换行
           else nStyle&=~TBBS_WRAPPED;//不换行
           SetButtonStyle(i,nStyle);//设置风格
        }
        Invalidate();//窗口更新
        GetParentFrame()->RecalcLayout();//工具栏状态更新
    }
    (3)在应用程序建立函数OnCreate()中为相应的工具条增加列控制功能,并注意对保存和恢复工具条状态函数的列控制参数处理,请参阅工具条建立技巧和状态保存与恢复中的有关函数,重新编译并执行应用程序就可以看到多行多列的工具条。
6.3.7 工具条的消息映射技巧
    如果工具条上的按钮都存在对应的菜单命令,那么可以直接利用类向导ClassWizard 进行命令消息的映射,否则必须通过手工的方法来进行命令按钮的消息映射。由于同一工具栏的命令按钮都存在相类似的功能,所以只要注意将同一工具条中的命令按钮ID值设置成连续值,就可以利用范围命令处理机制统一对所有按钮命令进行管理,即所有命令按钮的消息可以在同一命令消息处理函数和更新命令消息处理函数中进行管理,这样不但结构紧凑而且维护特别方便。鉴于类向导ClassWizard 不支持范围命令处理功能,所以其消息映射机制必须通过手工方式来完成。按钮命令消息既可以在框架窗口中映射,也可以在文档和视图中完成映射,由于文档对象通常用来管理程序中的数据部分,并且在多文档应用程序(MDI) 中,一个文档可能有多个视图与之关联,因此把工具栏中的命令按钮消息映射在文档中比较合适些。其实现步骤如下:
    (1)在文档实现文件CToolDoc.h中增加函数定义和数据成员:
    class CCTOOLDoc : public CDocument
    {......//其它代码
    protected:
    UINT iPosition; file://命令按钮消息位置
    ......//其它代码
    protected:
        file://{{AFX_MSG(CCTOOLDoc)
        afx_msg void OnTool(UINT nID);//命令消息映射
        afx_msg void OnUpdateTool(CCmdUI* pCmdUI);//更新消息映射
        file://}}AFX_MSG
        DECLARE_MESSAGE_MAP()
    ......//其它代码
    }
    (2)在文档实现文件CToolDoc.cpp中进行消息映射
    BEGIN_MESSAGE_MAP(CCTOOLDoc,CDocument)
        file://{{AFX_MSG_MAP(CCTOOLDoc)
        ON_COMMAND_RANGE(ID_BUTTON_LINE,
          ID_BUTTON_SORT,OnTool);
        ON_UPDATE_COMMAND_UI_RANGE(ID_BUTTON_LINE,
          ID_BUTTON_SORT,OnUpdateTool);
        file://}}AFX_MSG_MAP
    END_MESSAGE_MAP()
    函数中的参数ID_BUTTONLINE和ID_BUTTONSORT分别为工具栏中第一个按钮和最后一个按钮的消息,如果要增加或减少了按钮数,则必须使其ID值保持连续,并且需要修改函数中的最后一个消息值。对应函数的代码如下:
    void CtoolDoc::OnTool(UINT nID)
    {    file://按钮消息处理函数
         iPosition=nID-ID_BUTTON_LINE;
         switch(iPosition){
           case 1:
             ......//调用按钮一的处理功能
           case 2:
             ......//调用按钮二的处理功能
           ......
           case n:
             ......//调用按钮N的处理功能
           break;
         }
    }
    void CToolDoc::OnUpdateTool(CCmdUI* pCmdUI)
    {    file://更新按钮函数
         pCmdUI->SetCheck(iPosition==(pCmdUI->m_nID
           -ID_BUTTON_LINE));
    }
    由于命令按钮比较多,其处理功能代码部分这里省略。
6.3.8 驾驭工具条按钮的状态显示更新
    (1)互斥按钮的状态更新
    很多应用程序中都存在相互排斥的工具条按钮,如开始和停止按钮,初始状态时停止按钮被禁止,当应用程序进行数据通讯、查找和打印等功能时,停止按钮有效而开始按钮禁止,当命令结束或按停止按钮后,开始按钮有效而停止按钮被禁止。这就是工具条中按钮的显示状态更新问题。
    与菜单栏一样,工具条的构造、析构过程及其窗口的创建都是由应用程序的主框架窗口来管理的。当用户点击菜单条的菜单项时,Windows 就会向应用程序发送WM_INITMENUPOPUP消息,以便应用程序在菜单显示之前对菜单项进行添加或修改,当利用MFC 类库编程时只要针对某一具体的菜单项进行消息映射,加入相应的消息处理函数,MFC类库就会发送更新消息UPDATE_COMMAND_UI,使菜单在弹出时消息处理函数能够对菜单项进行禁止、变灰和其它处理。工具条的消息处理过程与菜单栏完全相同,它也存在工具条显示更新消息UPDATE_COMMAND_UI ,只不是由于工具条总是处于显示状态,所以对其消息处理函数的调用是在应用程序的空闲状态时进行处理的,这点与菜单栏有所不同,当应用程序命令按钮所要执行的功能占用大量的处理器资源时,如进行通讯处理或大量数据计算时,应用程序的主框架窗口就无暇进入空闲状态,于是工具条中的命令按钮函数就不能被及时调用,按钮的显示状态就不能被及时更新,这就为工具条按钮的显示状态及时更新带来一定的困难。
    基于文档的应用程序一般情况下都是在文档视中对工具条按钮进行处理,所以要实现按钮显示状态的及时更新,必须在命令按钮响应函数中利用CToolBarCtrl类的成员函数EnableButton()函数对按钮的显示状态进行必要的处理,其步骤如下:
    ①首先利用类向导ClassWizard 对CCTOOLView类进行开始按钮和停止按钮的COMMAND命令消息映射,增加对应消息的处理函数;
    ②在实现文件CTOOLView.h中增加成员变量:
    public:
        BOOL m_bContiune;//开始按钮标志
        BOOL m_bSet;//结束按钮标志
    ③在实现文件CTOOLView.cpp中增加代码完善按钮状态更新:
    ......//其它代码
    void DrawRectText(RECT &rc,CDC &dc);
    ......//其它代码
    void CCTOOLView::OnButtonStart()
    {   file://完善开始按钮函数功能
        CToolBar *pToolBar=(CToolBar *)//取得工具栏对象指针
          (GetParentFrame()->GetControlBar(IDR_DOCKTOOLBAR));
          file://取得工具条控制指针
        CToolBarCtrl *pToolBarCtrl=&(pToolBar->GetToolBarCtrl());
          file://控制工具条按钮状态
        pToolBarCtrl->EnableButton(ID_BUTTON_START,FALSE);
        pToolBarCtrl->EnableButton(ID_BUTTON_STOP,TRUE);
        CClientDC dc(this);//取得设备文本
        MSG msg;//定义消息
        RECT rc;
        GetClientRect(&rc);//以下画网格并显示
        m_bContinue=TRUE;
        m_bSet=FALSE;
        for(int ii=0;ii<2;ii++){
           if(m_bContinue==TRUE){
             ii=0;//状态改变退出
             DrawRectText(rc,dc);
             GetMessage(&msg,NULL,0,0);//检查消息
             TranslateMessage(&msg);//解释消息
             DispatchMessage(&msg);//分配消息
           }
        }
        ReleaseDC(&dc);
        m_bSet=TRUE;
    }
    void DrawRectText(RECT &rc,CDC &dc)
    {   file://显示文本函数
        int i,j,k,nDist=10;
        CPen pen,*pOldPen;
        pen.CreatePen(PS_SOLID,1,RGB(180,0,0));
        pOldPen=dc.SelectObject(&pen);//绘制背景网格
        j=rc.right/nDist+1;
        k=rc.bottom/nDist+1;
        for(i=0;i<j+k;i++){
          dc.MoveTo(i*nDist,0);
          dc.LineTo(0,i*nDist);
          if(i<j){
            dc.MoveTo(i*nDist,0);
            dc.LineTo(rc.right,(j-i)*nDist);
          } else {
            dc.MoveTo(0,(i-j)*nDist);
            dc.LineTo(rc.right,i*nDist);
          }
        }
        dc.SelectObject(pOldPen);
        DeleteObject(&pen);
        CFont font,*pOldFont;
        font.CreateFont(50,50,0,0,1000,//创建字体
          1,1,0,ANSI_CHARSET,
          OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,
          DEFAULT_QUALITY,DEFAULT_PITCH,NULL);
        pOldFont=dc.SelectObject(&font);
        dc.TextOut(10,10,"BUTTON");
        dc.SelectObject(pOldFont);
        DeleteObject(&font);
    }
    void CCTOOLView::OnButtonStop()
    {   file://完善停止按钮函数
        m_bContinue=FALSE;
        CToolBar *pToolBar=(CToolBar *)//取得工具栏对象指针
          (GetParentFrame()->GetControlBar(IDR_DOCKTOOLBAR));
          file://取得工具条控制指针
        CToolBarCtrl *pToolBarCtrl=&(pToolBar->GetToolBarCtrl());
          file://控制工具条按钮状态
        pToolBarCtrl->EnableButton(ID_BUTTON_START,TRUE);
        pToolBarCtrl->EnableButton(ID_BUTTON_STOP,FALSE);
        for(int jj=0;jj<2;jj++){
          if(m_bSet==FALSE){
            jj=0;
            GetMessage(&msg1,NULL,0,0);//检查消息
            TranslateMessage(&msg1);//解释消息
            DispatchMessage(&msg1);//分配消息
          }
        }
    }
    在开始按钮函数中,当在屏幕上不断显示信息的同时必须利用消息处理函数检查消息队列,并解释和分配相应的消息,否则其它按钮就无法检查到应用程序的鼠标等消息,无法实现显示状态更新;在更新按钮显示状态前,必须先取得工具栏对象和相应的控制指针,然后才能实现对按钮状态的控制,并且要实现按钮状态的更新,其设置按钮的更新功能函数必须最后执行结束才能有效,即在停止按钮函数中必须判断标志并检测消息队列。
    ④在平面工具条派生类中的鼠标按下函数中增加代码,以实现鼠标按下开始和停止按钮后,保证按钮的正常按下和显示状态:
    void CTestToolBar::OnLButtonDown(UINT nFlags, CPoint point)
    {   file://完善鼠标按下函数
        ......//其它代码
        if(m_nIndex==ToolBarCtrl.CommandToIndex(ID_BUTTON_STOP)){
          ToolBarCtrl.SetState(ID_BUTTON_STOP,ToolBarCtrl.GetState(
            ID_BUTTON_STOP)&(~TBSTATE_ENABLED&~TBSTATE_PRESSED));
          ToolBarCtrl.SetState(ID_BUTTON_START,ToolBarCtrl.
            GetState(ID_BUTTON_START)|TBSTATE_ENABLED);
          ToolBarCtrl.Invalidate();
          AfxGetApp()->OnIdle(-1);
          m_nIndex=0xffff;
        }
        if(m_nIndex==ToolBarCtrl.CommandToIndex(ID_BUTTON_START)){
          ToolBarCtrl.SetState(ID_BUTTON_START,ToolBarCtrl.GetState(
            ID_BUTTON_START)&(~TBSTATE_ENABLED & ~TBSTATE_PRESSED));
          ToolBarCtrl.SetState(ID_BUTTON_STOP,ToolBarCtrl.GetState(
            ID_BUTTON_STOP)&TBSTATE_ENABLED);
          ToolBarCtrl.Invalidate();
          AfxGetApp()->OnIdle(-1);
          m_nIndex=0xffff;
        }
    }
    (2)按钮按下状态的切换更新
    另一种按钮状态的更新情况是按钮按下状态的切换更新,这在绘图工具条和屏幕字体效果工具条中最常见。其实现步骤如下:
    ①首先在MainFrm.cpp中手工增加消息范围映射函数
    ON_COMMAND_RANGE(ID_EDIT_TOGGLE,ID_EDIT_UNINDENT,OnButton)
    ON_UPDATE_COMMAND_UI_RANGE(ID_EDIT_TOGGLE,
      ID_EDIT_UNINDENT,OnUpdateButton)
    ②然后在MainFrm.h中消息映射的最后增加成员函数定义和记录按下按钮序号的成员控制变量
        int  m_bIndex;//按下按钮序号
        afx_msg void OnButton(UINT nID);
        afx_msg void OnUpdateButton(CCmdUI* pCmdUI);
    ③最后分别增加实现消息函数的功能代码
    void CMainFrame::OnUpdateButton(CCmdUI* pCmdUI)
    {   file://按钮按下检查状态设置函数
        pCmdUI->SetCheck((UINT)(ID_EDIT_TOGGLE+
          m_bIndex) == pCmdUI->m_nID);
    }
    void CMainFrame::OnButton(UINT nID)
    {   file://记录按下按钮的序号
        m_bIndex=(int)(nID-ID_EDIT_TOGGLE);
    }
    如果涉及到其它控制的变化,可在这两个函数中分别增加。
    (3)按钮的部分显示更新
    最后一种更新工具条按钮显示状态的情况是部分按钮显示,这需要利用SetButtons()和SetButtonInfo()函数有效处理。步骤如下:
    ①首先在菜单栏中增加控制菜单项;
    ②在应用程序实现文件CTool.h中增加如下消息定义和函数定义
    ......//其它代码
    #define WM_IDLEACTIVATE WM_USER+1
    ......//其它代码
    class CCtrlbarsApp : public CWinApp
    {
    public:
        ......//其它代码
        virtual BOOL OnIdle(LONG lCount);
        ......//其它代码
    };
    ③在应用程序实现文件CTool. cpp中增加如下代码和副本函数功能,这个函数在工具条按钮的各种显示状态更新中都非常有用,是一个非常重要的程序设计技巧。
    在初始化函数CCTOOLApp::InitInstance()最后增加代码:
    m_pMainWnd = pMainFrame;
    完善相应的功能函数:
    BOOL CCTOOLApp::OnIdle(LONG lCount)
    {   file://保持应用程序一个副本函数
        if(lCount == 0){
          ASSERT(m_pMainWnd != NULL);
          for(HWND hWnd=::GetWindow(m_pMainWnd->m_hWnd,
            GW_HWNDFIRST);hWnd!=NULL;hWnd=::GetNextWindow(
            hWnd,GW_HWNDNEXT)){//获取主程序子窗口
              if(::GetParent(hWnd)==m_pMainWnd->m_hWnd){
                if(GetActiveWindow()==hWnd&&(::GetCapture()==NULL))
                  m_pMainWnd->SetActiveWindow();//保持应用程序活动窗口
                SendMessage(hWnd,WM_IDLEUPDATECMDUI,(WPARAM)TRUE,0L);
              }//保持窗口的一个有效副本
          }
        }
        return CWinApp::OnIdle(lCount);
    }
    ④最后在菜单项处理函数中进行工具条按钮的重组处理,并在需要处理应用程序窗口副本的任何函数中增加副本处理函数。
    void CMainFrame::OnViewShort()
    {
        m_wndMainToolBar.SetButtons(NULL,7);//共计7个
        m_wndMainToolBar.SetButtonInfo(0,ID_BUTTON_LINE,TBBS_BUTTON,1);
        m_wndMainToolBar.SetButtonInfo(1,ID_BUTTON_CURSE,TBBS_BUTTON,5);
        ......
        m_wndMainToolBar.SetButtonInfo(6,ID_BUTTON_TEST,TBBS_BUTTON,7);
        m_wndToolBar.Invalidate();//更新工具条
        AfxGetApp()->OnIdle(-1);//窗口副本处理函数
    }
<广告一下,呵呵:http://blog.csdn.net/wangjiwei2010>
6.3.9 工具条中的通用控制使用
    在工具条中通常不都是按钮,如存在输入框、下拉组合框和检查盒按钮等。这就需要利用VC中提供的特殊类型的工具条类控制或通过派生新类的方法来实现。
    (1)在派生的新类中实现控制
    如果利用派生新类的方法,很容易就可以实现文本输入框等通用控制功能。其实现步骤如下:
    ①利用类向导ClassWizard 建立新类CEDITBAR并生成实现文件;
    ②利用资源编辑器建立工具条的位图文件;
    ③在新类实现文件EDITBAR.h中增加如下函数定义和成员变量:
    class CEDITBAR : public CToolBar
    {
    public:
        CEDITBAR();
        CSearchBox m_SearchBox;//文本查找控制
        BOOL Init(CWnd* pParentWnd,BOOL bToolTips);
        BOOL SetColor(BOOL bColor);
        BOOL SetHorizontal();
        BOOL SetVertical();
        ......//其它代码
        BOOL m_bVertical;
        file://virtual CSize CalcFixedLayout(BOOL bStretch,BOOL bHorz);
        virtual CSize CalcDynamicLayout(int nLength,DWORD dwMode);
        ......//其它代码
    }
    ④在新类实现文件EDITBAR.cpp中增加如下函数:
    ......//其它代码
    #define COMBOBOX_INDEX 5      file://定义组合框参数
    #define COMBOBOX_WIDTH 150
    #define COMBOBOX_HEIGHT 150
    #define COMBOBOX_BITMAP 4
    static UINT BASED_CODE EditButtons[] =
    {   file://要求与工具栏位图顺序对应
        ID_EDIT_TOGGLE,
        ID_EDIT_NEXT,
        ID_EDIT_PREV,
        ID_EDIT_CLEARALL,
          ID_SEPARATOR,
        ID_EDIT_FINDEDIT,
          ID_SEPARATOR,
        ID_EDIT_FINDINFILES,
          ID_SEPARATOR,
        ID_EDIT_INDENT,
        ID_EDIT_UNINDENT,
          ID_SEPARATOR,
        ID_WINDOW_NEW,
        ID_WINDOW_SPLIT,
        ID_WINDOW_CASCADE,
        ID_WINDOW_HORZ,
        ID_WINDOW_VERT,
    };
    ......//其它代码
    BOOL CEDITBAR::Init(CWnd* pParentWnd,BOOL bToolTips)
    {   file://初始化输入框函数
        m_bVertical=FALSE;
        DWORD dwStyle=WS_CHILD|WS_VISIBLE|CBRS_TOP|CBRS_SIZE_DYNAMIC;
        if(bToolTips)dwStyle|=(CBRS_TOOLTIPS|CBRS_FLYBY);
        if(!Create(pParentWnd,dwStyle,IDB_CEDIT)) return FALSE;
        if(!LoadBitmap(IDB_CEDIT)) return FALSE;
        if(!SetButtons(EditButtons,sizeof(EditButtons)
          /sizeof(UINT)))return FALSE;
        CRect rect(-COMBOBOX_WIDTH,-COMBOBOX_HEIGHT,0,0);
        // 非常重要:其一是可以从控制接收信息,如动态提示等
        if(!m_SearchBox.Create(WS_CHILD|CBS_DROPDOWN|//建立文本查找
          CBS_AUTOHSCROLL|WS_VSCROLL|CBS_HASSTRINGS,rect,this,
          IDC_EDITBAR_SEARCHBOX)) return FALSE;
        HFONT hFont=(HFONT)GetStockObject(DEFAULT_GUI_FONT);
        if(hFont==NULL)hFont=(HFONT)GetStockObject(ANSI_VAR_FONT);
        m_SearchBox.SendMessage(WM_SETFONT,(WPARAM)hFont);
        if(!SetHorizontal())return FALSE;
        return TRUE;
    }
    BOOL CEDITBAR::SetHorizontal()
    {   file://设置按钮水平排列函数
        m_bVertical=FALSE;
        SetBarStyle(GetBarStyle()|CBRS_ALIGN_TOP);
        SetButtonInfo(COMBOBOX_INDEX,IDC_EDITBAR_SEARCHBOX,
          TBBS_SEPARATOR, COMBOBOX_WIDTH);
        if(m_SearchBox.m_hWnd!=NULL){
          CRect rect;
          GetItemRect(COMBOBOX_INDEX,rect);
          m_SearchBox.SetWindowPos(NULL,rect.left,
            rect.top,0,0,SWP_NOZORDER|SWP_NOACTIVATE|
            SWP_NOSIZE|SWP_NOCOPYBITS);
          m_SearchBox.ShowWindow(SW_SHOW);
        }
        return TRUE;
    }
    BOOL CEDITBAR::SetVertical()
    {   file://设置按钮垂直排列函数
        m_bVertical=TRUE;
        SetButtonInfo(COMBOBOX_INDEX,ID_EDIT_FIND,
          TBBS_BUTTON, COMBOBOX_BITMAP);
        if(m_SearchBox.m_hWnd != NULL)
          m_SearchBox.ShowWindow(SW_HIDE);
        return TRUE;
    }
    CSize CEDITBAR::CalcDynamicLayout(int nLength, DWORD dwMode)
    {   file://设置固定大小工具条函数
        if (dwMode&LM_COMMIT) {
           if (dwMode&LM_VERTDOCK)
              if(!m_bVertical) SetVertical();
           else if(m_bVertical) SetHorizontal();
           return CToolBar::CalcDynamicLayout(nLength,dwMode);
        } else {
           BOOL bOld=m_bVertical;
           BOOL bSwitch=(dwMode & LM_HORZ)?bOld:!bOld;
           if (bSwitch){
              if (bOld) SetHorizontal();
              else SetVertical();}
           CSize sizeResult=CToolBar::CalcDynamicLayout(nLength,dwMode);
           if (bSwitch){
              if (bOld) SetHorizontal();
              else SetVertical();}
           return sizeResult;
        }
    }
    /*
    CSize CEDITBAR::CalcFixedLayout(BOOL bStretch,BOOL bHorz)
    {   file://设置可变大小工具条函数
        if(!bHorz&&(m_dwStyle&CBRS_SIZE_DYNAMIC)&&
          !(m_dwStyle&CBRS_FLOATING)){
          CSize size;
          size=CControlBar::CalcFixedLayout(bStretch,bHorz);
          CRect rect;
          rect.SetRectEmpty();
          CalcInsideRect(rect, bHorz);
          size.cx=m_sizeVert.cx+(m_rectInsideVert.Width()-rect.Width());
          size.cy=m_sizeVert.cy+(m_rectInsideVert.Height()-rect.Height());
          return size;
        } else {
          if(m_bVertical) SetHorizontal();
          return CToolBar::CalcFixedLayout(bStretch,bHorz);
        }
    }
    */
    ⑤利用类向导ClassWizard建立新类CSearchBox并生成实现文件,同时为新类增加解释消息,即在新类实现文件SearchBox.h中增加如下函数定义:
     virtual BOOL PreTranslateMessage(MSG* pMsg);
    ⑥在其实现文件SearchBox.cpp文件中完善相应函数
    BOOL CSearchBox::PreTranslateMessage(MSG* pMsg)
    {   file://消息解释函数
        if((pMsg->message!=WM_KEYDOWN)||(pMsg->wParam!=VK_RETURN))
          return CComboBox::PreTranslateMessage(pMsg);
        // 在下拉列表中增加最后15次输入的数据
        if((pMsg->lParam&0x40000000)==0){//不重复
          CString strText;
          GetWindowText(strText);
          InsertString(0,strText);
          SetCurSel(0);
          if(GetCount()>15)
            DeleteString(GetCount()-1);
        }
        return TRUE;
    }
    ⑦在主程序实现文件MainFrm.h中增加工具条定义
    #include "EDITBAR.H"
    CEDITBAR m_wndEditBar;//带输入框工具条
    ⑧在主程序实现文件MainFrm.cpp中增加代码:
    file://在建立函数OnCreate中增加
    if (!m_wndEditBar.Init(this,m_bToolTips)){
       TRACE0("带编输入工具条建立失败/n");
       return -1;}
    ......
    DockControlBarLeftOf(&m_wndEditBar,
      &m_wndTestToolBar);//连接工具条
    此外,还可以为工具条增加按钮检查状态等控制功能,详见工具条按钮的显示状态更新。
    (2)利用工具条风格类实现控制
    利用工具条风格类同样可以实现下拉列表等控制功能,其具体步骤如下:
    ①为工具栏建立位图按钮并定义按钮对应的消息,同前;
    ②在实现文件MainFrm.h中增加工具栏控制和函数定义;
     CStyleBar m_wndStyleBar;
     BOOL      CreateStyleBar()
    ③在实现文件MainFrm.cpp中增加控制代码
    在建立函数OnCreate中增加建立函数
    if (!CreateStyleBar()) return -1;
    增加建立相应工具条函数的代码
    BOOL CMainFrame::CreateStyleBar()
    {   file://建立具有下拉列表控制的工具条函数
        const int nDropHeight = 100;
        if(!m_wndStyleBar.Create(this,WS_CHILD|WS_VISIBLE|
          CBRS_TOP|CBRS_TOOLTIPS|CBRS_FLYBY,IDW_STYLES)||
          !m_wndStyleBar.LoadBitmap(IDB_STYLES)||!m_wndStyleBar.
          SetButtons(styles,sizeof(styles)/sizeof(UINT))){
            TRACE0("特殊风格工具条建立失败/n");
            return FALSE;
        }
        // 建立组合框
        m_wndStyleBar.SetButtonInfo(0,IDW_COMBO,TBBS_SEPARATOR,100);
        // 设置组合框与按钮间距离
        m_wndStyleBar.SetButtonInfo(1,ID_SEPARATOR,TBBS_SEPARATOR,12);
        CRect rect;
        m_wndStyleBar.GetItemRect(0,&rect);
        rect.top=3;
        rect.bottom=rect.top+nDropHeight;
        if (!m_wndStyleBar.m_comboBox.Create(//建立组合框
           CBS_DROPDOWNLIST|WS_VISIBLE|WS_TABSTOP,
           rect, &m_wndStyleBar, IDW_COMBO)){
           TRACE0("组合框建立失败/n");
           return FALSE;
        }
        CString szStyle;//填充组合框
        if (szStyle.LoadString(IDS_LEFT))
           m_wndStyleBar.m_comboBox.AddString((LPCTSTR)szStyle);
        ......//同类的其它代码
        LOGFONT logFont;//为组合框建立字体
        memset(&logFont,0,sizeof(logFont));
        if(!::GetSystemMetrics(SM_DBCSENABLED)){
          logFont.lfHeight=-12;
          logFont.lfWeight=FW_BOLD;
          logFont.lfPitchAndFamily=VARIABLE_PITCH|FF_SWISS;
          CString strDefaultFont;
          strDefaultFont.LoadString(IDS_DEFAULT_FONT);
          lstrcpy(logFont.lfFaceName, strDefaultFont);
          if(!m_wndStyleBar.m_font.CreateFontIndirect(&logFont))
            TRACE0("组合框建立字体失败/n");
          else m_wndStyleBar.m_comboBox.SetFont(&m_wndStyleBar.m_font);
        } else {
          m_wndStyleBar.m_font.Attach(::GetStockObject(SYSTEM_FONT));
          m_wndStyleBar.m_comboBox.SetFont(&m_wndStyleBar.m_font);
        }
        return TRUE;
    }
    同样,也可以为这个特殊风格的工具条增加其它控制功能。
    (3)利用对话框类建立多功能工具条
    利用对话框工具条类可以建立功能更加强大的工具条,在对话框中实现的功能都可以增加到工具条中去。工具步骤如下:
    ①利用对话框资源增加一个对话框IDD_DLGTOOLBAR,并设计好对话框中的所有功能及功能对应的ID标识;
    ②在实现文件MainFrm.h中增加对话框工具条类控制和成员变量
    CDialogBar   m_wndDlgBar; file://对话框工具条控制
    BOOL         m_bDialogTop;//顶部标志
    ③在实现文件MainFrm.cpp中增加控制代码
    在建立函数OnCreate函数的状态条建立函数后增加:
    UINT nID, nStyle;
    int cxWidth;
    m_wndStatusBar.GetPaneInfo(0,nID,nStyle,cxWidth);
    m_wndStatusBar.SetPaneInfo(0,nID,SBPS_STRETCH|SBPS_NORMAL,cxWidth);
    if(!m_wndDlgBar.Create(this,IDD_TOOLBARCTRL,
      CBRS_LEFT|CBRS_TOOLTIPS|CBRS_FLYBY,IDD_TOOLBARCTRL)){
      TRACE0("对话框工具条建立失败/n");
      return -1;
    }
    一定要注意EnableDocking(CBRS_ALIGN_ANY)函数在所有建立函数的前面,并且对话框工具条在状态条建立函数的后面。
    ④为对话框资源中的所有控制消息手工增加映射,并完善相应的映射函数,可以是工具条的显示与隐藏等功能。示例函数如下:
    BOOL CMainFrame::OnDocktool(UINT nID)
    {   file://控制工具条的显示与隐藏函数
        BOOL bVisible = ((m_wndDockToolBar.GetStyle() & WS_VISIBLE) != 0);
        ShowControlBar(&m_wndDockToolBar, !bVisible, FALSE);
        RecalcLayout();
        return TRUE;
    }
    void CMainFrame::OnUpdateDocktool(CCmdUI* pCmdUI)
    {   file://工具条检查状态更新
        BOOL bVisible = ((m_wndDockToolBar.GetStyle() & WS_VISIBLE) != 0);
        pCmdUI->SetCheck(bVisible);
    }
    ⑤在菜单中增加顶部控制菜单项,建立消息函数后按照工具条的停靠位置中的方法为其完善相应的顶部控制功能函数。
6.3.10 工具条的显示和隐藏
6.3.10.1 单一工具条的显示与隐藏
    在大型应用程序中可能存在很多工具条,而用户实际使用时可能只需要其中的一部分,这就需要控制工具条的显示隐藏。对于单个工具条,其菜单项的一般控制方法为:
    首先打开菜单编辑器,在查看菜单栏中增加工具条对应的子菜单项,如为TestToolBar 工具条增加的子菜单项的“测试”的ID为ID_BOTTON_TEST,其Caption 为“测试工具条”,依次增加其它工具条的控制子菜单项。利用类向导ClassWizard 为主框架窗口类CMainFrame增加各子菜单项的ONCOMMAND和ON_UPDATE_COMMAND_UI 消息映射功能函数,并完善对应的功能函数:
    void CMainFrame::OnButtonTest()
    {    file://菜单命令消息函数
         BOOL bVisible=((m_wndTestToolBar.GetStyle()&
              WS_VISIBLE)!=0);//取得工具条状态
         ShowControlBar(&m_wndTestToolBar,!bVisible,FALSE);
         RecalcLayout();//更新工具条
    }
    void CMainFrame::OnUpdateButtonTest(CCmdUI* pCmdUI)
    {    file://菜单更新函数
         BOOL bVisible=((m_wndTestToolBar.GetStyle()&
              WS_VISIBLE)!=0);//取得工具条状态
         pCmdUI->SetCheck(bVisible);//设置检查标志
    }
6.3.10.2 特殊工具条的显示与隐藏
    当存在利用CStyleBar 类等建立的具有特殊控制功能的工具条时,工具条的显示与消隐必须利用下面的函数:
    BOOL CMainFrame::OnEdittool(UINT nID)
    {   file://具有下拉组合框功能工具条显示与消隐函数
        CWnd* pBar;//工具条窗口指针
        if ((pBar=GetDlgItem(nID))==NULL)//取得鼠标点击项
           return FALSE;//控制工具条窗口风格
        pBar->ShowWindow((pBar->GetStyle()&WS_VISIBLE)==0);
        RecalcLayout();//重新显示窗口
        return TRUE;
    }
    void CMainFrame::OnUpdateEdittool(CCmdUI* pCmdUI)
    {   file://具有下拉组合框功能工具条状态更新
        CWnd* pBar;//工具条窗口指针
        if((pBar=GetDlgItem(pCmdUI->m_nID))==NULL){
          pCmdUI->ContinueRouting();//继续判断
          return;
        } file://设置检查状态
        pCmdUI->SetCheck((pBar->GetStyle()&WS_VISIBLE)!=0);
    }
6.3.10.3 多工具条的显示与隐藏
    如果存在很多工具条,使用菜单控制的方法并不方便,最好采用对话框设置的方法,如笔者在程序中通过对话框来控制工具条的显示与消隐、按钮排列数和动态提示功能的取舍。其步骤如下:
    ①首先打开资源列表中的对话框资源,利用"Insert->Resource->Dialog"插入对话框IDD_TOOLBAR,在对话框中建立四个工具条复选框按钮、四个排列数值的单选按钮和两个动态提示功能的单选按钮(请有关图形),并为各按钮设置ID标识和按钮名称,注意选中每组按钮中首按钮的Group属性;
    ②通过双击增加的对话框标题栏为对话框建立新类CToolDlg和两个实现文件ToolDlg.h和ToolDlg.cpp;
    ③打开对话框实现文件ToolDlg.h,增加如下成员控制变量:
    class CToolDlg : public CDialog
    {   ......//其它代码
        file://{{AFX_DATA(CToolDlg)
        enum { IDD = IDD_TOOLBAR };
        BOOL    m_bMainTool;//各工具条控制变量
        BOOL    m_bTestTool;
        BOOL    m_bDockTool;
        BOOL    m_bDockNext;
        int     m_nToolTips;//动态提示控制变量
        int     m_nColumns; file://按钮排列数控制变量
        ......//其它代码
    }
    ④在实现文件ToolDlg.cpp中增加如下控制代码:
    CToolDlg::CToolDlg(CWnd* pParent /*=NULL*/)
        : CDialog(CToolDlg::IDD, pParent)
    {   file://完善成员变量初始化
        file://{{AFX_DATA_INIT(CToolDlg)
        m_bMainTool = FALSE;
        m_bTestTool = FALSE;
        m_bDockTool = FALSE;
        m_bDockNext = FALSE;
        m_nToolTips = -1;
        m_nColumns = -1;
        file://}}AFX_DATA_INIT
    }
    void CToolDlg::DoDataExchange(CDataExchange* pDX)
    {   file://完善对话框按钮状态初始化
        CDialog::DoDataExchange(pDX);
        file://{{AFX_DATA_MAP(CToolDlg)
        DDX_Check(pDX,IDC_MAINTOOL,m_bMainTool);
        DDX_Check(pDX,IDC_TESTTOOL,m_bTestTool);
        DDX_Check(pDX,IDC_DOCKTOOL,m_bDockTool);
        DDX_Check(pDX,IDC_DOCKNEXT,m_bDockNext);
        DDX_Radio(pDX,IDC_SHOW,m_nToolTips);
        DDX_Radio(pDX,IDC_TWO,m_nColumns);
        file://}}AFX_DATA_MAP
    }
    ⑤在主程序实现文件MainFrm.cpp 中增加ToolDlg.h 包含文件,并完善相应菜单选项功能。如笔者程序中为完善“测试”选项的功能,其控制代码如下:
    void CMainFrame::OnButtonTest()
    {   file://完善相应菜单选项
        CToolDlg ToolDlg;//调出对话框
        UINT nColumns = m_wndDockToolBar.GetColumns();
        nColumns -= 2;//取得按钮排列数
        file://取得工具栏的可视风格
        ToolDlg.m_bMainTool=((m_wndMainToolBar.
          GetStyle()&WS_VISIBLE)!=0);
        ToolDlg.m_bTestTool=((m_wndTestToolBar.
          GetStyle()&WS_VISIBLE)!=0);
        ToolDlg.m_bDockTool=((m_wndDockToolBar.
          GetStyle()&WS_VISIBLE)!=0);
        ToolDlg.m_bDockNext=((m_wndDockNextBar.
          GetStyle()&WS_VISIBLE)!=0);
        ToolDlg.m_nColumns=nColumns;
        ToolDlg.m_nToolTips=(m_bToolTips)?0:1;
        if (IDOK==ToolDlg.DoModal())
        {  file://控制工具条的显示与消隐
           ShowControlBar(&m_wndMainToolBar,
             ToolDlg.m_bMainTool,FALSE);
           ......//其它工具条控制代码,基本相同
           m_bToolTips=(ToolDlg.m_nToolTips==0);
           if (m_bToolTips){//控制动态提示功能
              m_wndMainToolBar.SetBarStyle(m_wndMainToolBar.
                GetBarStyle()|CBRS_TOOLTIPS|CBRS_FLYBY);
              ......//其它工具条控制代码,基本相同
           } else {
              m_wndMainToolBar.SetBarStyle(m_wndMainToolBar.
                GetBarStyle()&(CBRS_TOOLTIPS|CBRS_FLYBY));
              ......//其它工具条控制代码,基本相同
           }
           nColumns=ToolDlg.m_nColumns+2;//控制工具条按钮列数
           m_wndDockToolBar.SetColumns(nColumns);
           m_wndDockNextBar.SetColumns(nColumns);
           m_wndMainToolBar.Invalidate();//控制各工具条显示更新
           ......//其它工具条控制代码,基本相同
        }
    }
6.3.11 工具条的属性控制
    WIN95/98中的鼠标右键弹出属性表功能具有很强的功能特色,很多应用程序中都对各种功能进行了属性控制设置,给应用程序中的工具条增加属性控制功能更具有现实意义。在工具条的属性表中可以设置工具栏的消隐与显示、工具条按钮的排序和其它各种控制功能,这要根据应用程序的需要来确定。这里以控制工具条的显示与消隐为例,详细阐述其实现的具体步骤:
    (1)首先打开资源列表中的菜单资源,利用"Insert->Resource->Menu"增加菜单资源IDR_TOOLBAR,在菜单中建立四个工具条菜单选项,并为各菜单选项设置ID标识和菜单项名,注意将各菜单选项标识符设置为与工具条名称完全相同的控制符,如本例程序中主控工具条ID为IDR_wndMainToolBar,那么控制主控工具条的菜单选项ID标识符也必须是IDR_wndMainToolBar,这样设置后可以使用MFC 类库中的提供的功能来控制工具条的显示与消隐、菜单项检查状态与非检查状态。
    (2)然后利用类向导ClassWizard 为应用程序主框架窗口增加解释消息PreTranslateMessage处理函数;
    (3)在应用程序实现文件MainFrm.cpp中完善解释消息函数:
    BOOL CMainFrame::PreTranslateMessage(MSG* pMsg)
    {    if (pMsg->message==WM_RBUTTONDOWN){
         CWnd* pWnd=CWnd::FromHandlePermanent(pMsg->hwnd);
         CControlBar* pBar=DYNAMIC_DOWNCAST(CControlBar,pWnd);//取得控制条句柄
         if (pBar!=NULL){
            CMenu Menu;//建立弹出式菜单
            CPoint pt; file://鼠标指针位置
            pt.x = LOWORD(pMsg->lParam);
            pt.y = HIWORD(pMsg->lParam);
            pBar->ClientToScreen(&pt);//转换成屏幕坐标
            if (Menu.LoadMenu(IDR_TOOLBAR)){//调入菜单资源
               CMenu* pSubMenu = Menu.GetSubMenu(0);//取得选项
               if (pSubMenu!=NULL){//跟踪菜单以取得输入
                  pSubMenu->TrackPopupMenu(TPM_LEFTALIGN|
          TPM_RIGHTBUTTON,pt.x,pt.y,this);
               }
             }
          }
        }
        return CFrameWnd::PreTranslateMessage(pMsg);
    }
    (4)在消息映射函数后增加
    BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
        file://{{AFX_MSG_MAP(CMainFrame)
        ......//其它消息映射函数
        file://}}AFX_MSG_MAP
        // 增加扩展命令消息和更新消息映射,利用现成MFC函数更新菜单
        ON_UPDATE_COMMAND_UI(IDR_MAINTOOLBAR,OnUpdateControlBarMenu)
        ON_COMMAND_EX(IDR_MAINTOOLBAR,OnBarCheck)
        ......//其它工具条映射函数基本相同,只有标识不同
    END_MESSAGE_MAP()
    (5)重要的程序设计技巧步骤
上述步骤1和步骤4是实现工具条属性菜单有效控制的最关键步骤,如果不在步骤1中将菜单ID设置成工具条的ID,或者不在步骤4中设置相应的消息映射,都不可能实现工具条菜单与工具条状态的有效控制。工具条菜单中的其它属性功能可模仿实现。

第7章 演练CProgress
7.1 进度条的主要功能
    进度条控制(Progress Control)主要用来进行数据读写、文件拷贝和磁盘格式等操作时的工作进度提示情况,如安装程序等,伴随工作进度的进展,进度条的矩形区域从左到右利用当前活动窗口标题条的颜色来不断填充。
    进度条控制在MFC类库中的封装类为CProgressCtrl,通常仅作为输出类控制,所以其操作主要是设置进度条的范围和当前位置,并不断地更新当前位置。进度条的范围用来表示整个操作过程的时间长度,当前位置表示完成情况的当前时刻。SetRange()函数用来设置范围,初始范围为0-100,SetPos()函数用来设置当前位置,初始值为0,SetStep()函数用来设置步长,初始步长为10,StepIt()函数用来按照当前步长更新位置,OffsetPos()函数用来直接将当前位置移动一段距离。如果范围或位置发生变化,那么进度条将自动重绘进度区域来及时反映当前工作的进展情况。
    进度条的对象结构
    进度条控制的建立方法
    CProgressCtrl &ProgressCtrl  建立进度条控制对象结构
    Create                       建立进度条控制对象并绑定对象
    进度条控制类CprogressCtrl::Create的调用格式如下:
    BOOL Create( DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID );
    其中参数dwStyle用来确定进度条控制的控制风格;参数rect用来确定进度条控制的大小和位置;参数pParentWnd用来确定进度条父窗口指针;参数nID用来确定进度条控制的控制符ID值。
7.2 进度条控制的类属性
    进度条控制的类属性包括设置进度条最大最小控制范围SetRange、设置进度条当前位置    SetPos、设置进度条当前位置偏移值OffsetPos和设置进度条控制增量值SetStep。
7.3 进度条控制的操作方法
    进度条控制的操作方法主要是使进度条控制并重绘进度条的StepIt函数。
进度条控制的应用技巧示例
    1、利用应用程序向导AppWizard生成基于对象框的应用程序CProgDlg;
    2、在对话框中设置进度条和静态文本控制,其ID分别为IDC_PROG和IDCPERCENT;
    在对话框初始代码中增加控制的范围和位置:
    在ProgDlg.h中设置两个数据成员,用来表示进度条的最大值和步长:
    file://ProgDlg.h
class CProgDlg:public Cdialog
{ ......//其它代码
public:
int m_nMax,m_nStep;
...... file://其它代码
}
    (2)在ProgDlg.cpp中设置初始状态
BOOL  CProgDlg::OnInitDialog()
{ Cdialog::OnInitDialog();
......//其它代码
file://TODO:Add extra initialization here
CProgressCtrl *pProgCtrl=(CProgressCtrl*)GetDlgItem(IDC_PROG);
pProgCtrl->SetRange(0,200);//设置进度条范围
......//其它代码
m_nMax=200;
m_nStep=10;
SetTimer(1,1000,NULL);//设置进度条更新时钟
return TRUE;
}
    (3)完善WM_TIMER消息处理,使进度条按照当前步长进行更新,同时完成进度条的百分比显示:
void CProgDlg::OnTimer(UINT nIDEvent)
{ file://TODO:Add your message handler
CProgressCtrl *pProgCtrl=(CProgressCtrl*)GetDlgItem(IDC_PROG);
int nPrePos=pProgCtrl->StepIt();//取得更新前位置
char test[10];
int nPercent=(int)(((nPrePos+m_nStep)/m_nMax*100+0.5);
wsprintf(test,%d%%,nPercent);
GetDlgItem(IDC_PERCENT)->SetWindowText(text);
Cdialog::OnTimer(nIDEvent);
}

 


第8章 演练CSlider
    滑动条控制(Slider Control)也叫轨道条控制,其主要是用一个带有轨道和滑标的小窗口以及窗口上的刻度,来让用户选择一个离散数据或一个连续的数值区间。通过鼠标或键盘来进行数据的选择操作,这在WIN98/95中的很多应用程序中都可以看到,如控制面板中的鼠标等,滑动条既可以是水平方式的也可以是垂直方式的。滑动条控制的风格如下:
    TBS_HORZ    滑动条是水平方向的
    TBS_VERT    滑动条是垂直方向的
    TBS_LEFT    滑动条位于窗口左侧
    TBS_RIGHT   滑动条位于窗口右侧
    TBS_TOP     滑动条位于窗口顶部
    TBS_BOTTOM  滑动条位于窗口底部
    TBS_BOTH    滑动条位于窗口两侧
    TBS_AUTOTICKS滑动条具有刻度,默认
    TBS_NOTICKS  滑动条不具有刻度
    滑动条的刻度条在每一个数值位置显示一个刻度标记,如果在滑动条上显示某一数值选择区间,则应使用风格TBS_ENABLESELRANGE,此时选择区间两个不再是刻度标记,而是一个小的三角形符号。另外,使用风格TBS_NOTHUMB会使滑标消隐起来。
    滑动条控制在MFC类库中被封装为CSliderCtrl控制,其主要操作是设置刻度范围、绘制刻度标记、设置选择范围和当前滑标位置等。当用户进行交互操作时,滑动条控制将向其父窗口发送消息WM_HSCROLL,所以在应用程序中应重载父窗口的OnHScroll()成员函数,以便对消息进行正确处理系统发送的通知代码、滑标位置和指向CSliderCtrl对象的指针等。由于考虑到和水平卷动杆公用同一个成员函数,OnHScroll()函数参数表中的指针变量被定义为CScrollBar*类型,由于实际上消息是由滑动条产生的,所以在程序中必须把这个指针变量强制转换为CSliderCtrl*类型。滑动条和卷动杆的消息代码和含义都非常类似如TB_BOTTOM等,所以这种处理方法比较合理。SetRange()函数用来设置范围,SetPos()函数用来设置当前位置。
8.1 滑动条控制的对象结构
    滑动条控制的建立方法
    CsliderCtrl &SliderCtrl  建立滑动条控制对象结构
    Create                   建立滑动条控制对象并绑定对象
    滑动条控制类CSliderCtrl::Create的调用格式如下:
    BOOL Create( DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID );
    其中参数dwStyle用来确定滑动条控制风格;参数rect用来确定滑动条控制的大小和位置;参数pParentWnd用来确定滑动条控制的父窗口指针;参数nID用来确定滑动条控制的控制符ID值。
    2、滑动条控制的类属性
    滑动条控制对象的类属性包括取得滑动条大小GetLineSize、设置滑动条大小SetLineSize、取得滑动条页大小GetPageSize、设置滑动条页大小SetPageSize、取得滑动条最大位置GetRangeMax、取得滑动条最小位置GetRangeMin、取得滑动条范围GetRange、设置滑块最小位置SetRangeMin、设置滑块最大位置SetRangeMax、设置滑动条范围SetRange、取得滑块当前位置GetSelection、设置滑块当前位置SetSelection、取得滑动条当前位置GetPos和设置滑动条当前位置SetPos等。
    3、滑动条控制的操作方法
    滑动条控制的操作方法包括清除滑动条当前选择ClearSel、验证滑动条当前位置是否在最大最小位置之间VerifyPos和清除当前刻度标志ClearTics。
滑动条控制的应用技巧示例
    1、利用应用程序向导AppWizard生成基于对象框的应用程序CSlidDlg;
    2、在对话框中设置滑动条控制,其ID为IDC_SLIDER;
    3、在对话框初始代码中增加控制的范围和位置:
    (1)在SlidDlg.h中设置数据成员,用来表示滑动条的当前值:
file://SlidDlg.h
class CSlidDlg:public Cdialog
{ ......//其它代码
public:
int m_nCur;
......//其它代码
}
    (2)在SlidDlg.cpp中设置初始状态
BOOL  CSlidDlg::OnInitDialog()
{ Cdialog::OnInitDialog();
......//其它代码
file://TODO:Add extra initialization here
CSliderCtrl *pSlidCtrl=(CSliderCtrl*)GetDlgItem(IDC_SLLIDER);
pSlidCtrl->SetRange(1,5,TRUE);//设置滑动条范围
pSlidCtrl->SetPos(2);//设置滑动条位置
......//其它代码
return TRUE;
}
    (3)完善滑动条的消息处理,利用类向导ClassWizard增加对话框窗口的WM_HSCROLL消息处理函数,并取得滑标所指位置值:
void CSlidDlg::OnHScroll(UINT nSBCode,UINT nPos,CScrollBar *pScrollBar)
{ file://TODO:Add your message handler
Cdialog::OnHScroll(nSBCode,nPos,pScrollBar);
CSliderCtrl *pSlidCtrl=(CSliderCtrl*)GetDlgItem(IDC_SLLIDER);
m_nCur=pSlidCtrl->GetPos();//取得当前位置值
}


第9章 演练CSpin
9.1 旋转按钮控制的主要功能
    旋转按钮控制(Spin Button Control)又称为上下控制(Up Down Control),其主要功能是利用一对标有相反方向箭头的小按钮,通过点击它来在一定范围内改变当前的数值。旋转按钮控制的当前值通常显示在一个称为伙伴窗口(Buddy Window)的控制中,可以是一个编辑框等。
    与其它控制一样,可以通过设置旋转按钮的风格来控制其显示外观。其风格如下:
    UDS_HORZ        旋转按钮是水平方向的
    UDS_VERT        旋转按钮是垂直方向的
    UDS_ALIGNLEFT   旋转按钮位于伙伴窗口的左侧
    UDS_ALIGNRIGHT  旋转按钮位于伙伴窗口的右侧
    UDS_AUTOBUDDY   把旋转按钮的伙伴窗口设置为对话框中的顺序控制,即TAB顺序前一个控    制
    UDS_SETBUDDYINT 旋转按钮当前值改变时自动改变伙伴窗口标题所显示的数值
    UDS_NOTHOUSANDS 取消显示的数字中每三位间的“,”分隔符
    UDS_WRAP        旋转按钮的当前值超出最大(最小)值时自动回绕到最小(最大)值
    UDS_ARROWKEYS   可以让用户用光标键来改变当前值
    旋转按钮也可以不在伙伴窗口的任何一侧。如果位于伙伴窗口的一侧,应适当减少伙伴窗口的宽度以容纳下旋转按钮。
    旋转按钮控制在MFC类库中被封装为CSpinButtonCtrl类,其操作主要是获取和设置旋转按钮的变化范围、当前数值、伙伴窗口、伙伴窗口显示当前数据的数值基十进制还是十六进制和用户按住按钮时数值变化速度的加速度等。SetRange()函数用来设置范围,SetPos()函数用来设置当前位置。
9.2 旋转按钮控制的对象结构
    1、旋转按钮控制的建立方法
    CspinButtonCtrl &SpinButtonCtrl 建立旋转按钮控制对象结构
    Create       建立旋转按钮控制对象并绑定对象
    旋转按钮控制类CSpinButtonCtrl::Create的调用格式如下:
    BOOL Create( DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID );
    其中参数dwStyle用来确定旋转按钮控制的风格;参数rect用来确定旋转按钮控制的大小和    位置;参数pParentWnd用来确定父窗口指针;参数nID用来确定旋转按钮的控制符ID值。
    2、旋转按钮控制的类属性
    旋转按钮控制的类属性包括设置加速键SetAccel、取得加速键GetAccel、设置基数SetBase、取得基数GetBase、设置伙伴窗口SetBuddy、取得伙伴窗口GetBuddy、设置当前位置SetPos、取得当前位置GetPos、设置上限下限值SetRange和取得上限下限值GetRange等。
9.3 旋转按钮控制的应用技巧示例
    1、利用应用程序向导AppWizard生成基于对象框的应用程序CSpinDlg;
    2、在对话框中设置旋转按钮控制和编辑框控制,其ID分别为IDC_SPIN和IDC_EDIT;
    在对话框初始代码中增加控制的范围和位置:
    (1)在SpinDlg.h中设置数据成员,用来表示旋转按钮的步长值:
file://SlidDlg.h
class CSlidDlg:public Cdialog
{ ......//其它代码
public:
int m_nStep;
......//其它代码
}
    (2)在SpinDlg.cpp中设置初始状态
BOOL  CSlidDlg::OnInitDialog()
{ Cdialog::OnInitDialog();
......//其它代码
file://TODO:Add extra initialization here
CSpinButtonCtrl *pSpinCtrl=(CSpinButtonCtrl*)GetDlgItem(IDC_SPIN);
pSpinCtrl->SetRange(-50,50);//设置旋转按钮范围
pSpinCtrl->SetPos(10);//设置旋转按钮位置
......//其它代码
return TRUE;
}
    (3)完善旋转按钮的消息处理,利用类向导ClassWizard增加对编辑框的EN_CHANGE消息处理函数,以使伙伴窗口编辑框的数据随着旋转按钮一起变化:
void CSPinDlg::OnChangeEdit()
{ file://TODO:Add your message handler
m_nStep=GetDlgItemInt(IDC_EDIT);
......//进行其它处理
}


第10章 演练CStatusBar
10.1 状态条控制的主要功能
    状态条控制(Status Bar Control)比较容易理解,使用起来也比较简单。状态条是位于父窗口底部的一个水平子窗口,它可以被分成多个显示信息的小区域。其MFC中封装的CstatusBarCtrl控制类提供了应用的基本方法。
10.2 状态条控制的对象结构
    状态条控制的建立方法
    CStatusBarCtrl &StatusBarCtrl  建立状态条控制对象结构
    Create                         建立状态条控制对象并绑定
    状态条控制类CstatusBarCtrl::Create的调用格式如下:
    BOOL Create( DWORD dwStyle,const RECT& rect,CWnd* pParentWnd,UINT nID);
    其中参数dwStyle用来确定状态条的控制风格;参数rect用来确定状态条窗口的大小和位置;参数pParentWnd用来确定状态条父窗口的指针;nID用来确定状态条控制的标识符。
    状态条控制风格可以是下列值的组合:CCS_BOTTOM、CCS_NODIVIDER、CCS_NOHILITE、CCS_NOMOVEY、CCS_NOPARENTALIGN、CCS_NORESIZE和CCS_TOP等,具体内容和含义请见工具条控制中的有关部分。
    2、状态条控制的类属性
    状态条控制类属性包括设置给定部分显示文本SetText、取得给定部分的文本GetText、设置状态条区域划分数及其每部分的右边坐标SetParts、取得状态条区域划分数量GetParts、取得状态条的水平和垂直宽度GetBorders和取得状态条矩形区域GetRect。
10.3 状态条控制的应用技巧
    状态条控制除可以显示一定的帮助和提示信息外,还可以实现响应鼠标输入等功能。这里以在状态条上显示鼠标移动坐标为例,具体说明其应用技巧。
     利用应用程序向导生成的程序代码中,状态条作为主窗口的子窗口,其具有一个AFX_IDW_STATUS _BAR标识符,通过它调用函数GetDescendantWindow()和AfxGetMainWnd(),就可以取得状态条窗口的指针。由于基于文档的应用程序在建立时就具有状态条区域,所以只要利用类向导简单地加入鼠标移动消息处理功能函数和下述函数代码,就可以实现这一功能:
 Void CTestView::OnMouseMove(UINT nFlags,Cpoint point)
 {
  CclientDC dc(this);//建立设备文本
  OnPrepareDC(&dc,NULL);//设备映射处理
  dc.DPtoLP(&point);//鼠标指针转换
  char text[128];
  CstatusBar *pStatus=(CstatusBar *)AfxGetApp()->m_pMainWnd->
       GetDescendanWindow(AFX_IDW_STATUS_BAR);//取得窗口指针
  If(pStatus){//如果存在显示鼠标坐标
   Sprintf(text,”X坐标=%4d,Y坐标=%4d”,point.x,point.y);
   pStatus->SetPaneText(0,text);}
  CscrollView::OnMouseMove(nFlags,point); 
  }

<广告一下,呵呵:http://blog.csdn.net/wangjiwei2010>
--------------------------------------------
<script type="text/javascript"><!--google_ad_client = "pub-2019358045871963";google_ad_width = 728;google_ad_height = 90;google_ad_format = "728x90_as";google_ad_type = "text_image";google_ad_channel = "";//--></script><script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"></script>