Visual C++程序开发范例宝典(软件工程师典藏版)
上QQ阅读APP看书,第一时间看更新

2.13 标签控件典型实例

标签控件(Tab Control)提供了一组水平标签,可以单击不同的标签进入相应的页面。但标签控件不能直接在各个标签页上插入控件,只能在选中不同标签页时显示不同的对话框或控件。

实例084 应用标签控件

这是一个可以提高基础技能的实例

实例位置:光盘\mingrisoft\02\084

实例说明

在程序中经常会使用标签控件来切换界面。本实例实现了使用标签控件进行切换的功能,运行结果如图2.51所示。

图2.51 应用标签控件

技术要点

在使用标签控件时,最主要的功能就是,选择不同的标签时有不同的显示信息,要实现这一功能,需要使用标签控件的TCN_SELCHANGE事件,该事件当选中标签改变后触发。可以在该事件的处理函数中使用GetCurSel方法和SetCurSel方法获得或设置当前被选中的标签索引。

(1)GetCurSel方法。GetCurSel方法用于获得当前被选中的标签索引,语法如下:

int GetCurSel( ) const;

返回值:返回当前被选中的标签项索引。

(2)SetCurSel方法。SetCurSel方法用于将某个标签设置为当前选中的标签。语法如下:

int SetCurSel( int nItem );

参数说明:

● nItem:标识标签索引。

返回值:之前选中的标签索引。

实现过程

(1)新建一个基于对话框的应用程序。

(2)添加3个对话框资源,资源ID分别为IDD_DIALOG_EMP、IDD_DIALOG_CLI和IDD_DIALOG_PRO,并设置对话框资源的Style属性为Child,Border属性为None,为3个对话框资源关联类,分别为CEmployee 、CClient和Cprovidedlg,并分别向对话框资源中添加控件。

(3)向主对话框中添加一个标签控件,并为标签控件关联变量m_Tab。

(4)在主对话框头文件中声明1个图像列表对象和3个对话框类对象,代码如下:

              CImageList     m_ImageList;                 //图像列表对象
              CEmployee*   m_eDlg;                        //员工对话框对象
              CClient*       m_cDlg;                      //客户对话框对象
              CProvidedlg*   m_pDlg;                      //供应商对话框对象

(5)在主对话框的OnInitDialog函数中创建图像列表,向图像列表中添加图标,关联标签控件和图像列表控件,并创建对话框,代码如下:

              m_ImageList.Create(24,24,ILC_COLOR24|ILC_MASK,1,0);        //创建图像列表
              m_ImageList.Add(AfxGetApp()->LoadIcon(IDI_ICON1));         //向图像列表中添加图标
              m_ImageList.Add(AfxGetApp()->LoadIcon(IDI_ICON2));         //向图像列表中添加图标
              m_ImageList.Add(AfxGetApp()->LoadIcon(IDI_ICON3));         //向图像列表中添加图标
              m_Tab.SetImageList(&m_ImageList);                          //将图像列表关联到标签控件中
              m_Tab.InsertItem(0,"员工信息",0);                          //插入标签项
              m_Tab.InsertItem(1,"客户信息",1);                          //插入标签项
              m_Tab.InsertItem(2,"供应商信息",2);                        //插入标签项
              m_eDlg=new CEmployee;                                       //为指针分配内存空间
              m_cDlg=new CClient;                                         //为指针分配内存空间
              m_pDlg=new CProvidedlg;                                     //为指针分配内存空间
              m_eDlg->Create(IDD_DIALOG_CLI,&m_Tab);                     //创建员工对话框
              m_cDlg->Create(IDD_DIALOG_EMP,&m_Tab);                     //创建客户对话框
              m_pDlg->Create(IDD_DIALOG_PRO,&m_Tab);                     //创建供应商对话框
              m_eDlg->CenterWindow();                                    //设置员工窗口在中心位置
              m_eDlg->ShowWindow(SW_SHOW);                               //显示客户窗口

(6)处理对话框的TCN_SELCHANGE事件,在该事件中获得当前选中标签项的索引,根据索引判断显示的对话框,代码如下:

          void CUseTabDlg::OnSelchangeTab1(NMHDR*pNMHDR,LRESULT*pResult)        //事件处理函数
          {
              int index=m_Tab.GetCurSel();                                      //获得当前选中标签项索引
              switch(index)                                                    //判断标签项索引值
              {
              case 0:                                                           //值为0时
                  m_eDlg->CenterWindow();                                      //设置员工对话框在中心位置
                  m_eDlg->ShowWindow(SW_SHOW);                                 //显示员工对话框
                  m_cDlg->ShowWindow(SW_HIDE);                                 //隐藏客户对话框
                  m_pDlg->ShowWindow(SW_HIDE);                                 //隐藏供应商对话框
                  break;
              case 1:                                                           //值为1时
                  m_cDlg->CenterWindow();                                      //设置客户对话框在中心位置
                  m_eDlg->ShowWindow(SW_HIDE);                                 //隐藏员工对话框
                  m_cDlg->ShowWindow(SW_SHOW);                                 //显示客户对话框
                  m_pDlg->ShowWindow(SW_HIDE);                                 //隐藏供应商对话框
                  break;
              case 2:                                                           //值为2时
                  m_pDlg->CenterWindow();                                      //设置供应商对话框在中心位置
                  m_eDlg->ShowWindow(SW_HIDE);                                 //隐藏员工对话框
                  m_cDlg->ShowWindow(SW_HIDE);                                 //隐藏客户对话框
                  m_pDlg->ShowWindow(SW_SHOW);                                 //显示供应商对话框
                 break;
            }
            *pResult = 0;
        }

(7)处理对话框的WM_CLOSE事件,在主对话框关闭时销毁3个非模态对话框,并释放指针,代码如下:

        void CUseTabDlg::OnClose()
        {
            m_eDlg->DestroyWindow();                                   //销毁员工对话框
            delete m_eDlg;                                              //释放员工对话框指针
            m_cDlg->DestroyWindow();                                   //销毁客户对话框
            delete m_cDlg;                                              //释放客户对话框指针
            m_pDlg->DestroyWindow();                                   //销毁供应商对话框
            delete m_pDlg;                                              //释放供应商对话框指针
            CDialog::OnClose();                                        //关闭对话框
        }

举一反三

根据本实例,读者可以:

使用标签控件控制显示的控件。

实例085 自定义标签控件

这是一个可以提高基础技能的实例

实例位置:光盘\mingrisoft\02\085

实例说明

图2.52 自定义标签控件

许多应用软件都具有漂亮的标签控件,本实例通过自绘标签控件实现了一个类似.NET环境下的标签控件,实例运行结果如图2.52所示。

技术要点

在设计自定义选项卡时,主要应用了设备上下文CDC类的几个方法实现区域的填充、文本绘制等功能。下面逐一介绍。

(1)FillSolidRect方法。设备上下文的FillSolidRect方法用于使用指定的颜色来填充矩形区域。语法如下:

void FillSolidRect(LPCRECT lpRect, COLORREF clr);

参数说明:

● lpRect:表示填充的矩形区域。

● clr:表示使用的RGB颜色值。

(2)SetTextColor方法。设备上下文的SetTextColor方法用于设置输出的文本颜色。语法如下:

virtual COLORREF SetTextColor(COLORREF crColor);

参数说明:

● crColor:表示设置的文本颜色。

返回值:表示之前设备上下文的文本颜色。

(3)SetBkMode方法。设备上下文的SetBkMode方法用于设置背景模式。语法如下:

int SetBkMode(int nBkMode);

● nBkMode:表示设置的背景模式,为OPAQUE,表示使用当前的背景颜色填充背景。为TRANSPARENT,表示使用透明的背景模式。

返回值:表示之前设备上下文的背景模式。

(4)SelectObject方法。设备上下文的SelectObject方法用于选中一个GDI对象。例如,当用户需要输出文本时,可以定义一个字体对象,然后利用该方法载入字体,之后输出的文本将以载入的字体输出。语法如下:

CGdiObject* SelectObject(CGdiObject* pObject);

● pObject:表示一个GDI对象,可以是画刷、画笔、字体、位图等对象。

返回值:表示设备上下文之前选中的GDI对象。

(5)DrawText方法。设备上下文的DrawText方法用于按指定的格式输入文本。语法如下:

int DrawText(const CString& str, LPRECT lpRect, UINT nFormat);

● str:表示输出的字符串。

● lpRect:表示输出字符串所在的矩形区域。

● nFormat:表示文本输出格式,是一组标记的集合。为DT_LEFT,表示水平居左对齐,为DT_VCENTER,表示垂直居中,为DT_SINGLELINE,表示单行输出,不回行。

实现过程

(1)新建一个基于对话框的应用程序。

(2)以CTabCtrl类为基类派生一个子类CNetTabCtrl。

(3)向对话框中添加一个标签控件,设置标签控件的Bottom属性,添加一个CNetTabCtrl类的成员变量m_Tab。

(4)向CNetTabCtrl中添加DrawTabBorder方法,绘制标签页的边框。代码如下:

          void CNetTabCtrl::DrawTabBorder(CDC *pDC, CRect &TabRC)
          {
              pDC->Draw3dRect(&TabRC, RGB(255, 255, 255), RGB(177, 174, 162));//绘制标签控件的矩形边框
          }

(5)向CNetTabCtrl中添加DrawItemFrame方法,绘制标签控件中各个选项卡的边框。代码如下:

          void CNetTabCtrl::DrawItemFrame(DRAWITEMSTRUCT *lpDrawItem)
          {
              int nCurSel=GetCurSel();                                 //获取选中的选项卡索引
              int nIndex=lpDrawItem->itemID;                           //获取当前的选项卡索引
              BOOL bSelected=(nCurSel==nIndex);                        //判断选中的选项卡与当前的选项卡是否相同
              CDC dc;
              CRect itemRC=lpDrawItem->rcItem;                         //获取选项卡的矩形区域
              dc.Attach(lpDrawItem->hDC);
              if(bSelected)                                           //项目处于选中状态
              {
                  itemRC.OffsetRect(-1, 0);
                  dc.FrameRect(&itemRC,&CBrush(RGB(172,168,153)));    //绘制选项卡边框
              }
              else                                                    //项目处于非选中状态
              {
                  //绘制左右两端的分隔线
                  if (nIndex == 0)
                      dc.FillSolidRect(itemRC.left, itemRC.top + 2, 1, itemRC.Height() -4, RGB(172, 168, 153));
                  dc.FillSolidRect(itemRC.right -1, itemRC.top + 2, 1, itemRC.Height() -4, RGB(172, 168, 153));
              }
              dc.Detach();                                            //分离设备上下文句柄
          }

(6)改写标签控件的DrawItem虚方法,根据当前选项卡的不同状态,绘制不同效果的选项卡。代码如下:

            void CNetTabCtrl::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
            {
              ASSERT(lpDrawItemStruct->CtlType == ODT_TAB);
              CDC dc;
              dc.Attach(lpDrawItemStruct->hDC);
              CRect itemRC(lpDrawItemStruct->rcItem);                         //获取项目区域
              int nIndex=lpDrawItemStruct->itemID;                            //获取项目索引
              int nState=lpDrawItemStruct->itemState;                         //获取项目状态
              static COLORREF clrBK=RGB(229,229,215);                          //定义默认背景颜色
              static COLORREF clrSelBK=RGB(252,252,254);                       //定义选中的背景颜色
              COLORREF clrText = RGB(113, 111, 100);
              if(nState&ODS_SELECTED)                                        //处于选中状态
              {
                    dc.FillSolidRect((CRect)lpDrawItemStruct->rcItem,clrSelBK);
                    clrText=RGB(0,0,0);
              }
              else                                                           //非选中状态
            {
              if(nIndex==0)                                                     //第一个选项卡
              {
                    CRect itemRC = lpDrawItemStruct->rcItem;
                    itemRC.OffsetRect(-1, 0);
                    dc.FillSolidRect(itemRC,clrBK);                             //填充选项卡区域
              }
              else
              {
                    dc.FillSolidRect((CRect)lpDrawItemStruct->rcItem,clrBK);    //填充选项卡区域
              }
            }
            //绘制项目文本和图像
            char szText[MAX_PATH]={0};                                           //定义字符数组
            TC_ITEM tcItem;                                                      //定义选项卡对象
            tcItem.mask        =TCIF_TEXT|TCIF_IMAGE;                           //设置掩码
            tcItem.pszText    =szText;
            tcItem.cchTextMax  =MAX_PATH;
            GetItem(nIndex,&tcItem);                                            //获取选项卡信息
            CImageList*pImages=GetImageList();                                  //获取控件关联的图像列表
            if (pImages != NULL)
            {
              CPoint iconPT;
              iconPT.x = itemRC.left;
              iconPT.y = itemRC.top;
              pImages->Draw(&dc,tcItem.iImage,iconPT,ILD_NORMAL);               //绘制选项卡显示的位图
            }
            itemRC.left+=40;                                                    //设置位图与文本的间距
            dc.SetTextColor(clrText);                                           //设置文本颜色
            dc.SetBkMode(TRANSPARENT);                                          //设置透明的背景模式
            CFont*pFont=GetFont();                                              //获取字体对象
            dc.SelectObject(pFont);                                             //选中字体对象
            //输出文本
            dc.DrawText(szText, strlen(szText), itemRC, DT_LEFT|DT_VCENTER|DT_SINGLELINE);
            dc.Detach();                                                        //分离设备上下文句柄
        }

(7)处理标签控件的WM_PAINT消息,在其消息处理函数中绘制每个选项卡。代码如下:

        void CNetTabCtrl::OnPaint()
        {
            CPaintDC dc(this);
            DRAWITEMSTRUCT drawItem;                                        //定义一个选项结构
            drawItem.CtlType=ODT_TAB;                                      //设置选项类型为选项卡
            drawItem.CtlID=GetDlgCtrlID();                                 //获取标签控件的ID
            drawItem.hwndItem=GetSafeHwnd();                               //获取标签控件句柄
            drawItem.hDC=dc.GetSafeHdc();                                  //获取设备上下文
            drawItem.itemAction=ODA_DRAWENTIRE;                            //设置自绘风格
            GetClientRect(&drawItem.rcItem);                               //获取客户区域
            CRect clientRC;
            CRect pageRC;
            pageRC = drawItem.rcItem;
            AdjustRect(FALSE,pageRC);                                      //调整区域
            drawItem.rcItem.top = pageRC.top -2;
            DrawTabBorder(&dc,(CRect)drawItem.rcItem);                     //绘制标签页的边框
            int nItemCount=GetItemCount();                                  //获取选项卡数量
            int nCurSel=GetCurSel();                                        //获取选中的选项卡索引
            if (nItemCount < 1)
              return;
            for(int i=0;i<nItemCount;i++)                                   //遍历选项卡
            {
              drawItem.itemID = i;
              if(i==nCurSel)                                               //当前选项卡处于选中状态
                    drawItem.itemState=ODS_SELECTED;                       //设置选中标记
              else
                    drawItem.itemState = 0;
              GetItemRect(i,&drawItem.rcItem);                             //获取选项卡的区域
              DrawItem(&drawItem);                                         //调用DrawItem方法绘制选项卡
              DrawItemFrame(&drawItem);                                    //绘制项目边框
            }
        }

(8)改写标签控件的PreSubclassWindow方法,设置标签控件选项卡最小宽度和默认的宽度和高度。代码如下:

        void CNetTabCtrl::PreSubclassWindow()
        {
            SetMinTabWidth(100);                                         //设置最小选项卡的宽度
            SetItemSize(CSize(120,32));                                  //设置选项卡的宽度和高度
            CTabCtrl::PreSubclassWindow();
        }

(9)创建一个对话框类—CIndexDlg,向对话框中添加编辑框和图片控件。设置对话框Style属性为Child,设置Border属性为None。

(10)在主对话框中利用类向导为标签控件命名为“m_Tab”,其类型为CNetTabCtrl。向主对话框类中添加成员变量。代码如下:

          CImageList m_ImageList;                                        //定义图像列表
          CIndexDlg m_IndexDlg;                                          //定义子对话框

(11)在主对话框初始化时创建子对话框,创建图像列表,向图像列表中添加位图,向标签控件中添加选项卡。代码如下:

          m_IndexDlg.Create(IDD_INDEXDLG_DIALOG,&m_Tab);                   //在标签控件上创建子窗口
          m_ImageList.Create(32,32,ILC_COLOR16|ILC_MASK,1,1);              //创建图像列表控件
          CBitmap bmp;                                                      //定义位图对象
          bmp.LoadBitmap(IDB_BITMAP1);                                     //载入位图
          m_ImageList.Add(&bmp,RGB(255,255,255));                          //向图像列表中添加位图
          bmp.Detach();                                                    //分离位图句柄
          bmp.LoadBitmap(IDB_BITMAP2);                                     //载入位图
          m_ImageList.Add(&bmp,RGB(255,255,255));                          //向图像列表中添加位图
          bmp.Detach();                                                    //分离位图句柄
          bmp.LoadBitmap(IDB_BITMAP3);                                     //载入位图
          m_ImageList.Add(&bmp,RGB(255,255,255));                          //向图像列表中添加位图
          bmp.Detach();                                                    //分离位图句柄
          m_Tab.SetImageList(&m_ImageList);                                //为标签控件设置选项卡
          m_Tab.InsertItem(0,"索引",0);                                    //向标签控件中插入选项卡
          m_Tab.InsertItem(1, "查找", 1);
          m_Tab.InsertItem(2, "目录", 2);
          m_Tab.SetCurSel(0);                                              //将第一个选项卡选中
          LRESULT result;
          OnSelchangeTab1(NULL,&result);                                   //当前选项卡发生了改变

举一反三

根据本实例,读者可以:

设计具有热点效果的标签控件。