您的位置:首页 > 其它

在VC中使用TabCtrl(TAB控件)

2008-10-09 12:08 375 查看
在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);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: