CTabCtrl控件从零开始自绘

8 篇文章 153 订阅 ¥9.90 ¥99.00
69 篇文章 4 订阅

原理:每种控件都是一个窗口,建立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;
}

 

 

 

 

 

 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 13
    评论
### 回答1: Ctabctrl是一个MFC框架下的控件,通常用于显示多个tab页,每一个tab页显示不同的内容。在实际开发中,由于多个tab页之间的内容不同,可能会出现一些需要重新绘制的情况。这时,我们就可以通过重新绘制ctabctrl来达到我们想要的效果。 在使用ctabctrl控件时,我们首先需要选择一个合适的绘制方式,并为tabctrl控件进行预处理。当我们需要对控件进行重绘时,可以先使用WM_SETREDRAW消息,将控件设置为不进行重绘状态,此时修改控件的属性并不会对界面上显示的内容产生影响。当需要恢复控件的重绘状态时,可以使用WM_SETREDRAW消息,将控件设置为可以重绘状态。 在进行重绘时,我们通常需要先擦除控件内容,并重新绘制。在绘制时,我们可以使用MFC框架下提供的CDC类和其他相关类,来进行绘制。在绘制时,需要考虑到控件的不同状态和不同信息,以达到最佳的绘制效果。最后,如果需要控制重绘的时间和频率,可以在控件初始化时设置相关的定时器和消息处理函数,来控制重绘的方式和间隔时间。 总之,ctabctrl控件的重绘是一个比较重要的开发问题,需要在具体使用时仔细研究,理解控件的特性和绘制方式,以达到理想的效果。 ### 回答2: ctabctrl 是一个MFC控件,在Windows窗口界面下使用极为广泛,主要功能是用于设置和维护多个选项卡,方便用户在多个选项间便捷切换。在使用 ctabctrl 的过程中,如果发现选项卡显示不正确,可能需要进行重绘操作。 CTabCtrl 重绘操作通常需要在如下三种情况下进行: 一、 当选项卡背景色与其他控件或窗口颜色不一致时,需要进行重绘操作,使得选项卡的背景色与其他控件或窗口颜色相统一,使得整个界面更加和谐。 二、 当选项卡中的文本或图标发生变化,或者选项卡的排列顺序发生改变时,需要进行重绘操作,使得选项卡显示的内容与实际操作结果保持一致。 三、 当选项卡中的控件需要刷新时,需要进行重绘操作,以便及时更新选项卡内的控件,保证用户操作的实时性。 CTabCtrl 重绘操作通常采用 Invalidate() 函数来触发,常见的调用方式有两种: 一、 在选项卡的 OnPaint() 函数中进行调用,并将需要刷新的区域作为参数传入。例如: void CMyTabCtrl::OnPaint() { CPaintDC dc(this); // 需要进行重绘的选项卡区域 CRect rect; GetClientRect(&rect); rect.DeflateRect(1,1); // 缩小矩形区域,保证视觉效果 m_myTabCtrl.InvalidateRect(&rect); } 二、 在选项卡的其他操作函数中(例如 OnSelChange())调用 Invalidate() 函数,以保证操作结果及时显示。 总之,CTabCtrl 重绘操作是 Windows 界面编程中的一个重要内容,需要程序员在开发中充分理解掌握其使用方法,以便打造更加完美的用户界面体验。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小米的修行之路

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值