CTabCtrl控件的简化使用
实例下载:https://download.csdn.net/download/yaoyuanyylyy/13122971
前面通过定义了YTABCTRL_一系列的宏来简化CTabCtrl控件的使用,现在重新将其定义为一个类集,在使用的时候,只要继承此类即可更方便使用。
YTabCtrl类包括的方法如下:
- TabCtrl_init:初始化CTabCtrl控件,有两个重载。可以直接传入父窗口的CTabCtrl控件指针,或者传入父窗口指针和控件ID来初始化。
- TabCtrl_add_page:添加页面,需在TabCtrl_init之后调用。此方法包含一个模板类型参数,用来表示子页面的类型,且必须是CDialog的子类型。另外需要两个参数,即子页面的ID和页面标题。
- TabCtrl_finish:完成页面添加操作,需在添加完页面后(即TabCtrl_finish后)调用。可传入一个参数,用来表示是否显示CTabCtrl控件的标签栏,默认为true表示要显示。
- TabCtrl_update:更新子页面。在CTabCtrl控件的TCN_SELCHANGE单击后的事件中调用一下即可。
- TabCtrl_show:显示指定的子页面。显示传入的索引index参数所指向的子页面。
- TabCtrl_is_page:判断当前页面是否是给定的子页面对话框类的实例。子页面对话框类型通过模板参数来提供。
- TabCtrl_current_page:获取当前页面所属的子页面对话框类的指针。子页面对话框类型通过模板参数来提供。
- TabCtrl_has_page:判断当前的CTabCtrl中是否有子页面。在所添加的子页面的创建过程中,可能会因为某些条件或bug导致创建子页面失败,这时候子页面就不会添加到CTabCtrl控件中。
YTabCtrl中使用std::vector来存储子页面指针,在其析构函数中释放。使用此类只需要继承即可,不用考虑指针释放问题。
使用步骤
首先,子类对话框使用时在包含"YTabCtrl.h"文件后,直接继承该类即可。
其次,在对话框的OnInitDialog方法中添加子页面。
最后,在CTabCtrl的TCN_SELCHANGE单击后的事件中更新子页面。
CTabCtrl的嵌套
如果有嵌套使用CTabCtrl的需求,只要在子页面中按照使用步骤继续即可。
子页面判断与获取
在如下的示例中,可以在最外层的主对话框中判断CTabCtrl控件中当前处于哪个子页面,即使子页面又包含了子页面也可以方便地判断出来。
判断代码如下:
YTabCtrl代码
#ifndef YY_TABCTRL_INCLUDE_H__
#define YY_TABCTRL_INCLUDE_H__
#pragma once
#include <typeinfo>
#include <vector>
/**
* MFC CTabCtrl 使用方法的简化:通过类继承类使用
*/
class YTabCtrl
{
private:
typedef std::vector<std::pair<CDialog*, CString>> MAP_PAGE;
MAP_PAGE m_mapPages__;
CTabCtrl* m_pTabCtrl__;
size_t m_nCurSelTab__;
public:
YTabCtrl() : m_pTabCtrl__(nullptr), m_nCurSelTab__(0)
{
}
virtual ~YTabCtrl() {
for (MAP_PAGE::iterator iter = m_mapPages__.begin(); iter != m_mapPages__.end(); ++iter)
delete iter->first;
}
public:
// CTabCtrl初始化:传入父窗口指针和CTabCtrl控件的ID
void TabCtrl_init(CWnd* parent, UINT id) {
m_pTabCtrl__ = (CTabCtrl*)parent->GetDlgItem(id);
}
// CTabCtrl初始化:传入CTabCtrl控件变量指针
void TabCtrl_init(CTabCtrl* pTabCtrl) {
m_pTabCtrl__ = pTabCtrl;
}
// 添加子页面
template<typename T, typename = std::enable_if<std::is_base_of<CDialog, T>::value>::type >
void TabCtrl_add_page(UINT pageDlgID, CString title) {
CDialog* dlg = new T;
#ifdef AFX_DESIGN_TIME
dlg->Create(T::IDD, m_pTabCtrl__);
#else
dlg->Create(pageDlgID, m_pTabCtrl__);
#endif
if (dlg->m_hWnd != nullptr)
m_mapPages__.push_back(std::make_pair(dlg, title));
else
delete dlg;
}
// 设置子页面:是否示CTabCtrl控件的标题标签
void TabCtrl_finish(bool showTitle = true) {
CRect rcTab_;
m_pTabCtrl__->GetClientRect(rcTab_);
if (showTitle)
rcTab_.top += 22;
else
rcTab_.top += 0;
rcTab_.bottom -= 2;
rcTab_.left += 1;
rcTab_.right -= 3;
for (MAP_PAGE::iterator iter_ = m_mapPages__.begin(); iter_ != m_mapPages__.end(); ++iter_)
{
iter_->first->MoveWindow(&rcTab_);
iter_->first->ShowWindow(SW_HIDE);
m_pTabCtrl__->InsertItem(m_pTabCtrl__->GetItemCount(), iter_->second);
}
if (m_mapPages__.size() > 0 && (m_nCurSelTab__ < (int)m_mapPages__.size()))
m_mapPages__.at(m_nCurSelTab__).first->ShowWindow(SW_SHOW);
}
// 单击TabCtrl时更新页面
void TabCtrl_update() {
TabCtrl_show(m_pTabCtrl__->GetCurSel());
}
// 显示指定的页面
void TabCtrl_show(size_t index) {
if (index > m_mapPages__.size()) return;
m_mapPages__.at(m_nCurSelTab__).first->ShowWindow(SW_HIDE);
m_mapPages__.at(index).first->ShowWindow(SW_SHOW);
m_nCurSelTab__ = index;
}
// RTTI:判断当前页面是否是给定的对话框类的实例
template<typename T, typename = std::enable_if<std::is_base_of<CDialog, T>::value>::type >
bool TabCtrl_is_page() const {
return (m_nCurSelTab__ >= 0) && (typeid(*m_mapPages__.at(m_nCurSelTab__).first) == typeid(T));
}
// 获取当前页面
template<typename T, typename = std::enable_if<std::is_base_of<CDialog, T>::value>::type >
T* TabCtrl_current_page() const {
return (m_nCurSelTab__ >= 0) ? (T*)m_mapPages__.at(m_nCurSelTab__).first : nullptr;
}
//当前CTabCtrl控件是否有子页面
bool TabCtrl_has_page() const {
return m_mapPages__.size() > 0;
}
};
#endif
下面是旧版本的通过宏来使用的遗留,请忽略
修改:
- 2018-06-30:添加YTABCTRL_RELEASE宏,用来在使用完之后释放vector中的页面指针,可在对话框析构函数中调用
- 2018-06-05:使用std::vector存储页面,简化YTABCTRL_ADD_PAGE定义,不再传入序号,按添加顺序显示页面
- 2019-01-26:修改 YTABCTRL_IS_PAGE 和 YTABCTRL_CURRENT_PAGE 的定义的条件判断,其中的__m_nCurSelTab判断应该为>=0
- 2019-10-16:修复:宏YTABCTRL_INIT的id参数没有使用;宏YTABCTRL_FINISH中如果没有添加任何页面则不显示;无需再包含typeinfo.h头文件
- 2019-10-29:YTABCTRL_FINISH宏的最后的比较中添加类型转换,要不然总有个警告提示;__m_nCurSelTab<(int)__m_mapPages.size())
- 2020-05-28:YTABCTRL_SHOW宏中__m_nCurSelTab名称未加前缀
为了更方便地使用VC++中的CTabCtrl控件,特在YTabCtrl.h文件中定义了几个宏,具体包括:
- YTABCTRL_DECLEAR:成员声明,该宏在包含CTabCtrl控件的对话框的类定义中使用。
- YTABCTRL_INIT:CTabCtrl关联初始化,首先应调用该函数实现自己的CTabCtrl控件与本代码的关联。需传入CTabc控件的ID
- YTABCTRL_INIT_FROM_VAR:和YTABCTRL_INIT的功能一样,只是传入的是CTabCtrl控件的变量指针。
- YTABCTRL_ADD_PAGE:项TAB控件中添加页面,添加的顺序就是页面在TabCtrl中的页面顺序。传入的参数包括页面的类名,页面的ID,页面在TAB控件中显示的标题文字。
- YTABCTRL_FINISH:完成页面与TAB控件之间的功能
- YTABCTRL_UPDATE:单击CTabCtrl时更新页面。该宏需在CTabCtrl的单击事件中调用,单击消息响应由自己添加
- YTABCTRL_IS_PAGE:运行时类型识别。判断当前页面是否是给定的对话框类的实例,需传入要判断的类名
- YTABCTRL_CURRENT_PAGE:获取当前页面的对话框指针,返回对象为页面对话框的基类CDialog指针,可以转换为页面对话框类对象
- YTABCTRL_RELEASE:释放页面指针。采用std::vector来保存页面指针,在使用完成之后需要释放掉
一个简单的例子如下:
使用步骤如下,简称CTabCtrl所在对话框类为父类(具体请下载例子查看):
1.父类的定义中调用:
YTABCTRL_DECLEAR();
2.在父类的OnInitDialog函数中调用:
YTABCTRL_INIT(IDC_TAB1);
YTABCTRL_ADD_PAGE(CPageOneDlg, IDD_DLG_PAGE_ONE, _T("页面1"));
YTABCTRL_ADD_PAGE(CPageTwoDlg, IDD_DLG_PAGE_TWO, _T("页面2"));
YTABCTRL_ADD_PAGE(CPageThreeDlg, IDD_DLG_PAGE_THREE, _T("页面3"));
YTABCTRL_ADD_PAGE(CPageFourDlg, IDD_DLG_PAGE_FOUR, _T("页面4"));
YTABCTRL_FINISH();
3.在CTabCtrl控件的单击事件TCN_SELCHANGE中调用:
YTABCTRL_UPDATE();
4.如果有需要,可在父类中判当前页面是哪个页面,然后调用页面类的函数:
if( YTABCTRL_IS_PAGE(CPageTwoDlg) ) {
CPageTwoDlg *pDlg = (CPageTwoDlg*)YTABCTRL_CURRENT_PAGE();
AfxMessageBox(pDlg->Test());
}
5.YTableCtrl.h文件源码
#ifndef YY_TABCTRL_INCLUDE_H__
#define YY_TABCTRL_INCLUDE_H__
#pragma once
#include <vector>
using namespace std;
/**
* MFC CTabCtrl 使用方法的简化宏定义
* @author YaoYuan
*/
// 成员变量与函数成员声明
#define YTABCTRL_DECLEAR() \
typedef vector<pair<CDialog*, CString>> MAP_PAGE; MAP_PAGE __m_mapPages; \
int __m_nCurSelTab; CTabCtrl *__m_pTabCtrl; bool __m_bShowTitle; CDialog* __m_pDlgTemp;
// 变量初始化:传入CTabCtrl的ID
#define YTABCTRL_INIT(id) \
__m_nCurSelTab = 0; __m_bShowTitle = true; __m_pDlgTemp=nullptr; __m_pTabCtrl = (CTabCtrl*)GetDlgItem(id);
// 变量初始化:传入CTabCtrl控件变量指针
#define YTABCTRL_INIT_FROM_VAR(pTabCtrl) \
__m_nCurSelTab = 0; __m_bShowTitle = true; __m_pTabCtrl = pTabCtrl;
// 不显示TAB控件的标签,需在YTABCTRL_FINISH之前调用
#define YTABCTRL_NO_TAG() \
__m_bShowTitle = false;
// 释放页面指针
#define YTABCTRL_RELEASE() \
for( MAP_PAGE::iterator iter_ = __m_mapPages.begin(); iter_ != __m_mapPages.end(); ++iter_ ) \
{ delete iter_->first; }
// 添加TAB页面
#ifdef AFX_DESIGN_TIME
#define YTABCTRL_ADD_PAGE(order, cls, title) \
__m_pDlgTemp = new cls; __m_pDlgTemp->Create(cls::IDD, &__m_pTabCtrl); __m_mapPages.push_back(std::make_pair(__m_pDlgTemp, title); __m_pDlgTemp=nullptr;
#else
#define YTABCTRL_ADD_PAGE(cls, id, title) \
__m_pDlgTemp = new cls; __m_pDlgTemp->Create(id, __m_pTabCtrl); __m_mapPages.push_back(std::make_pair( __m_pDlgTemp, title)); __m_pDlgTemp=nullptr;
#endif
// 设置TAB页面
#define YTABCTRL_FINISH() \
CRect rcTab_; __m_pTabCtrl->GetClientRect(rcTab_); \
if(__m_bShowTitle) rcTab_.top += 22; else rcTab_.top += 0; rcTab_.bottom -= 2; rcTab_.left += 1; rcTab_.right -= 3; \
for( MAP_PAGE::iterator iter_ = __m_mapPages.begin(); iter_ != __m_mapPages.end(); ++iter_ ) \
{ iter_->first->MoveWindow(&rcTab_); iter_->first->ShowWindow(SW_HIDE); __m_pTabCtrl->InsertItem(__m_pTabCtrl->GetItemCount(), iter_->second); } \
if(__m_mapPages.size()>0 && __m_nCurSelTab<(int)__m_mapPages.size()) __m_mapPages.at(__m_nCurSelTab).first->ShowWindow(SW_SHOW);
// 单击TabCtrl时更新页面
#define YTABCTRL_UPDATE() \
int nCurSelTab_ = __m_pTabCtrl->GetCurSel(); \
__m_mapPages.at(__m_nCurSelTab).first->ShowWindow(SW_HIDE); \
__m_nCurSelTab = nCurSelTab_; \
__m_mapPages.at(__m_nCurSelTab).first->ShowWindow(SW_SHOW);
// 显示指定的页面
#define YTABCTRL_SHOW(index) \
if(index<0 || index>__m_mapPages.size()) return ; \
int nCurSelTab_ = index; \
__m_mapPages.at(__m_nCurSelTab).first->ShowWindow(SW_HIDE); \
__m_nCurSelTab = nCurSelTab_; \
__m_mapPages.at(__m_nCurSelTab).first->ShowWindow(SW_SHOW);
// RTTI:判断当前页面是否是给定的对话框类的实例
#define YTABCTRL_IS_PAGE(cls) (__m_nCurSelTab>=0 ? ((typeid(*__m_mapPages.at(__m_nCurSelTab).first) == typeid(cls)) ? true : false) : false)
// 获取当前页面
#define YTABCTRL_CURRENT_PAGE() (__m_nCurSelTab>=0 ? __m_mapPages.at(__m_nCurSelTab).first : nullptr)
#endif
例子下载链接:https://download.csdn.net/download/yaoyuanyylyy/10460932
例子是早期版本,没有YTABCTRL_RELEASE宏定义