在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---------------------------------------///
#if !defined(AFX_TABSHEET_H__42EE262D_D15F_46D5_8F26_28FD049E99F4__INCLUDED_) #define AFX_TABSHEET_H__42EE262D_D15F_46D5_8F26_28FD049E99F4__INCLUDED_
#if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 // TabSheet.h : header file //
/ // CTabSheet window
class CTabSheet : public CTabCtrl { // Construction public: CTabSheet(); virtual ~CTabSheet();
// Attributes public:
// Operations public:
// Overrides // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CTabSheet) protected: virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct); // override to draw text only; eg, colored text or different font virtual void OnDrawText(CDC& dc, CRect rc, CString sText, BOOL bDisabled, UINT format); virtual void PreSubclassWindow(); //}}AFX_VIRTUAL
// Implementation public: int GetCurSel(); int SetCurSel(int nItem); //void Show(); BOOL AddPage(LPCTSTR title, CDialog *pDialog, UINT ID, int nImage=-1); void EnableTab(int iIndex, BOOL bEnable = TRUE);//Index从0开始计数 void EnableAllTabs(BOOL bEnable = TRUE); void DeleteAllTabs(); void DeleteTab(int iIndex); virtual BOOL IsTabEnabled(int iIndex); BOOL HideTab(BOOL bHide = FALSE); BOOL IsTabHided(); enum ITEMPOS{TOP,BOTTOM,LEFT,RIGHT};//设置选项卡的位置:顶、底、左、右 void SetItemPos(ITEMPOS nItemPos);//设置选项卡的位置:顶、底、左、右
protected: //void SetRect(); void SetRect(int iIndex);
// Generated message map functions protected: CArray<BOOL, BOOL> m_arrayStatusTab; //** enabled Y/N
BOOL m_bHideTab;
CStringArray m_Title; CUIntArray m_IDD; typedef CDialog* PDIALOG; CArray<PDIALOG, PDIALOG> m_pPages; int m_nCurrentPage; //{{AFX_MSG(CTabSheet) afx_msg void OnLButtonDown(UINT nFlags, CPoint point); //}}AFX_MSG
DECLARE_MESSAGE_MAP() };
/
//{{AFX_INSERT_LOCATION}} // Microsoft Visual C++ will insert additional declarations immediately before the previous line.
#endif // !defined(AFX_TABSHEET_H__42EE262D_D15F_46D5_8F26_28FD049E99F4__INCLUDED_)
//------------------------------TabSheet.cpp---------------------------------------///
// TabSheet.cpp : implementation file //
#include "stdafx.h" #include "TabSheet.h"
#ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif
/ // CTabSheet
CTabSheet::CTabSheet() { //m_nNumOfPages = 0; m_nCurrentPage = 0; m_bHideTab=FALSE; }
CTabSheet::~CTabSheet() { m_arrayStatusTab.RemoveAll(); m_pPages.RemoveAll(); m_IDD.RemoveAll(); m_Title.RemoveAll(); }
BEGIN_MESSAGE_MAP(CTabSheet, CTabCtrl) //{{AFX_MSG_MAP(CTabSheet) ON_WM_LBUTTONDOWN() //}}AFX_MSG_MAP END_MESSAGE_MAP()
/ // CTabSheet message handlers
//将字符串从半角转到全角(bDBC=FALSE)或从全角转到半角(bDBC=TRUE) CString strConv(const CString& strIn, BOOL bDBC /* = TRUE*/) { CString strTmp = strIn; LPTSTR szText; if(!strTmp.IsEmpty()) { szText = strTmp.GetBuffer(0); size_t nLen = _tcslen(szText); if(bDBC) { for(size_t i=0;i<nLen;i++) { if(12288==szText[i]) szText[i] = 32; else { if(szText[i]>65280 && szText[i]<65375) szText[i] -= 65248; } } } else { for(size_t i=0;i<nLen;i++) { if(32==szText[i]) szText[i] = 12288; else { if(szText[i]<127) szText[i] += 65248; } } } strTmp.ReleaseBuffer(); } return strTmp; }
BOOL CTabSheet::AddPage(LPCTSTR title, CDialog *pDialog, UINT ID, int nImage) { int iIndex = static_cast<int>( m_pPages.Add(pDialog) ); m_IDD.Add(ID); //m_Title.Add(title); m_Title.Add(strConv(title,FALSE));//将标签的标题字符串全部转化成全角字符,以便标签在左或右时标题能够竖直排列
m_pPages[iIndex]->Create( m_IDD[iIndex], this ); InsertItem( iIndex, m_Title[iIndex], nImage ); SetRect(iIndex); m_pPages[iIndex]->ShowWindow(iIndex ? SW_HIDE : SW_SHOW);
//** the initial status is enabled m_arrayStatusTab.Add(TRUE);
return TRUE; }
void CTabSheet::SetRect(int iIndex) { ASSERT(iIndex < m_pPages.GetCount());
CRect tabRect, itemRect; int nX, nY, nXc, nYc;//左、顶、宽、高 GetClientRect(&tabRect);//获取整个TAB控件的位置大小。 GetItemRect(0, &itemRect);//item指的只是标签页,因此这里是获得标签页的位置大小
if (m_bHideTab) { nX=tabRect.left;//使用tabRect.left、tabRect.top即可将各页显示的子对话框遮住TAB控件的标签。 nY=tabRect.top; nXc=tabRect.Width(); nYc=tabRect.Height(); } else { DWORD style = GetStyle(); #define offset 2 if(style & TCS_VERTICAL) {//选项卡在TAB控件的侧边 nY = tabRect.top + offset; nYc = tabRect.bottom -nY - (offset + 1); if (style & TCS_BOTTOM) {//选项卡在右边 nX = tabRect.left + offset; nXc = itemRect.left - nX - (offset + 1); } else {//选项卡在左边 nX = itemRect.right + offset; nXc = tabRect.right - nX - (offset + 1); } } else {//选项卡在TAB控件的顶部或底部 nX = tabRect.left + offset; nXc = tabRect.right - nX - (offset + 1); if (style & TCS_BOTTOM) {//选项卡在底部 nY = tabRect.top + offset; nYc = itemRect.top - nY - (offset + 1); } else {//选项卡在顶部 nY = itemRect.bottom + offset; nYc = tabRect.bottom - nY - (offset + 1); } } } m_pPages[iIndex]->MoveWindow(nX, nY, nXc, nYc); }
void CTabSheet::OnLButtonDown(UINT nFlags, CPoint point) { CTabCtrl::OnLButtonDown(nFlags, point);
int selectedPage = GetCurFocus(); if (!m_arrayStatusTab[selectedPage]) { CTabCtrl::SetCurSel(m_nCurrentPage); return; }
if(m_nCurrentPage != selectedPage) { m_pPages[m_nCurrentPage]->ShowWindow(SW_HIDE); m_nCurrentPage=selectedPage; m_pPages[m_nCurrentPage]->ShowWindow(SW_SHOW); } }
int CTabSheet::SetCurSel(int nItem) { if( nItem < 0 || nItem >= m_pPages.GetCount()) return -1;
int ret = m_nCurrentPage;
if(m_nCurrentPage != nItem ) { m_pPages[m_nCurrentPage]->ShowWindow(SW_HIDE); m_nCurrentPage = nItem; m_pPages[m_nCurrentPage]->ShowWindow(SW_SHOW); CTabCtrl::SetCurSel(nItem); }
return ret; }
int CTabSheet::GetCurSel() { return CTabCtrl::GetCurSel(); }
void CTabSheet::EnableTab(int iIndex, BOOL bEnable) { ASSERT(iIndex < m_arrayStatusTab.GetSize());
//** if it should change the status ---- if (m_arrayStatusTab[iIndex] != bEnable) { m_arrayStatusTab[iIndex] = bEnable; m_pPages[iIndex]->EnableWindow(bEnable);
//** redraw the item ------- CRect rect;
GetItemRect(iIndex, &rect); InvalidateRect(rect); } }
void CTabSheet::EnableAllTabs(BOOL bEnable) { for (int i=0; i<m_arrayStatusTab.GetCount(); i++)//CArray数组类的GetCount()与GetSize()是相同的,都是返回数组的元素个数。 { EnableTab(i, bEnable); } }
void CTabSheet::DeleteAllTabs() { m_arrayStatusTab.RemoveAll();
DeleteAllItems();
m_pPages.RemoveAll(); m_IDD.RemoveAll(); m_Title.RemoveAll();
}
void CTabSheet::DeleteTab(int iIndex) { ASSERT(iIndex < m_pPages.GetCount());
m_arrayStatusTab.RemoveAt(iIndex);
DeleteItem(iIndex);
m_pPages.RemoveAt(iIndex); m_IDD.RemoveAt(iIndex); m_Title.RemoveAt(iIndex);
}
BOOL CTabSheet::IsTabEnabled(int iIndex) { ASSERT(iIndex < m_pPages.GetCount());
return m_arrayStatusTab[iIndex]; }
// // Draw the tab: mimic SysTabControl32, except use gray if tab is disabled // void CTabSheet::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) { DRAWITEMSTRUCT& ds = *lpDrawItemStruct;
int iItem = ds.itemID;
// Get tab item info TCHAR text[128]; TCITEM tci; tci.mask = TCIF_TEXT|TCIF_IMAGE; tci.pszText = text; tci.cchTextMax = sizeof(text); GetItem(iItem, &tci);
// use draw item DC CDC dc; dc.Attach(ds.hDC);
DWORD style = GetStyle();//取TAB控件的style,以判断是否设置有TCS_VERTICAL属性
//** Draw the image CRect rect = ds.rcItem; rect.top += ::GetSystemMetrics(SM_CYEDGE); dc.SetBkMode(TRANSPARENT); //dc.FillSolidRect(rect, ::GetSysColor(COLOR_BTNFACE)); CImageList* pImageList = GetImageList(); if (pImageList && tci.iImage >= 0) { //rect.left += dc.GetTextExtent(_T(" ")).cx; rect.left += 2; rect.top += 1;
IMAGEINFO info; pImageList->GetImageInfo(tci.iImage, &info); CRect ImageRect(info.rcImage);
pImageList->Draw(&dc, tci.iImage, CPoint(rect.left, rect.top), ILD_TRANSPARENT);
if(style & TCS_VERTICAL) {//选项卡在TAB控件的侧边 rect.top += ImageRect.Height(); } else {//选项卡在TAB控件的顶部或底部 rect.left += ImageRect.Width(); } } /* typedef struct tagLOGFONT { LONG lfHeight; // 高度 LONG lfWidth; // 宽度 LONG lfEscapement; // 打印角度,单位是0.1度,900垂直打印,0水平打印 LONG lfOrientation; // 字体打印角度,1800上下倒置,900左右倒置. LONG lfWeight; // 字体粗细,默认是0,还常用400,700 BYTE lfItalic; // 斜体字,默认0非斜体,1斜体. BYTE lfUnderline; // 下划线,默认0无. BYTE lfStrikeOut; // 字体被直线穿过,默认0无. BYTE lfCharSet; // 字符集,如宋体字,一般设置为DEFAUL_CHARSET. BYTE lfOutPrecision; // 符合度,看不明白?一般设置为OUT_DEFAUL_PRECIS BYTE lfClipPrecision; // 不懂,一般设置为CLIP_DEAFAUL_PRECIS BYTE lfQuality; // 字体图形质量,不管,设为DEFAUL_QUALITY BYTE lfPitchAndFamily; // 字间距,不管,设为DEFAUL_PITCH+FF_DONTCARE TCHAR lfFaceName[LF_FACESIZE]; // 所有字体式样数组,供字体回调函数调用 } LOGFONT, *PLOGFONT;*/
//Draw Text,必须设置使用LOGFONT,否则在切换选项卡的位置时选项卡的文字显示将出现问题。但是对于英文字符并不能置为竖排。 LOGFONT logFont; memset(&logFont, 0, sizeof(LOGFONT)); //logFont.lfEscapement = 0; //logFont.lfOrientation = 900;//如果注释掉此行,好像也没发现有何不同 //logFont.lfHeight = 16;//如果注释掉此行,好像也没发现有何不同 CFont newFont; newFont.CreateFontIndirect(&logFont); CFont *pOldFont = dc.SelectObject(&newFont);
if(style & TCS_VERTICAL) {//选项卡在TAB控件的侧边 OnDrawText(dc, rect, text, !IsTabEnabled(iItem), DT_WORDBREAK|DT_CENTER|DT_VCENTER); } else {//选项卡在TAB控件的顶部或底部 OnDrawText(dc, rect, text, !IsTabEnabled(iItem), DT_SINGLELINE|DT_CENTER|DT_VCENTER); } dc.SelectObject(pOldFont);
dc.Detach(); }
void CTabSheet::PreSubclassWindow() { // TODO: Add your specialized code here and/or call the base class CTabCtrl::PreSubclassWindow(); ModifyStyle(0, TCS_OWNERDRAWFIXED); }
// // Draw tab text. You can override to use different color/font. // void CTabSheet::OnDrawText(CDC& dc, CRect rc, CString sText, BOOL bDisabled, UINT format) { dc.SetTextColor(GetSysColor(bDisabled ? COLOR_3DHILIGHT : COLOR_BTNTEXT)); dc.DrawText(sText, &rc, format);
if (bDisabled) {// disabled: draw again shifted northwest for shadow effect rc += CPoint(-1,-1); dc.SetTextColor(GetSysColor(COLOR_GRAYTEXT)); dc.DrawText(sText, &rc, format); } }
BOOL CTabSheet::HideTab(BOOL bHide) { m_bHideTab = bHide;
for (int iIndex=0; iIndex<m_pPages.GetCount(); iIndex++) { SetRect(iIndex); }
return m_bHideTab; }
BOOL CTabSheet::IsTabHided() { return m_bHideTab; }
void CTabSheet::SetItemPos(ITEMPOS nItemPos) { switch (nItemPos) { case TOP://Top ModifyStyle(TCS_VERTICAL|TCS_BOTTOM, 0); break; case BOTTOM://Bottom ModifyStyle(TCS_VERTICAL, TCS_BOTTOM); break; case LEFT://Left ModifyStyle(TCS_BOTTOM, TCS_VERTICAL); break; case RIGHT://Right ModifyStyle(0, TCS_BOTTOM|TCS_VERTICAL); break; default: break; }; for (int iIndex=0; iIndex<m_pPages.GetCount(); iIndex++) { SetRect(iIndex); } }