VC++ CTabCtrl控件自定义宏使用例子

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"文件后,直接继承该类即可。
继承YTabCtrl类
其次,在对话框的OnInitDialog方法中添加子页面。
初始化
最后,在CTabCtrl的TCN_SELCHANGE单击后的事件中更新子页面。
更新子页面

CTabCtrl的嵌套

如果有嵌套使用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控件例子

使用步骤如下,简称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宏定义

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值