MFC模仿vc6.0梯形标签控件

来源:互联网 发布:浏览器 for mac 编辑:程序博客网 时间:2024/06/04 21:08

最终效果如图:
这里写图片描述

功能:
支持插入,删除,设置标签项

详细说明:

一:使用MFC应用程序向导,创建一个工程名为“tb”的基于对话框程序,用于演示通过创建自定义子窗口来开发一个标签控件的过程

二:在类视图中添加CWnd派生类,由空白窗口开发出一个标签控件,如果1-1所示
图1-1

三:在建立好的自定义类头文件中(VcTab.h)中,添加一些成员变量和函数

#include <Afxtempl.h>class CVcTab : public CWnd{// Constructionpublic:    struct SItem    {        CString szText; //每项文字        int nWidth;     //每项宽度        int nImage;     //每项图像索引    };    struct SLader       //标签梯形的四个点    {        POINT m_TopLeft;        POINT m_BottomLeft;        POINT m_BottomRight;        POINT m_TopRight;    };    enum{POINT_CNT=4};          //标签形状点的个数    CArray<SItem,SItem> m_arr;  //标签项的数据集合    CArray<SLader,SLader> m_lader;  //标签项的梯形坐标    int m_nCurSel,m_nTrack;     //选择状态和跟踪状态的项索引    CFont m_font;               //文字字体    CFont m_fontSel;            //选中标签字体    static const CPen m_pen;    //选择状态的边框    static const COLORREF m_clNormal,m_clSel,m_clTrack;//三态标签项的背景色    static const COLORREF m_clText;     //所有标签文本的颜色    POINT m_pt[POINT_CNT];              //选择的标签的梯形点    CRect m_rtSel;                      //选择的标签的矩形(以上边两点为边的大矩形)    CString m_szSel;                    //选择的标签的文字public:    int GetTextWidth(CString szText);   //根据文字和字体计算每项宽度    void GetFreeLab(CRect& rect);       //获取Tab控件的的不包含标签项的剩余控件    int GetCurSel()const    {        return m_nCurSel;    }    //获取标签总数    int GetItemCount()const    {        return m_arr.GetSize();    }    //获取标签文本    CString GetItemText(int nItem)const    {        ASSERT(nItem<m_arr.GetSize());        return m_arr[nItem].szText;    }    //设置标签文本    void SetItemText(int nItem,CString szText)    {        ASSERT(nItem<m_arr.GetSize()&&nItem>=0);        m_arr[nItem].szText=szText;    }    //删除标签项    void DeleteItem(int nItem)    {        ASSERT(nItem < m_arr.GetSize()&&nItem>=0);        m_arr.RemoveAt(nItem);    }    //获取标签项的梯形点坐标    void GetLaderPoint(int nItem,SLader& slader)    {        ASSERT(nItem<m_lader.GetSize()&&nItem>=0);        slader=m_lader[nItem];    }    BOOL InsertItem(int nItem,LPCTSTR szItem);    BOOL Create(DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID);    void Register();    .............    }

四:在源文件中,编写注册和创建两个成员函数的代码

#define VC_TAB _T("__VC_TAB__")         //自定义窗口类名void CVcTab::Register(){    WNDCLASS wc={0};    wc.style=CS_HREDRAW|CS_VREDRAW;    wc.cbClsExtra=0;    wc.cbWndExtra=0;    wc.lpfnWndProc=::DefWindowProc;    wc.hInstance=AfxGetInstanceHandle();    wc.hIcon=NULL;    wc.hCursor=AfxGetApp()->LoadStandardCursor(IDC_ARROW);    wc.hbrBackground=::GetSysColorBrush(COLOR_BTNSHADOW);    wc.lpszClassName=VC_TAB;    wc.lpszMenuName=NULL;    AfxRegisterClass(&wc);}BOOL CVcTab::Create(DWORD dwStyle, const RECT &rect, CWnd *pParentWnd, UINT nID){    static BOOL b=TRUE;    if(b)    {        //只执行一次        b=FALSE;        Register();    }    CFont* pFont=pParentWnd->GetFont();    if(!pFont)    {        HFONT hFont=(HFONT)::GetStockObject(DEFAULT_GUI_FONT);        pFont=CFont::FromHandle(hFont);    }    //从父窗口复制字体,如果获取失败则从系统字体复制,    //设置选中标签的字体    LOGFONT lf;    pFont->GetLogFont(&lf);    m_font.CreateFontIndirect(&lf);    lf.lfWeight=700;    m_fontSel.CreateFontIndirect(&lf);    return CWnd::Create(VC_TAB,NULL,dwStyle,rect,pParentWnd,nID);}

五:在源文件中继续编写插入标签项的函数

int CVcTab::GetTextWidth(CString szText)    {    CClientDC dc(this);    dc.SelectObject(&m_font);    CSize size=dc.GetTextExtent(szText);    return size.cx;}BOOL CVcTab::InsertItem(int nItem, LPCTSTR szItem){    SItem item;    item.szText=szItem;    item.nImage=-1;//保留    item.nWidth=GetTextWidth(szItem)+15;    m_arr.InsertAt(nItem,item);    return TRUE;}

六:在类视图中添加一些窗口显示与鼠标操作相关的消息映射函数
这里写图片描述

七:修改以上建立的所有消息映射函数的代码

#define DISTWIDTH   10                  //标签梯形的上边和下边差的1/2#define IDC_NEWLAB      2010            //修改标签项文本时创建的编辑框控件IDconst COLORREF CVcTab::m_clNormal=GetSysColor(COLOR_BTNFACE),CVcTab::m_clSel=GetSysColor(COLOR_WINDOW),CVcTab::m_clTrack=GetSysColor(COLOR_GRADIENTINACTIVECAPTION),CVcTab::m_clText=RGB(0,0,0);const CPen CVcTab::m_pen(0,1,RGB(0,0,0));       //标签边框画笔CVcTab::CVcTab(){    m_nTrack=-1;    m_nCurSel=0;}BOOL CVcTab::OnEraseBkgnd(CDC* pDC) {    //去除背景绘图可以减少闪烁    return TRUE;}void CVcTab::OnPaint() {    CPaintDC dc(this); // device context for painting    // TODO: Add your message handler code here    dc.SelectObject(m_font);    dc.SetBkMode(TRANSPARENT);    dc.SelectObject(m_pen);    dc.SetTextColor(m_clText);    CString szText;    CRect rect,rc;    GetClientRect(rc);    rect=rc;    rect.right=rect.left+DISTWIDTH;    int i=-1,nCount=GetItemCount(),nSel=GetCurSel();    while(++i<nCount)    {        rect.left=rect.right-DISTWIDTH;        rect.right=rect.right+m_arr[i].nWidth+DISTWIDTH;        //获取标签项梯形的四个点坐标        POINT pt[4]={            {rect.left,rect.top},                           {rect.left+DISTWIDTH,rect.bottom},            {rect.right-DISTWIDTH,rect.bottom},            {rect.right,rect.top}                           };          //保存标签项图形的四个点坐标        SLader slader;        slader.m_TopLeft=pt[0];        slader.m_BottomLeft=pt[1];        slader.m_BottomRight=pt[2];        slader.m_TopRight=pt[3];        m_lader.InsertAt(i,slader);        szText=GetItemText(i);        if(nSel!=i)        {            //跟踪态(加量)和正常态            CBrush br(i==m_nTrack?m_clTrack:m_clNormal);            CBrush* pOldBrush=dc.SelectObject(&br);            dc.Polygon(pt,sizeof(pt)/sizeof(pt[0]));            dc.DrawText(szText,szText.GetLength(),rect,                DT_SINGLELINE|DT_VCENTER|DT_CENTER);            dc.SelectObject(pOldBrush);            br.DeleteObject();        }        else        {            //选择态            CBrush br(m_clSel);            CBrush* pOldBr=dc.SelectObject(&br);            CFont* pOldFont=dc.SelectObject(&m_fontSel);            dc.Polygon(pt,sizeof(pt)/sizeof(pt[0]));            dc.DrawText(szText,szText.GetLength(),rect,                DT_SINGLELINE|DT_VCENTER|DT_CENTER);            dc.SelectObject(pOldFont);            dc.SelectObject(pOldBr);            for(int j=0;j<POINT_CNT;j++)                m_pt[j]=pt[j];            m_rtSel=rect;            m_szSel=szText;        }        rect.top=rc.top;        rect.bottom=rc.bottom;    }    //用正常色填充剩余控件部分的控件    rect.left=rect.right;    rect.right=rc.right;    dc.FillSolidRect(rect,RGB(200,200,200));    //重新绘制选中标签项,使梯形覆盖左右的梯形    CBrush br(m_clSel);    CBrush* pOldBr=(CBrush*)dc.SelectObject(&br);    CFont* pOldFont=(CFont*)dc.SelectObject(&m_fontSel);    dc.Polygon(m_pt,sizeof(m_pt)/sizeof(m_pt[0]));    dc.DrawText(m_szSel,m_szSel.GetLength(),m_rtSel,        DT_SINGLELINE|DT_VCENTER|DT_CENTER);    dc.SelectObject(pOldFont);    dc.SelectObject(pOldBr);}void CVcTab::OnMouseMove(UINT nFlags, CPoint point) {    // TODO: Add your message handler code here and/or call default    CWnd::OnMouseMove(nFlags, point);    CRect rect;    GetClientRect(rect);    if(!rect.PtInRect(point))    {        ReleaseCapture();        return;    }    if(GetCapture()-this)        SetCapture();    int i=-1,nCount=GetItemCount();    rect.right=rect.left+DISTWIDTH;    while(++i<nCount)    {        //循环测试每个标签项的空间        rect.left=rect.right-DISTWIDTH;        rect.right+=m_arr[i].nWidth+DISTWIDTH;        if(rect.PtInRect(point))        {            //若光标在某个标签区域内移动,则设置为加量标签            if(m_nTrack!=i)            {                m_nTrack=i;                Invalidate(FALSE);            }            return;        }    }}void CVcTab::OnLButtonDown(UINT nFlags, CPoint point) {    // TODO: Add your message handler code here and/or call default    CRect rect;    GetClientRect(rect);    rect.right=rect.left+DISTWIDTH;    int i=-1,nCount=GetItemCount();    BOOL flag=TRUE;     //如果点击位置不再任何标签项内,=TRUE;    while(++i<nCount)    {        //循环测试每个标签项的空间        rect.left=rect.right-DISTWIDTH;        rect.right+=m_arr[i].nWidth+DISTWIDTH;        if(rect.PtInRect(point))        {            flag=FALSE;            //若单击某个标签区域内,则设置为选择标签            m_nCurSel=i;            Invalidate(FALSE);            //模拟发送CTabCtrl的TCN_SELCHANGE反射型消息            NMHDR hdr={m_hWnd,GetDlgCtrlID(),TCN_SELCHANGE};            GetParent()->SendMessage(WM_NOTIFY,(WPARAM)m_hWnd,(LPARAM)&hdr);            return;        }    }    //项父窗口发送消息    GetClientRect(&rect);    if(flag&&rect.PtInRect(point))    {        GetParent()->SendMessage(WM_LBUTTONDOWN,(WPARAM)nFlags,(LPARAM)MAKELONG((WORD)point.x,(WORD)point.y));    }    CWnd::OnLButtonDown(nFlags, point);}

八:在主对话框的源文件(tbDlg.h)中,添加一些成员变量和函数

public:    enum{IDC_TAB=1234};         //控件ID    CVcTab m_tab;               //控件类对象public:    //用于作为TCN_SELCHANGE的消息反射函数    void OnSelChangeTab(NMHDR* pNMHDR,LRESULT* pResult);

九:在主对话框的源文件中,编写TCN_SELCHANGE的消息反射函数

void CTbDlg::OnSelChangeTab(NMHDR *pNMHDR, LRESULT *pResult){    int nSel=m_tab.GetCurSel();    CString str;    str.Format("第%d页",nSel+1);    str+=m_tab.GetItemText(nSel);    SetWindowText(str);    *pResult=0;}

十:添加消息映射代码,建立控件消息和函数直接的关联

BEGIN_MESSAGE_MAP(CTbDlg, CDialog)    ON_NOTIFY(TCN_SELCHANGE,IDC_TAB,OnSelChangeTab)    //{{AFX_MSG_MAP(CTbDlg)    ON_WM_PAINT()    ON_WM_QUERYDRAGICON()    ON_WM_KEYDOWN()    ON_WM_LBUTTONDOWN()    //}}AFX_MSG_MAPEND_MESSAGE_MAP()

十一:在资源文件中加入一个新的对话框IDD_DIALGO1,去掉新建对话框的所有默认控件,为新建对话框添加一个新类CEditDlg,在新对话框类的头文件中加入一些变量

class CEditDlg : public CDialog{// Constructionpublic:    CString m_str;      //自定义标签控件标签项文本    CRect m_rtDlg;      //自定义标签窗口大小    BOOL m_bType;       //弹出该对话框是添加标签项还是修改标签项    ........}

十二:修改CEditDlg对话框的初始化函数

BOOL CEditDlg::OnInitDialog() {    CDialog::OnInitDialog();    //移动弹出模态对话框的位置,使其在要修改的标签项的表面    this->MoveWindow(m_rtDlg);    return TRUE;  }

十三:在新建的对话中添加一个按钮和EditCtrl控件,修改按钮的ID为IDOK,双击按钮建立OnOk()按钮消息相应函数后,删除按钮控件,修改对话框界面如下
这里写图片描述

十四:修改IDD_DIALOG1的按钮消息相应函数,使的当按下回车键后,关闭对话框的时候将对话框内的文本传回父窗口

CEditDlg::CEditDlg(CWnd* pParent /*=NULL*/)    : CDialog(CEditDlg::IDD, pParent){    m_bType=0;    //{{AFX_DATA_INIT(CEditDlg)        // NOTE: the ClassWizard will add member initialization here    //}}AFX_DATA_INIT}void CEditDlg::OnOK() {    // TODO: Add extra validation here    CTbDlg* pParent=(CTbDlg*)GetParent();    CString str;    this->GetDlgItemText(IDC_EDIT,str);    if(str.IsEmpty())    {        CDialog::OnOK();        return;    }    switch(m_bType)    {    case 0://修改标签项        {            int nItem=pParent->m_tab.GetCurSel();            pParent->m_tab.SetItemText(nItem,str);            pParent->m_tab.Invalidate(FALSE);            CDialog::OnOK();            return;        }    case 1://添加标签项        {            int nCount=pParent->m_tab.GetItemCount();            pParent->m_tab.InsertItem(nCount,str);            pParent->m_tab.Invalidate(FALSE);            CDialog::OnOK();            return;        }    default:        {            CDialog::OnOK();            return;        }    }}

相关提示:父窗口与子窗口之间的数据传递可参考以下链接
enter description here

十五:在主对话中添加一些与鼠标和键盘消息相关的消息相应函数
图1-3

十六:修改主对话的消息相应函数,按下回车键的时候,系统会自动调用主对话框的OnOk()函数

void CTbDlg::OnOK() {    // TODO: Add extra validation here    CEditDlg dlg;    CRect rc=m_tab.m_rtSel;    ClientToScreen(&rc);    dlg.m_bType=0;    dlg.m_rtDlg=rc;    dlg.DoModal();//  CDialog::OnOK();}void CTbDlg::OnLButtonDown(UINT nFlags, CPoint point) {    // TODO: Add your message handler code here and/or call default//  AfxMessageBox("Test");    CVcTab* pWnd=(CVcTab*)GetDlgItem(IDC_TAB);    CRect rect,rc;    pWnd->GetClientRect(&rect);    if(rect.PtInRect(point))    {        CEditDlg dlg;        dlg.m_bType=1;        m_tab.GetFreeLab(rc);        m_tab.GetClientRect(&rect);        if(rc.left>=rect.right)            return;        ClientToScreen(&rc);        dlg.m_rtDlg=rc;        dlg.DoModal();    }    CDialog::OnLButtonDown(nFlags, point);}

十七:修改主对话框的初始化函数,调用CVcTab::Create函数创建自定义标签控件

BOOL CTbDlg::OnInitDialog(){    CDialog::OnInitDialog();    m_tab.Create(WS_VISIBLE,CRect(0,0,500,30),this,IDC_TAB);    m_tab.InsertItem(0,_T("Build"));    m_tab.InsertItem(1,_T("Debug"));    m_tab.InsertItem(2,_T("Find in Files1"));    m_tab.InsertItem(3,_T("Find in Files2"));    // Set the icon for this dialog.  The framework does this automatically    //  when the application's main window is not a dialog    SetIcon(m_hIcon, TRUE);         // Set big icon    SetIcon(m_hIcon, FALSE);        // Set small icon    // TODO: Add extra initialization here    return TRUE;  // return TRUE  unless you set the focus to a control}

以上只是工程的部分代码,完整代码请点以下链接下载
=enter description here