在VC中使用TabCtrl

    技术2022-05-20  42

    在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);     } }


    最新回复(0)