在VC中使用TabCtrl(TAB控件)

在VC中使用TabCtrl无疑是一件相当令人头疼的事情,而偏偏网上的相关资料又比较稀少,一些现成解决方案也多少存在一些问题,于是参考一些现成的TabCtrl类经过糅合修改成以下的一个类:CTabSheet
该类以vckbase的一篇文章《 在对话框中加入属性页》中提到的“方案五”的CTabSheet类为模板,并参考 CodeProject的CXTabCtrl、XPTabCtrl以及网上的一些其它资料修改而成。
该类具有以下特点:每个标签页都使用一个对话框以设计该页界面,可以随意设置禁用某页,可以设置隐藏TAB控件(用各页的对话框遮盖),可以随意设置选项卡的位置(顶部、底部、左边、右边),可以自由的添加或删除某页,可以为某个选项卡添加图标。

使用方法:
添加对话框资源,并且各个子对话框资源的属性应设置为:Style为Child,Border为None。为这些对话框建立类(直接从CDialog继承)如CPage1、CPage2……
在主对话框的类中添加成员变量:CPage1 m_Page1;、CPage2 m_Page2;……
在主对话框资源中,加入一个Tab Control,并且适当调整位置和大小。利用ClassWizard来为这个Tab Control创建一个CTabSheet的控件变量m_TabSheet。
在主对话框的OnInitDialog()加入:
m_TabSheet.AddPage("tab1", &m_page1, IDD_DIALOG1);
m_TabSheet.AddPage("tab2", &m_page2, IDD_DIALOG2);
……
如果要给标签加上图标,在AddPage之前设置好ImageList:
    //为TabCtrl控件添加图标
    m_imageList.Create(16, 16, ILC_COLOR32, 1, 1);
    CBitmap bitmap1,bitmap2;
    bitmap1.LoadBitmap(IDB_BITMAP1);
    bitmap2.LoadBitmap(IDB_BITMAP2);
    m_imageList.Add(&bitmap1, RGB(0,0,0));
    m_imageList.Add(&bitmap2, RGB(0,0,0));
    m_TabSheet.SetImageList(&m_imageList);
   
    //给TabCtrl添加页
    m_TabSheet.AddPage(_T("Page1"), &m_Page1, m_Page1.IDD, 0);
    m_TabSheet.AddPage(_T("第二页"), &m_Page2, m_Page2.IDD, 1);
很不可思议的是,我在测试中,如果在工程中没有把TabCtrl的标签设置成左边或右边的话,那么在运行时修改标签的位置为左边或右边时会出现问题,但是只要曾经设置过TabCtrl的标签为左边或右边后,以后运行不管工程中的TabCtrl的标签是怎样设置的,在运行时都可以正确的修改其标签的位置。
示例工程文件见我的网络硬盘:http://wooddoor.ys168.com/
以下是该CTabSheet类的源码:
//------------------------------TabSheet.h---------------------------------------///
  1. #if !defined(AFX_TABSHEET_H__42EE262D_D15F_46D5_8F26_28FD049E99F4__INCLUDED_)
  2. #define AFX_TABSHEET_H__42EE262D_D15F_46D5_8F26_28FD049E99F4__INCLUDED_
  3. #if _MSC_VER > 1000
  4. #pragma once
  5. #endif // _MSC_VER > 1000
  6. // TabSheet.h : header file
  7. //
  8. /
  9. // CTabSheet window
  10. class CTabSheet : public CTabCtrl
  11. {
  12. // Construction
  13. public:
  14.     CTabSheet();
  15.     virtual ~CTabSheet();
  16. // Attributes
  17. public:
  18. // Operations
  19. public:
  20. // Overrides
  21.     // ClassWizard generated virtual function overrides
  22.     //{{AFX_VIRTUAL(CTabSheet)
  23. protected:
  24.     virtual  void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
  25.     // override to draw text only; eg, colored text or different font
  26.     virtual  void OnDrawText(CDC& dc, CRect rc, CString sText, BOOL bDisabled, UINT format);
  27.     virtual void PreSubclassWindow();
  28.     //}}AFX_VIRTUAL
  29. // Implementation
  30. public:
  31.     int GetCurSel();
  32.     int SetCurSel(int nItem);
  33.     //void Show();
  34.     BOOL AddPage(LPCTSTR title, CDialog *pDialog, UINT ID, int nImage=-1);
  35.     void EnableTab(int iIndex, BOOL bEnable = TRUE);//Index从0开始计数
  36.     void EnableAllTabs(BOOL bEnable = TRUE);
  37.     void DeleteAllTabs();
  38.     void DeleteTab(int iIndex);
  39.     virtual BOOL IsTabEnabled(int iIndex);
  40.     BOOL HideTab(BOOL bHide = FALSE);
  41.     BOOL IsTabHided();
  42.     enum ITEMPOS{TOP,BOTTOM,LEFT,RIGHT};//设置选项卡的位置:顶、底、左、右
  43.     void SetItemPos(ITEMPOS nItemPos);//设置选项卡的位置:顶、底、左、右
  44. protected:
  45.     //void SetRect();
  46.     void SetRect(int iIndex);
  47.     // Generated message map functions
  48. protected:
  49.     CArray<BOOLBOOL> m_arrayStatusTab; //** enabled Y/N
  50.     BOOL m_bHideTab;
  51.     CStringArray m_Title;
  52.     CUIntArray m_IDD;
  53.     typedef CDialog* PDIALOG;
  54.     CArray<PDIALOG, PDIALOG> m_pPages;
  55.     int m_nCurrentPage;
  56.     //{{AFX_MSG(CTabSheet)
  57.     afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
  58.     //}}AFX_MSG
  59.     DECLARE_MESSAGE_MAP()
  60. };
  61. /
  62. //{{AFX_INSERT_LOCATION}}
  63. // Microsoft Visual C++ will insert additional declarations immediately before the previous line.
  64. #endif // !defined(AFX_TABSHEET_H__42EE262D_D15F_46D5_8F26_28FD049E99F4__INCLUDED_)

//------------------------------TabSheet.cpp---------------------------------------///
  1. // TabSheet.cpp : implementation file
  2. //
  3. #include "stdafx.h"
  4. #include "TabSheet.h"

  5. #ifdef _DEBUG
  6. #define new DEBUG_NEW
  7. #undef THIS_FILE
  8. static char THIS_FILE[] = __FILE__;
  9. #endif
  10. /
  11. // CTabSheet
  12. CTabSheet::CTabSheet()
  13. {
  14.     //m_nNumOfPages = 0;
  15.     m_nCurrentPage = 0;
  16.     m_bHideTab=FALSE;
  17. }
  18. CTabSheet::~CTabSheet()
  19. {
  20.     m_arrayStatusTab.RemoveAll();
  21.     m_pPages.RemoveAll();
  22.     m_IDD.RemoveAll();
  23.     m_Title.RemoveAll();
  24. }
  25. BEGIN_MESSAGE_MAP(CTabSheet, CTabCtrl)
  26.     //{{AFX_MSG_MAP(CTabSheet)
  27.     ON_WM_LBUTTONDOWN()
  28.     //}}AFX_MSG_MAP
  29. END_MESSAGE_MAP()
  30. /
  31. // CTabSheet message handlers
  32. //将字符串从半角转到全角(bDBC=FALSE)或从全角转到半角(bDBC=TRUE)
  33. CString strConv(const CString& strIn, BOOL bDBC /* = TRUE*/)
  34. {   
  35.     CString strTmp = strIn;   
  36.     LPTSTR szText;   
  37.     if(!strTmp.IsEmpty())
  38.     {   
  39.         szText = strTmp.GetBuffer(0);   
  40.         size_t nLen = _tcslen(szText);   
  41.         if(bDBC)
  42.         {   
  43.             for(size_t i=0;i<nLen;i++)
  44.             {   
  45.                 if(12288==szText[i])   
  46.                     szText[i] = 32;   
  47.                 else
  48.                 {   
  49.                     if(szText[i]>65280 && szText[i]<65375)   
  50.                         szText[i] -= 65248;   
  51.                 }   
  52.             }   
  53.         }   
  54.         else
  55.         {   
  56.             for(size_t i=0;i<nLen;i++)
  57.             {   
  58.                 if(32==szText[i])   
  59.                     szText[i] = 12288;   
  60.                 else
  61.                 {   
  62.                     if(szText[i]<127)   
  63.                         szText[i] += 65248;   
  64.                 }   
  65.             }   
  66.         }   
  67.         strTmp.ReleaseBuffer();   
  68.     }   
  69.     return strTmp;   
  70. }
  71. BOOL CTabSheet::AddPage(LPCTSTR title, CDialog *pDialog, UINT ID, int nImage)
  72. {
  73.     int iIndex = static_cast<int>( m_pPages.Add(pDialog) );
  74.     m_IDD.Add(ID);
  75.     //m_Title.Add(title);
  76.     m_Title.Add(strConv(title,FALSE));//将标签的标题字符串全部转化成全角字符,以便标签在左或右时标题能够竖直排列
  77.     m_pPages[iIndex]->Create( m_IDD[iIndex], this );
  78.     InsertItem( iIndex, m_Title[iIndex], nImage );
  79.     SetRect(iIndex);
  80.     m_pPages[iIndex]->ShowWindow(iIndex ? SW_HIDE : SW_SHOW);
  81.     //** the initial status is enabled
  82.     m_arrayStatusTab.Add(TRUE); 
  83.     return TRUE;
  84. }
  85. void CTabSheet::SetRect(int iIndex)
  86. {
  87.     ASSERT(iIndex < m_pPages.GetCount());
  88.     CRect tabRect, itemRect;
  89.     int nX, nY, nXc, nYc;//左、顶、宽、高
  90.     GetClientRect(&tabRect);//获取整个TAB控件的位置大小。
  91.     GetItemRect(0, &itemRect);//item指的只是标签页,因此这里是获得标签页的位置大小
  92.     if (m_bHideTab)
  93.     {
  94.         nX=tabRect.left;//使用tabRect.left、tabRect.top即可将各页显示的子对话框遮住TAB控件的标签。
  95.         nY=tabRect.top;
  96.         nXc=tabRect.Width();
  97.         nYc=tabRect.Height();
  98.     }
  99.     else
  100.     {
  101.         DWORD style = GetStyle();
  102. #define offset 2
  103.         if(style & TCS_VERTICAL)
  104.         {//选项卡在TAB控件的侧边
  105.             nY = tabRect.top + offset;
  106.             nYc = tabRect.bottom -nY - (offset + 1);
  107.             if (style & TCS_BOTTOM)
  108.             {//选项卡在右边
  109.                 nX = tabRect.left + offset;
  110.                 nXc = itemRect.left - nX - (offset + 1);
  111.             }
  112.             else
  113.             {//选项卡在左边
  114.                 nX = itemRect.right + offset;
  115.                 nXc = tabRect.right - nX - (offset + 1);
  116.             }
  117.         }
  118.         else
  119.         {//选项卡在TAB控件的顶部或底部
  120.             nX = tabRect.left + offset;
  121.             nXc = tabRect.right - nX - (offset + 1);
  122.             if (style & TCS_BOTTOM)
  123.             {//选项卡在底部
  124.                 nY = tabRect.top + offset;
  125.                 nYc = itemRect.top - nY - (offset + 1);
  126.             }
  127.             else
  128.             {//选项卡在顶部
  129.                 nY = itemRect.bottom + offset;
  130.                 nYc = tabRect.bottom - nY - (offset + 1);
  131.             }
  132.         }
  133.     }
  134.     m_pPages[iIndex]->MoveWindow(nX, nY, nXc, nYc);
  135. }
  136. void CTabSheet::OnLButtonDown(UINT nFlags, CPoint point) 
  137. {
  138.     CTabCtrl::OnLButtonDown(nFlags, point);
  139.     int selectedPage = GetCurFocus();
  140.     if (!m_arrayStatusTab[selectedPage])
  141.     {
  142.         CTabCtrl::SetCurSel(m_nCurrentPage);
  143.         return;
  144.     }
  145.     if(m_nCurrentPage != selectedPage)
  146.     {
  147.         m_pPages[m_nCurrentPage]->ShowWindow(SW_HIDE);
  148.         m_nCurrentPage=selectedPage;
  149.         m_pPages[m_nCurrentPage]->ShowWindow(SW_SHOW);
  150.     }
  151. }
  152. int CTabSheet::SetCurSel(int nItem)
  153. {
  154.     if( nItem < 0 || nItem >= m_pPages.GetCount())
  155.         return -1;
  156.     int ret = m_nCurrentPage;
  157.     if(m_nCurrentPage != nItem )
  158.     {
  159.         m_pPages[m_nCurrentPage]->ShowWindow(SW_HIDE);
  160.         m_nCurrentPage = nItem;
  161.         m_pPages[m_nCurrentPage]->ShowWindow(SW_SHOW);
  162.         CTabCtrl::SetCurSel(nItem);
  163.     }
  164.     return ret;
  165. }
  166. int CTabSheet::GetCurSel()
  167. {
  168.     return CTabCtrl::GetCurSel();
  169. }
  170. void CTabSheet::EnableTab(int iIndex, BOOL bEnable)
  171. {
  172.     ASSERT(iIndex < m_arrayStatusTab.GetSize());
  173.     //** if it should change the status ----
  174.     if (m_arrayStatusTab[iIndex] != bEnable)
  175.     {
  176.         m_arrayStatusTab[iIndex] = bEnable;
  177.         m_pPages[iIndex]->EnableWindow(bEnable);
  178.         //** redraw the item -------
  179.         CRect rect;
  180.         GetItemRect(iIndex, &rect);
  181.         InvalidateRect(rect);
  182.     }
  183. }
  184. void CTabSheet::EnableAllTabs(BOOL bEnable)
  185. {
  186.     for (int i=0; i<m_arrayStatusTab.GetCount(); i++)//CArray数组类的GetCount()与GetSize()是相同的,都是返回数组的元素个数。
  187.     {
  188.         EnableTab(i, bEnable);
  189.     }
  190. }
  191. void CTabSheet::DeleteAllTabs()
  192. {
  193.     m_arrayStatusTab.RemoveAll();
  194.     DeleteAllItems();
  195.     m_pPages.RemoveAll();
  196.     m_IDD.RemoveAll();
  197.     m_Title.RemoveAll();
  198. }
  199. void CTabSheet::DeleteTab(int iIndex)
  200. {
  201.     ASSERT(iIndex < m_pPages.GetCount());
  202.     m_arrayStatusTab.RemoveAt(iIndex);
  203.     DeleteItem(iIndex);
  204.     m_pPages.RemoveAt(iIndex);
  205.     m_IDD.RemoveAt(iIndex);
  206.     m_Title.RemoveAt(iIndex);
  207. }
  208. BOOL CTabSheet::IsTabEnabled(int iIndex)
  209. {
  210.     ASSERT(iIndex < m_pPages.GetCount());
  211.     return m_arrayStatusTab[iIndex];
  212. }
  213. //
  214. // Draw the tab: mimic SysTabControl32, except use gray if tab is disabled
  215. //
  216. void CTabSheet::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
  217. {
  218.     DRAWITEMSTRUCT& ds = *lpDrawItemStruct;
  219.     int iItem = ds.itemID;
  220.     // Get tab item info
  221.     TCHAR text[128];
  222.     TCITEM tci;
  223.     tci.mask = TCIF_TEXT|TCIF_IMAGE;
  224.     tci.pszText = text;
  225.     tci.cchTextMax = sizeof(text);
  226.     GetItem(iItem, &tci);
  227.     // use draw item DC
  228.     CDC dc;
  229.     dc.Attach(ds.hDC);
  230.     DWORD style = GetStyle();//取TAB控件的style,以判断是否设置有TCS_VERTICAL属性
  231.     //**  Draw the image
  232.     CRect rect = ds.rcItem;
  233.     rect.top += ::GetSystemMetrics(SM_CYEDGE);
  234.     dc.SetBkMode(TRANSPARENT);
  235.     //dc.FillSolidRect(rect, ::GetSysColor(COLOR_BTNFACE));
  236.     CImageList* pImageList = GetImageList();
  237.     if (pImageList && tci.iImage >= 0) 
  238.     {
  239.         //rect.left += dc.GetTextExtent(_T(" ")).cx;
  240.         rect.left += 2;
  241.         rect.top += 1;
  242.         IMAGEINFO info;
  243.         pImageList->GetImageInfo(tci.iImage, &info);
  244.         CRect ImageRect(info.rcImage);
  245.         pImageList->Draw(&dc, tci.iImage, CPoint(rect.left, rect.top), ILD_TRANSPARENT);
  246.         if(style & TCS_VERTICAL)
  247.         {//选项卡在TAB控件的侧边
  248.             rect.top += ImageRect.Height();
  249.         }
  250.         else
  251.         {//选项卡在TAB控件的顶部或底部
  252.             rect.left += ImageRect.Width();
  253.         }
  254.     }
  255. /*
  256.     typedef struct tagLOGFONT {
  257.         LONG lfHeight;                 // 高度
  258.         LONG lfWidth;                  // 宽度
  259.         LONG lfEscapement;             // 打印角度,单位是0.1度,900垂直打印,0水平打印
  260.         LONG lfOrientation;            // 字体打印角度,1800上下倒置,900左右倒置.
  261.         LONG lfWeight;                 // 字体粗细,默认是0,还常用400,700
  262.         BYTE lfItalic;                 // 斜体字,默认0非斜体,1斜体.
  263.         BYTE lfUnderline;              // 下划线,默认0无.
  264.         BYTE lfStrikeOut;              // 字体被直线穿过,默认0无.
  265.         BYTE lfCharSet;                // 字符集,如宋体字,一般设置为DEFAUL_CHARSET.
  266.         BYTE lfOutPrecision;           // 符合度,看不明白?一般设置为OUT_DEFAUL_PRECIS
  267.         BYTE lfClipPrecision;          // 不懂,一般设置为CLIP_DEAFAUL_PRECIS
  268.         BYTE lfQuality;                // 字体图形质量,不管,设为DEFAUL_QUALITY
  269.         BYTE lfPitchAndFamily;         // 字间距,不管,设为DEFAUL_PITCH+FF_DONTCARE
  270.         TCHAR lfFaceName[LF_FACESIZE]; // 所有字体式样数组,供字体回调函数调用
  271.     } LOGFONT, *PLOGFONT;*/
  272.     //Draw Text,必须设置使用LOGFONT,否则在切换选项卡的位置时选项卡的文字显示将出现问题。但是对于英文字符并不能置为竖排。
  273.     LOGFONT logFont;
  274.     memset(&logFont, 0, sizeof(LOGFONT));
  275.     //logFont.lfEscapement = 0;
  276.     //logFont.lfOrientation = 900;//如果注释掉此行,好像也没发现有何不同
  277.     //logFont.lfHeight = 16;//如果注释掉此行,好像也没发现有何不同
  278.     CFont  newFont;
  279.     newFont.CreateFontIndirect(&logFont);
  280.     CFont *pOldFont = dc.SelectObject(&newFont);
  281.     if(style & TCS_VERTICAL)
  282.     {//选项卡在TAB控件的侧边
  283.         OnDrawText(dc, rect, text, !IsTabEnabled(iItem), DT_WORDBREAK|DT_CENTER|DT_VCENTER);
  284.     }
  285.     else
  286.     {//选项卡在TAB控件的顶部或底部
  287.         OnDrawText(dc, rect, text, !IsTabEnabled(iItem), DT_SINGLELINE|DT_CENTER|DT_VCENTER);
  288.     }
  289.     dc.SelectObject(pOldFont);
  290.     dc.Detach();
  291. }
  292. void CTabSheet::PreSubclassWindow() 
  293. {
  294.     // TODO: Add your specialized code here and/or call the base class
  295.     CTabCtrl::PreSubclassWindow();
  296.     ModifyStyle(0, TCS_OWNERDRAWFIXED);
  297. }
  298. //
  299. // Draw tab text. You can override to use different color/font.
  300. //
  301. void CTabSheet::OnDrawText(CDC& dc, CRect rc, CString sText, BOOL bDisabled, UINT format)
  302. {
  303.     dc.SetTextColor(GetSysColor(bDisabled ? COLOR_3DHILIGHT : COLOR_BTNTEXT));
  304.     dc.DrawText(sText, &rc, format);
  305.     if (bDisabled)
  306.     {// disabled: draw again shifted northwest for shadow effect
  307.         rc += CPoint(-1,-1);
  308.         dc.SetTextColor(GetSysColor(COLOR_GRAYTEXT));
  309.         dc.DrawText(sText, &rc, format);
  310.     }
  311. }
  312. BOOL CTabSheet::HideTab(BOOL bHide)
  313. {
  314.     m_bHideTab = bHide;
  315.     for (int iIndex=0; iIndex<m_pPages.GetCount(); iIndex++)
  316.     {
  317.         SetRect(iIndex);
  318.     }
  319.     return m_bHideTab;
  320. }
  321. BOOL CTabSheet::IsTabHided()
  322. {
  323.     return m_bHideTab;
  324. }
  325. void CTabSheet::SetItemPos(ITEMPOS nItemPos)
  326. {
  327.     switch (nItemPos)
  328.     {
  329.     case TOP://Top
  330.         ModifyStyle(TCS_VERTICAL|TCS_BOTTOM, 0);
  331.         break;
  332.     case BOTTOM://Bottom
  333.         ModifyStyle(TCS_VERTICAL, TCS_BOTTOM);
  334.         break;
  335.     case LEFT://Left
  336.         ModifyStyle(TCS_BOTTOM, TCS_VERTICAL);
  337.         break;
  338.     case RIGHT://Right
  339.         ModifyStyle(0, TCS_BOTTOM|TCS_VERTICAL);
  340.         break;
  341.     default:
  342.         break;
  343.     };
  344.     for (int iIndex=0; iIndex<m_pPages.GetCount(); iIndex++)
  345.     {
  346.         SetRect(iIndex);
  347.     }
  348. }

评论 1 您还未登录,请先 登录 后发表或查看评论

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

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页

打赏作者

gnuljf

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

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

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

打赏作者

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

抵扣说明:

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

余额充值