MFC模仿vc6.0梯形标签控件
来源:互联网 发布:浏览器 for mac 编辑:程序博客网 时间:2024/06/04 21:08
最终效果如图:
功能:
支持插入,删除,设置标签项
详细说明:
一:使用MFC应用程序向导,创建一个工程名为“tb”的基于对话框程序,用于演示通过创建自定义子窗口来开发一个标签控件的过程
二:在类视图中添加CWnd派生类,由空白窗口开发出一个标签控件,如果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
十五:在主对话中添加一些与鼠标和键盘消息相关的消息相应函数
十六:修改主对话的消息相应函数,按下回车键的时候,系统会自动调用主对话框的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
- MFC模仿vc6.0梯形标签控件
- VC6.0 MFC添加树形控件CTreeCtrl
- MFC 模仿编译器属性 设置控件
- MFC标签控件Tab
- VC6.0 MFC CFileDialog
- VC6.0+MFC HelloWorld
- CS秘密花园:梯形标签
- css实现梯形标签页
- mfc 模仿键盘按键向银行密码控件输入密码
- vc6.0 mfc createtimerqueqe出错
- vc6.0 MFC 添加控制台
- vc6.0 GDI+ MFC 配置
- VC6.0 MFC中WebBrowser控件禁止新窗口弹出的解决办法
- 32bits的xp下使用vc6.0生成MFC串口程序的控件拷贝目录
- VC6下使用MFC开发视频监控控件
- VC6下使用MFC开发视频监控控件
- VC6.0常用控件使用方法
- VC6.0 应用MSCOM控件
- RecyclerView里notifyItemRemoved的坑
- 从Java角度分析Python的入门介绍
- 快速入门shell脚本编写(四)
- 推荐6个微信小程序天气接口Api
- Session 的创建和销毁
- MFC模仿vc6.0梯形标签控件
- java读取excel实现 poi
- FFmpeg学习问题集合记录
- 推荐使用maven shade进行打包,assembly打包会出现若干问题
- lnmp1.4 一键安装包 搭建laravel 5.4 报http 500错误
- 通讯录右边的26个英文字母,点击同步
- HDU 1828 Picture
- 断言(ASSERT)的用法
- Log4j日志配置详解