CSDN博客

img happyparrot

演练VC中的COMMON一族(转贴)之一

发表于2004/3/12 13:25:00  4875人阅读

分类: 控件应用

声明:由于在CSDN论坛看到不少VC新手问及VC控件的使用。本人手头正好有一份以前下载到的一份资料,讲述的比较详细。所以发出来和大家共享。但由于时间久远,已不及得下载的地址了。所以只能以收藏方式发表。下面有原作者的单位和姓名。在此表示歉意!

演练VC中的COMMON一族
辽宁省铁岭市委办公室(112000)宋立波
    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
    }
    (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);
}
标签控制的整个实现过程虽然比较繁锁,但只要掌握其实现的本质,设计一个优秀的标签控制界面也并非很困难的事情。
笔者实现的标签控制的演练示例结果如下:
                  标签控制演练示例结果


 

0 0

相关博文

我的热门文章

img
取 消
img