MFC---标签控件Tab Control(常用控件)

前面两节讲了树形控件Tree Control,本节开始讲解标签控件Tab Control,也可以称为选项卡控件。

标签控件简介

标签控件也比较常见。它可以把多个页面集成到一个窗口中,每个页面对应一个标签,用户点击某个标签时,它对应的页面就会显示。下图是Windows系统配置中标签控件的例子:

在这里插入图片描述

使用标签控件我们可以同时加载多个有关联的页面,用户只需点击标签即可实现页面切换,方便灵活的进行操作。每个标签除了可以显示标签文本,还可以显示图标。

标签控件相当于是一个页面的容器,可以容纳多个对话框,而且一般也只容纳对话框,所以我们不能直接在标签控件上添加其他控件,必须先将其他控件放到对话框中,再将对话框添加到标签控件中。最终我们点击标签切换页面时,切换的不是控件的组合,而是对话框。

标签控件的通知消息

在对标签控件进行一些操作,比如点击标签时,标签控件也会向父窗口发送一些通知消息。我们可以为这些通知消息添加处理函数,实现各种功能。标签控件的主要通知消息及含义如下所示:
TCN_SELCHANGE:通知父窗口控件的标签选择项已经改变
TCN_SELCHANGING 通知父窗口控件的标签选择项正在改变
TCN_KEYDOWN:通知父窗口在控件范围内键盘被按下
TCN_GETOBJECT:具有TCS_EX_REGISTERDROP扩展特性并且对象被拖动时的通知消息
TCN_FOCUSCHANGE:通知父窗口控件的按钮聚焦已经改变
NM_CLICK:通知父窗口用户在控件区域范围内点击了鼠标左键
NM_RCLICK:通知父窗口用户在控件区域范围内点击了鼠标右键
NM_RELEASEDCAPTURE:通知父窗口在控件区域范围内释放鼠标捕获消息

标签控件的相关结构体

标签控件在使用中也有一些相关的结构体经常用到,主要以下几个:

  1. TCITEMHEADER结构体

该结构体用来指定或获取标签控件本身的属性。用在TCM_INSERTITEM、TCM_GETITEM和TCM_SETITEM消息中。

typedef struct tagTCITEMHEADER {      
    UINT mask;   // 掩码,可以为TCIF_IMAGE(iImage成员有效)、TCIF_RTLREADING、TCIF_TEXT(pszText成员有效)   
    UINT lpReserved1;   // 预留   
    UINT lpReserved2;   // 预留   
    LPTSTR pszText;     // 标签文本字符串   
    int cchTextMax;      
    int iImage;         // 图标在标签控件图像序列中的索引   
} TCITEMHEADER, *LPTCITEMHEADER;
  1. TCITEM结构体

该结构体用来指定或获取标签页的属性。用在TCM_INSERTITEM、TCM_GETITEM和TCM_SETITEM消息中。

typedef struct tagTCITEM {     
    UINT mask;  // 掩码,可以是TCIF_IMAGE(iImage成员有效)、TCIF_PARAM(lParam成员有效)、TCIF_RTLREADING、TCIF_STATE、TCIF_TEXT(pszText成员有效)   
#if (_WIN32_IE >= 0x0300)   
    DWORD dwState;   
    DWORD dwStateMask;   
#else   
    UINT lpReserved1;   
    UINT lpReserved2;   
#endif   
    LPTSTR pszText;   
    int cchTextMax;   
    int iImage;   
    LPARAM lParam;     // 与标签页关联的32位数据   
} TCITEM, *LPTCITEM;
  1. TCHITTESTINFO结构体

该结构体包含了鼠标单击测试的信息。

typedef struct tagTCHITTESTINFO {   
    POINT pt;  // 鼠标点击测试的客户区坐标   
    UINT flags; // 接收点击测试的结果。有以下几种:TCHT_NOWHERE(坐标点不在标签上)、TCHT_ONITEM(坐标点在标签上但不在标签文本或图标上)、TCHT_ONITEMICON(坐标点在标签图标上)、TCHT_ONITEMLABEL(坐标点在标签文本上)   
} TCHITTESTINFO, *LPTCHITTESTINFO;
  1. NMTCKEYDOWN结构体
    该结构体包含了标签控件中键盘按下的相关信息。主要用在TCN_KEYDOWN通知消息中。
typedef struct tagNMTCKEYDOWN {   
    NMHDR hdr;   
    WORD wVKey;   
    UINT flags;   
} NMTCKEYDOWN;

标签控件的上半部分就讲到这里了,下节教程将继续讲解标签控件的知识和应用实例。

上一节中讲了标签控件知识的上半部分,本节继续讲下半部分。

标签控件的创建

MFC为标签控件的操作提供了CTabCtrl类。

与之前的控件类似,创建标签控件可以在对话框模板中直接拖入Tab Control,也可以使用CTabCtrl类的Create成员函数创建。Create函数的原型如下:

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

参数dwStyle为标签控件的风格,rect为标签控件的位置和大小,pParentWnd为指向标签控件父窗口的指针,nID指定标签控件的ID。这里还是要具体说下dwStyle,下面列出了几种主要的控件风格:
TCS_BUTTONS:标签(控件上部用来选择标签页的位置)外观为按钮风格,且整个控件周围没有边框。
TCS_FIXEDWIDTH :所有标签具有相同的宽度。
TCS_MULTILINE:标签以多行显示,如果需要,可以显示所有标签。
TCS_SINGLELINE:只显示一行标签,用户可以滚动着看其他标签。
TCS_TABS:标签以普通标签样式显示,且整个控件周围有边框。
如果想了解标签控件的所有风格,可以查阅MSDN。

CTabCtrl类的主要成员函数

       int GetCurSel( ) const;

获取标签控件中当前选择标签的索引。如果成功则返回选择标签的索引,否则返回-1。

       BOOL GetItem(int nItem,TCITEM* pTabCtrlItem) const;

获取标签控件中某个标签的信息。参数nItem为标签索引,pTabCtrlItem为指向TCITEM结构体的指针,用来接收标签信息。若获取成功返回TRUE,否则返回FALSE。

       int GetItemCount( ) const;

获取标签控件中标签的数量。

       int SetCurSel(int nItem);

在标签控件中选择某标签。参数nItem为要选择的标签的索引。如果成功则返回之前选择标签的索引,否则返回-1。

       BOOL SetItem(int nItem,TCITEM* pTabCtrlItem);

设置某标签的所有或部分属性。参数nItem为标签的索引,pTabCtrlItem为指向TCITEM结构体的指针,包含了新的标签属性。成功则返回TRUE,否则返回FALSE。

       BOOL DeleteAllItems( );

删除标签控件中所有标签。

       BOOL DeleteItem(int nItem);

删除标签控件中的某个标签。参数nItem为要删除标签的索引。

       LONG InsertItem(int nItem,LPCTSTR lpszItem);

在标签控件中插入新的标签。参数nItem为新标签的索引,lpszItem为标签文本字符串。如果插入成功则返回新标签的索引,否则返回-1。

标签控件的应用实例

最后依然是给大家写一个简单的实例,说明CTabCtrl类的几个成员函数及标签控件通知消息等的使用方法。

此实例实现的功能:在一个标签控件中加入两个标签页,标签文本分别为“wdl”和“Android开发网”,点击不同的标签显示不同的标签页。下面是具体实现步骤:

  1. 创建一个基于对话框的MFC工程,名称设置为“Example33”。

  2. 在自动生成的对话框模板IDD_EXAMPLE33_DIALOG中,删除“TODO: Place dialog controls here.”静态文本框、“OK”按钮和“Cancel”按钮。添加一个Tab Control控件,并为其关联一个CTabCtrl类型的控件变量m_tab。

  3. 创建两个新的对话框,ID分别设为IDD_WDL_DIALOG、IDD_ANDROID_DIALOG,两者都将Border属性设为None,Style属性设为Child。在对话框模板IDD_WDL_DIALOG中加入一个静态文本框,Caption属性设为“wdl www.wdl.com”,并为其生成对话框类CWdlDlg;在对话框模板IDD_ANDROID_DIALOG中也加入一个静态文本框,Caption属性设为“Android开发网 www.wdl.com/android”,并为其生成对话框类CAndroidDlg。

  4. 在“Example33Dlg.h”文件中包含“WdlDlg.h”和“AndroidDlg.h”两个头文件,然后继续在“Example33Dlg.h”文件中为CExample33Dlg类添加两个成员变量:

	CWdlDlg m_wdlDlg;
	CAndroidDlg m_androidDlg;
  1. 在CExample33Dlg对话框初始化时,我们也初始化标签控件。修改CExample33Dlg::OnInitDialog()函数如下:
BOOL CExample33Dlg::OnInitDialog()   
{   
    CDialogEx::OnInitDialog();   
   
    // Add "About..." menu item to system menu.   
   
    // IDM_ABOUTBOX must be in the system command range.   
    ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);   
    ASSERT(IDM_ABOUTBOX < 0xF000);   
   
    CMenu* pSysMenu = GetSystemMenu(FALSE);   
    if (pSysMenu != NULL)   
    {   
        BOOL bNameValid;   
        CString strAboutMenu;   
        bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);   
        ASSERT(bNameValid);   
        if (!strAboutMenu.IsEmpty())   
        {   
            pSysMenu->AppendMenu(MF_SEPARATOR);   
            pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);   
        }   
    }   
   
    // Set the icon for this dialog.  The framework does this automatically   
    //  when the application's main window is not a dialog   
    SetIcon(m_hIcon, TRUE);         // Set big icon   
    SetIcon(m_hIcon, FALSE);        // Set small icon   
   
    // TODO: Add extra initialization here   
    CRect tabRect;   // 标签控件客户区的位置和大小   
   
    m_tab.InsertItem(0, _T("Wdl"));         // 插入第一个标签“Wdl”  
    m_tab.InsertItem(1, _T("Android开发网"));  // 插入第二个标签“Android开发网”   
    m_wdlDlg.Create(IDD_WDL_DIALOG, &m_tab);    // 创建第一个标签页   
    m_androidDlg.Create(IDD_ANDROID_DIALOG, &m_tab); // 创建第二个标签页   
   
    m_tab.GetClientRect(&tabRect);    // 获取标签控件客户区Rect   
    // 调整tabRect,使其覆盖范围适合放置标签页   
    tabRect.left += 1;                  
    tabRect.right -= 1;   
    tabRect.top += 25;   
    tabRect.bottom -= 1;   
    // 根据调整好的tabRect放置m_wdlDlg子对话框,并设置为显示   
    m_wdlDlg.SetWindowPos(NULL, tabRect.left, tabRect.top, tabRect.Width(), tabRect.Height(), SWP_SHOWWINDOW);   
    // 根据调整好的tabRect放置m_androidDlg子对话框,并设置为隐藏   
    m_androidDlg.SetWindowPos(NULL, tabRect.left, tabRect.top, tabRect.Width(), tabRect.Height(), SWP_HIDEWINDOW);   
   
    return TRUE;  // return TRUE  unless you set the focus to a control   
}
  1. 运行程序,查看结果,这时我们发现切换标签时,标签页并不跟着切换,而总是显示CWdlDlg对话框。

  2. 我们要实现的是标签页的切换效果,所以还要为m_tab标签控件的通知消息TCN_SELCHANGE添加处理函数,并修改如下:

void CExample33Dlg::OnTcnSelchangeTab1(NMHDR *pNMHDR, LRESULT *pResult)   
{   
    // TODO: Add your control notification handler code here   
    *pResult = 0;   
    CRect tabRect;    // 标签控件客户区的Rect   
   
    // 获取标签控件客户区Rect,并对其调整,以适合放置标签页   
    m_tab.GetClientRect(&tabRect);   
    tabRect.left += 1;   
    tabRect.right -= 1;   
    tabRect.top += 25;   
    tabRect.bottom -= 1;   
   
    switch (m_tab.GetCurSel())   
    {   
    // 如果标签控件当前选择标签为“鸡啄米”,则显示m_jzmDlg对话框,隐藏m_androidDlg对话框   
    case 0:   
        m_wdlDlg.SetWindowPos(NULL, tabRect.left, tabRect.top, tabRect.Width(), tabRect.Height(), SWP_SHOWWINDOW);   
        m_androidDlg.SetWindowPos(NULL, tabRect.left, tabRect.top, tabRect.Width(), tabRect.Height(), SWP_HIDEWINDOW);   
        break;   
    // 如果标签控件当前选择标签为“Android开发网”,则隐藏m_jzmDlg对话框,显示m_androidDlg对话框   
    case 1:   
        m_wdlDlg.SetWindowPos(NULL, tabRect.left, tabRect.top, tabRect.Width(), tabRect.Height(), SWP_HIDEWINDOW);   
        m_androidDlg.SetWindowPos(NULL, tabRect.left, tabRect.top, tabRect.Width(), tabRect.Height(), SWP_SHOWWINDOW);   
        break;   
    default:   
        break;   
    }   
}
   8. 再运行程序,最终的标签页切换效果如下面两图:

标签控件第一个标签页
在这里插入图片描述

标签页第二个标签页
在这里插入图片描述

经过两讲内容,终于把标签控件的主要知识讲完了。如果想了解更多的相关内容,可以查看MSDN。

  • 38
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Frank---7

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值