CTabCtrl控件从零开始自绘
程序员文章站
2022-03-10 22:08:32
...
原理:每种控件都是一个窗口,建立CWnd 的派生类,在派生类的onpaint()函数中进行绘制5个标签,然后创建5个窗口,作为标签页的显示窗口。建立点击消息映射,通过点击不同的标签来显示不同的窗口
实例代码下载:点击打开链接
效果:
主要代码代码实现:
BOOL CCTabCtrl自绘Dlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
// 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动
// 执行此操作
SetIcon(m_hIcon, TRUE); // 设置大图标
SetIcon(m_hIcon, FALSE); // 设置小图标
CRect rect;
GetClientRect(&rect);
rect.bottom = 30;
//注册窗口类型名称
CString strClassName = AfxRegisterWndClass(CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS,
::LoadCursor(NULL, IDC_ARROW),
(HBRUSH) ::GetStockObject(WHITE_BRUSH),
::LoadIcon(NULL, IDI_APPLICATION));
//创建窗口
m_Tab.Create(strClassName,"zzcTab",WS_VISIBLE|WS_CHILD|WS_TABSTOP,rect,this,1234);
m_nImgList.Create(16,16,ILC_COLOR32|ILC_MASK,6,6);
for (int i = 0;i < 6;i++)
{
m_nImgList.Add(AfxGetApp()->LoadIcon(IDI_GOUWU+i));
}
m_Tab.SetImageList(&m_nImgList);
m_Tab.InsertItem(0,"娱乐",4);
m_Tab.InsertItem(1,"体育",5);
m_Tab.InsertItem(2,"视频",1);
m_Tab.InsertItem(3,"微博",2);
m_Tab.InsertItem(4,"购物",0);
//以下创建5个标签窗口
GetClientRect(&m_rcDlg);
m_rcDlg.top = rect.bottom + 0;
m_nDlg1.Create(IDD_DLG1,this);
m_nDlg2.Create(IDD_DLG2,this);
m_nDlg3.Create(IDD_DLG3,this);
m_nDlg4.Create(IDD_DLG4,this);
m_nDlg5.Create(IDD_DLG5,this);
m_nDlg1.MoveWindow(m_rcDlg.left,m_rcDlg.top,m_rcDlg.Width(),m_rcDlg.Height());
m_nDlg2.MoveWindow(m_rcDlg.left,m_rcDlg.top,m_rcDlg.Width(),m_rcDlg.Height());
m_nDlg3.MoveWindow(m_rcDlg.left,m_rcDlg.top,m_rcDlg.Width(),m_rcDlg.Height());
m_nDlg4.MoveWindow(m_rcDlg.left,m_rcDlg.top,m_rcDlg.Width(),m_rcDlg.Height());
m_nDlg5.MoveWindow(m_rcDlg.left,m_rcDlg.top,m_rcDlg.Width(),m_rcDlg.Height());
DisplayWindow(0,m_rcDlg);
return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
}
BEGIN_MESSAGE_MAP(CCTabCtrl自绘Dlg, CDialogEx)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_NOTIFY(TCN_SELCHANGE,1234,OnSelChangeTab)
END_MESSAGE_MAP()
void CCTabCtrl自绘Dlg::OnSelChangeTab(NMHDR* pnr,LRESULT* plt)
{
int nSel = m_Tab.GetCurSelItem();
CString strText;
strText.Format("当前选择的是第%d页:",nSel+1);
strText += m_Tab.GetItemText(nSel);
SetWindowText(strText);
DisplayWindow(nSel,m_rcDlg);
*plt = 0;
}
//显示特定的标签窗口
void CCTabCtrl自绘Dlg::DisplayWindow(int nIndex,CRect rc)
{
m_nDlg1.ShowWindow(SW_HIDE);
m_nDlg2.ShowWindow(SW_HIDE);
m_nDlg3.ShowWindow(SW_HIDE);
m_nDlg4.ShowWindow(SW_HIDE);
m_nDlg5.ShowWindow(SW_HIDE);
switch (nIndex)
{
case 0:
m_nDlg1.ShowWindow(SW_SHOWNORMAL);
break;
case 1:
m_nDlg2.ShowWindow(SW_SHOWNORMAL);
break;
case 2:
m_nDlg3.ShowWindow(SW_SHOWNORMAL);
break;
case 3:
m_nDlg4.ShowWindow(SW_SHOWNORMAL);
break;
case 4:
m_nDlg5.ShowWindow(SW_SHOWNORMAL);
break;
}
}
Cwnd派生类的实现:
#pragma once
// CMyTab
class CMyTab : public CWnd
{
DECLARE_DYNAMIC(CMyTab)
public:
struct sItemData //每一个标签页的数据
{
CString strText; //标签页文字
int nWidth; //标签页宽带
int nImgIndex; //标签页图像索引
sItemData()
{
strText.Empty();
nWidth = 0;
nImgIndex = -1;
}
};
CArray<sItemData,sItemData> m_ItemDataArr;//存放所有标签页的数据
int m_nCurSelItem; //当前选择的标签页索引
int m_nTrack; //设置标签的光标热点标志
void RegisterWnd(); //注册窗口
CImageList* m_pImageList;//图标列表
CImageList* SetImageList(CImageList* pImgList);//获取图标列表对象
int GetTextWidth(CString strText);//根据文字的内容,得到标签页的宽度
int GetCurSelItem()const //得到当前选择的标签页的索引
{
return m_nCurSelItem;
}
int GetItemCount()const //得到标签页的总数
{
return m_ItemDataArr.GetSize();
}
CString GetItemText(int nItem)const //得到某一项标签页上的文字
{
return m_ItemDataArr[nItem].strText;
}
void SetItemText(int nItem,CString strText)//设置某一项标签页上的内容
{
m_ItemDataArr[nItem].strText = strText;
}
BOOL InsertItem(int nItem,CString strText);//插入一个标签页
BOOL InsertItem(int nItem,CString strText,int nImageIndex);//插入一个带图标的标签页
public:
CMyTab();
virtual ~CMyTab();
protected:
DECLARE_MESSAGE_MAP()
public:
afx_msg void OnMouseMove(UINT nFlags, CPoint point);
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
afx_msg BOOL OnEraseBkgnd(CDC* pDC);
afx_msg void OnPaint();
};
.cpp文件
// MyTab.cpp : 实现文件
//
#include "stdafx.h"
#include "CTabCtrl自绘.h"
#include "MyTab.h"
// CMyTab
IMPLEMENT_DYNAMIC(CMyTab, CWnd)
CMyTab::CMyTab()
{
m_pImageList = NULL;
m_nCurSelItem = 0;
m_nTrack = -1;
}
CMyTab::~CMyTab()
{
}
BEGIN_MESSAGE_MAP(CMyTab, CWnd)
ON_WM_MOUSEMOVE()
ON_WM_LBUTTONDOWN()
ON_WM_ERASEBKGND()
ON_WM_PAINT()
END_MESSAGE_MAP()
// CMyTab 消息处理程序
int CMyTab::GetTextWidth(CString strText)//计算标签页文本的宽度
{
CClientDC dc(this);
CSize size = dc.GetTextExtent(strText);
return size.cx;
}
BOOL CMyTab::InsertItem(int nItem,CString strText)//插入一个不带图标的标签页
{
sItemData ItemData;
ItemData.strText = strText;
ItemData.nImgIndex = -1;
ItemData.nWidth = GetTextWidth(strText)+4;
m_ItemDataArr.InsertAt(nItem,ItemData);
return TRUE;
}
BOOL CMyTab::InsertItem(int nItem,CString strText,int nImageIndex)
{
sItemData ItemData;
ItemData.strText = strText;
ItemData.nImgIndex = nImageIndex;
ItemData.nWidth = GetTextWidth(strText)+16+6;
m_ItemDataArr.InsertAt(nItem,ItemData);
return TRUE;
}
void CMyTab::OnPaint()
{
CPaintDC dc(this);
dc.SetBkMode(TRANSPARENT);
CRect rcTotal,rcSingle;
GetClientRect(&rcTotal);
//对标签页进行逐项绘制
rcSingle = rcTotal;
rcSingle.right = rcSingle.left;
int i = 0,nCount = m_ItemDataArr.GetSize(),nSel = GetCurSelItem();
CString strText;
while (i < nCount)
{
if (m_ItemDataArr[i].nImgIndex == -1)//不带图标的绘制
{
rcSingle.left = rcSingle.right;//绘制时的循环移动过程
rcSingle.right += m_ItemDataArr[i].nWidth;
strText = GetItemText(i);
if (nSel != i)//非选中状态
{
dc.FillSolidRect(&rcSingle,i == m_nTrack?RGB(190,190,190):GetSysColor(GetSysColor(COLOR_WINDOWTEXT)));
dc.SetTextColor(GetSysColor(COLOR_WINDOWTEXT));
dc.DrawEdge(&rcSingle,BDR_RAISEDOUTER,BF_RECT);
dc.DrawText(strText,&rcSingle,DT_VCENTER|DT_SINGLELINE);
}
else//选中状态
{
dc.FillSolidRect(&rcSingle,GetSysColor(GetSysColor(8)));
dc.SetTextColor(RGB(100,0,0));
dc.DrawEdge(&rcSingle,BDR_SUNKENOUTER,BF_RECT);
dc.DrawText(strText,&rcSingle,DT_VCENTER|DT_SINGLELINE);
}
}
else//带图标的绘制
{
rcSingle.left = rcSingle.right;//绘制时的循环移动过程
rcSingle.right += m_ItemDataArr[i].nWidth;
strText = GetItemText(i);
if (nSel != i)//非选中状态
{
dc.FillSolidRect(&rcSingle,i == m_nTrack?RGB(190,190,190):GetSysColor(GetSysColor(COLOR_WINDOWTEXT)));
dc.SetTextColor(GetSysColor(COLOR_WINDOWTEXT));
dc.DrawEdge(&rcSingle,BDR_RAISEDOUTER,BF_RECT);
CRect rcIcon = rcSingle;
int nOffSet = (rcIcon.Height() - 16)/2;
rcIcon.top += nOffSet;
rcIcon.left += 2;
m_pImageList->Draw(&dc,m_ItemDataArr[i].nImgIndex,rcIcon.TopLeft(),ILD_TRANSPARENT);
CRect rcText = rcSingle;
rcText.left = rcSingle.left + 18;
dc.DrawText(strText,&rcText,DT_VCENTER|DT_SINGLELINE);
}
else//选中状态
{
dc.FillSolidRect(&rcSingle,GetSysColor(GetSysColor(8)));
dc.SetTextColor(RGB(100,0,0));
dc.DrawEdge(&rcSingle,BDR_SUNKENOUTER,BF_RECT);
CRect rcIcon = rcSingle;
int nOffSet = (rcIcon.Height() - 16)/2;
rcIcon.top += nOffSet;
rcIcon.left += 2;
m_pImageList->Draw(&dc,m_ItemDataArr[i].nImgIndex,rcIcon.TopLeft(),ILD_TRANSPARENT);
CRect rcText = rcSingle;
rcText.left = rcSingle.left + 18;
dc.DrawText(strText,&rcText,DT_VCENTER|DT_SINGLELINE);
}
}
i++;
}
}
void CMyTab::OnMouseMove(UINT nFlags, CPoint point)
{
CRect rc;
GetClientRect(&rc);
if (!rc.PtInRect(point))
{
m_nTrack = -1;
ReleaseCapture();
Invalidate(FALSE);
return;
}
if (GetCapture() != this)
{
SetCapture();
}
//TRACE("x=%d,y=%d\r\n",point.x,point.y);
int i = 0,nCount = m_ItemDataArr.GetSize();
rc.right = rc.left;
while (i < nCount)//循环测试光标和每个标签页的关系
{
rc.left = rc.right;
rc.right += m_ItemDataArr[i].nWidth;
if (rc.PtInRect(point))
{
if (m_nTrack != i)//光标在某个标签区域内,将此标签设置为加亮标签
{
m_nTrack = i;
Invalidate(FALSE);
}
return;
}else{
m_nTrack = -1;
Invalidate(FALSE);
}
i++;
}
CWnd::OnMouseMove(nFlags, point);
}
void CMyTab::OnLButtonDown(UINT nFlags, CPoint point)
{
CRect rc;
GetClientRect(&rc);
int i = 0,nCount = m_ItemDataArr.GetSize();
rc.right = rc.left;
while (i < nCount)//循环测试光标和每个标签页的关系
{
rc.left = rc.right;
rc.right += m_ItemDataArr[i].nWidth;
if (rc.PtInRect(point))//光标点击了某个标签页,设置其为选中状态
{
m_nCurSelItem = i;//设置为选择标签
Invalidate(FALSE);
//模拟发送CTabCtrl的TCN_SELCHANGE反射型消息
NMHDR nr;
nr.hwndFrom = m_hWnd;
nr.idFrom = GetDlgCtrlID();
nr.code = TCN_SELCHANGE;
GetParent()->SendMessage(WM_NOTIFY,(WPARAM)GetDlgCtrlID(),(LPARAM)&nr);
}
i++;
}
CWnd::OnLButtonDown(nFlags, point);
}
BOOL CMyTab::OnEraseBkgnd(CDC* pDC)//去除背景绘制,减少闪烁
{
return TRUE;
return CWnd::OnEraseBkgnd(pDC);
}
CImageList* CMyTab::SetImageList(CImageList* pImgList)
{
CImageList* pOldList = m_pImageList;
m_pImageList = pImgList;
return pOldList;
}