MFC第二十四天 使用GDI对象画笔和画刷来开发控件(分页控件选择态的算法分析、使用CToolTipCtrl开发动静态提示)

文章目录

GDI对象画笔和画刷来开发控件

梯形边框的按钮控件

CMainDlg.h

cpp 复制代码
class CMainDlg : public CDialogEx
{
// 构造
	CLadderCtrl m_ladd;

protected:
	virtual BOOL OnInitDialog();
	afx_msg void OnPaint();
public:
	afx_msg void OnSize(UINT nType, int cx, int cy);
};

CMainDlg.cpp

cpp 复制代码
void CMainDlg::OnSize(UINT nType, int cx, int cy)
{
	CDialogEx::OnSize(nType, cx, cy);
	
	if (m_ladd)
	{
		m_ladd.SetWindowPos(NULL, 0, cy - 20, cx, cy, SWP_NOZORDER);
	}
}
cpp 复制代码
BOOL CMainDlg::OnInitDialog(){
	CDialogEx::OnInitDialog();
	SetIcon(m_hIcon, TRUE);			// 设置大图标
	SetIcon(m_hIcon, FALSE);		// 设置小图标
	CRect rect;
	GetClientRect(rect);
	rect.top = rect.bottom - 20;
	m_ladd.Create(WS_CHILD | WS_VISIBLE, rect, this,8888);
	m_ladd.InsertItem(0, _T("组建"));
	m_ladd.InsertItem(1, _T("调试"));
	m_ladd.InsertItem(2, _T("在文件1中查找"));
	m_ladd.InsertItem(3, _T("在文件2中查找"));
	m_ladd.InsertItem(4, _T("结果"));
	return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
}

CLadderCtrl.h

cpp 复制代码
class CLadderCtrl : public CWnd{
	DECLARE_DYNAMIC(CLadderCtrl)
	CStringArray m_ds;  //存储一组字符串 使用Add方法添加新的字符串,使用RemoveAt方法删除指定位置的字符串,使用GetAt方法获取特定位置的字符串等。
	int m_nIndex{}; 
	CBrush m_br, m_brSel;
	CFont m_font, m_fontSel;
public:
	CLadderCtrl();
	virtual ~CLadderCtrl();
	BOOL Create(DWORD dwStyle, CRect rect, CWnd* pParent, int nID);
	LONG InsertItem(int nItem, LPCTSTR lpszItem);
	int SetCurSel(int nItem);
	int GetCurSel() const;
	int GetItemCount() const;
	BOOL DeleteAllItems();	BOOL DeleteItem(int nItem);
};

CLadderCtrl.cpp

cpp 复制代码
CLadderCtrl::CLadderCtrl(){
	m_br.CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
	m_brSel.CreateSolidBrush(GetSysColor(COLOR_HIGHLIGHT));
	HFONT hfont = (HFONT)GetStockObject(DEFAULT_GUI_FONT); // GetStockObject 返回的是void* 要给转为具体类型
	LOGFONT lf;
	GetObject(hfont, sizeof(lf), &lf);
	m_font.CreateFontIndirect(&lf);
	lf.lfWeight = 700;
	m_fontSel.CreateFontIndirect(&lf);
}
CLadderCtrl::~CLadderCtrl()	{}
BOOL CLadderCtrl::Create(DWORD dwStyle, CRect rect, CWnd* pParent, int nID){
	return CWnd::Create(NULL, NULL, dwStyle, rect, pParent, nID);
}
cpp 复制代码
LONG CLadderCtrl::InsertItem(int nItem, LPCTSTR lpszItem)
{
	m_ds.InsertAt(nItem,lpszItem);
	return nItem;
}
int CLadderCtrl::SetCurSel(int nItem)
{
	return 0;
}
int CLadderCtrl::GetCurSel() const
{
	return 0;
}
cpp 复制代码
int CLadderCtrl::GetItemCount() const
{
	return (int)m_ds.GetCount();
}
BOOL CLadderCtrl::DeleteAllItems()
{
	m_ds.RemoveAll();
	return TRUE;
}
BOOL CLadderCtrl::DeleteItem(int nItem)
{
	m_ds.RemoveAt(nItem);
	return TRUE;
}
cpp 复制代码
void CLadderCtrl::OnPaint()
{
	CPaintDC dc(this); // device context for painting
	CRect rect;
	GetClientRect(rect);
	dc.SetBkMode(TRANSPARENT);
	dc.FillSolidRect(rect, GetSysColor(COLOR_INFOBK));

	int i = -1, nCount =(int) m_ds.GetCount();
 
	const int H = rect.Height(); //创建一个宽度为矩形高度的四分之一,高度为整个矩形高度的矩形rc。
	CRect rc{ H/4,0,0,rect.bottom };
	dc.SelectObject(&m_br);
	POINT ptSel[4];
	CString sSel;
	CRect rSel;

	dc.SelectObject(&m_font); //宋体9 就是默认字体

	/*HFONT hFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
	LOGFONT lf;
	GetObject(hFont, sizeof(lf), &lf);*/

	while (++i<nCount)
	{
		CString str = m_ds[i];

		CSize size = dc.GetTextExtent(str); //获取字符串str在设备上下文dc中的宽度和高度,并将结果存储在CSize对象size中
		rc.right = rc.left + size.cx +H; //左边界加上字符串宽度和一个常量H	 可以保证矩形rc的宽度至少能够容纳字符串,并且在右边留出一个宽度为常量H的空白区域
	
		POINT pts[] = { {rc.left-H/4,rc.top},{rc.right + H / 4,rc.top},{rc.right - H / 4,rc.bottom},{rc.left + H / 4,rc.bottom} };
		if (i!=m_nIndex)
		{
			dc.Polygon(pts, _countof(pts));
					//dc.Rectangle(rc);
			dc.DrawText(str, rc, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
		}
		else
		{
			memcpy(ptSel, pts, sizeof(pts));
			sSel = str;
			rSel = rc;
		}
		rc.left = rc.right;
	}
	dc.SelectObject(&m_brSel);

	dc.Polygon(ptSel, _countof(ptSel)); //要覆盖二次绘画
	dc.SelectObject(&m_fontSel);
	dc.DrawText(sSel, rSel, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
 }
//	dc.TextOut(0, 0, _T("测试系统颜色"));

void CLadderCtrl::OnLButtonDown(UINT nFlags, CPoint point)  //和上面算法一致 这个用来算出位置
{
	CWnd::OnLButtonDown(nFlags, point);
	CClientDC dc(this);
	dc.SelectStockObject(DEFAULT_GUI_FONT);
	int i = -1, nCount = (int)m_ds.GetCount();
	CRect rect;
	GetClientRect(rect);
	CRect rc{ 0,0,0,rect.bottom };

	while (++i<nCount)   
	{
		CString str = m_ds[i];

		CSize size = dc.GetTextExtent(str);
		rc.right = rc.left + size.cx + rect.Height();
		dc.SelectObject(i == m_nIndex ? &m_brSel : &m_br);
		if (rc.PtInRect(point))
		{
			if (i != m_nIndex)  //去除无效点击 
			{
				m_nIndex = i;
				Invalidate(FALSE);
			}
			break;
		}
		rc.left = rc.right;
	}
}

矩形边框的三态按钮控件 CToolTipCtrl开发动静态提示

CMainDlg.h

cpp 复制代码
#include "CLadderCtrl.h"
class CMainDlg : public CDialogEx
{
// 构造
	CLadderCtrl m_ladd;	
	CToolTipCtrl m_tip;
public:
	CMainDlg(CWnd* pParent = nullptr);	// 标准构造函数	
public:
	afx_msg void OnSize(UINT nType, int cx, int cy);
	virtual BOOL PreTranslateMessage(MSG* pMsg);
};

CMainDlg.cpp

cpp 复制代码
BOOL CMainDlg::OnInitDialog()
{
	CDialogEx::OnInitDialog();
	CRect rect;
	GetClientRect(rect);
	rect.top = rect.bottom - 20;
	m_ladd.Create(WS_CHILD | WS_VISIBLE, rect, this,8888);
	m_ladd.InsertItem(0, _T("组建"));
	m_ladd.InsertItem(1, _T("调试"));
	m_ladd.InsertItem(2, _T("在文件1中查找"));
	m_ladd.InsertItem(3, _T("在文件2中查找"));
	m_ladd.InsertItem(4, _T("结果"));
	m_tip.Create(this);
	m_tip.AddTool(&m_ladd, _T("没想到吧"));
	return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
}
cpp 复制代码
void CMainDlg::OnSize(UINT nType, int cx, int cy)
{
	CDialogEx::OnSize(nType, cx, cy);
	if (m_ladd)
		m_ladd.SetWindowPos(NULL, 0, cy - 20, cx, 20, SWP_NOZORDER);
}
BOOL CMainDlg::PreTranslateMessage(MSG* pMsg) //皇军的岗楼
{
	m_tip.RelayEvent(pMsg);
	return CDialogEx::PreTranslateMessage(pMsg);
}

CLadderCtrl.h

cpp 复制代码
class CLadderCtrl : public CWnd
{
	DECLARE_DYNAMIC(CLadderCtrl)
	CStringArray m_ds;
	int m_nIndex{};
	int m_nTrack{};
	CBrush m_br, m_brSel,m_brTrack; //三态按钮  三态控件  没有选中色(普通) 选中色 放在上面的框色(追踪态)
	CBrush m_brBack;
	CPen m_pen;
	int GetIndex(CPoint point);
public:
	CLadderCtrl();
	virtual ~CLadderCtrl();
	BOOL Create(DWORD dwStyle, CRect rect, CWnd* pParent, int nID);

	LONG InsertItem(int nItem, LPCTSTR lpszItem);
	int SetCurSel(int nItem);
	int GetCurSel() const;
	int GetItemCount() const;
	BOOL DeleteAllItems();
	BOOL DeleteItem(int nItem);
// 对话框数据
#ifdef AFX_DESIGN_TIME
	enum { IDD = IDD_MAIN_DIALOG };
#endif

protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持

	DECLARE_MESSAGE_MAP()
public:
	afx_msg void OnPaint();
	afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
	afx_msg void OnMouseMove(UINT nFlags, CPoint point);
};

CLadderCtrl.cpp: 实现文件

cpp 复制代码
#include "pch.h"
#include "CreatControl.h"
#include "afxdialogex.h"
#include "CLadderCtrl.h"
CLadderCtrl::CLadderCtrl()  //有对应的构造函数可以在定义时直接给予初始化{}
{
	m_br.CreateSolidBrush(RGB(64,86,140));
	m_brSel.CreateSolidBrush(RGB(230,245,255)); 
	m_brTrack.CreateSolidBrush(RGB(187, 194, 241));
	m_brBack.CreateSolidBrush(RGB(93, 107, 153)); //默认的背景
	m_pen.CreatePen(PS_SOLID, 1, RGB(93, 107, 153));
}
CLadderCtrl::~CLadderCtrl()
{
}
cpp 复制代码
BOOL CLadderCtrl::Create(DWORD dwStyle, CRect rect, CWnd* pParent, int nID)
{
	return CWnd::Create(NULL, NULL, dwStyle, rect, pParent, nID);
}
LONG CLadderCtrl::InsertItem(int nItem, LPCTSTR lpszItem)
{
	m_ds.InsertAt(nItem,lpszItem);
	return nItem;
}
int CLadderCtrl::SetCurSel(int nItem)
{
	ASSERT(nItem > -1 && nItem < m_ds.GetCount());
	m_nIndex = nItem;
	Invalidate(FALSE);
	return 0;
}
int CLadderCtrl::GetCurSel() const
{
	return m_nIndex;
}

int CLadderCtrl::GetItemCount() const
{
	
	return (int)m_ds.GetCount();
}

BOOL CLadderCtrl::DeleteAllItems()
{
	m_ds.RemoveAll();
	return TRUE;
}

BOOL CLadderCtrl::DeleteItem(int nItem)
{
	m_ds.RemoveAt(nItem);
	return TRUE;
}

void CLadderCtrl::DoDataExchange(CDataExchange* pDX)
{
	CWnd::DoDataExchange(pDX);
}


BEGIN_MESSAGE_MAP(CLadderCtrl, CWnd)
	ON_WM_PAINT()
	ON_WM_LBUTTONDOWN()
	ON_WM_MOUSEMOVE()
END_MESSAGE_MAP()
cpp 复制代码
void CLadderCtrl::OnPaint()
{
	CPaintDC dc(this); // device context for painting
	CRect rect;
	GetClientRect(rect);
	dc.SetBkMode(TRANSPARENT);
	dc.FillRect(rect,&m_brBack);

	int i = -1, nCount =(int) m_ds.GetCount();
	int nLeft = 0;
	CRect rc{ 0,0,0,rect.bottom };

	CFont* pFont = GetFont();
	dc.SelectStockObject(DEFAULT_GUI_FONT); //宋体9 就是默认字体
	dc.SelectObject(&m_pen);

	/*HFONT hFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
	LOGFONT lf;
	GetObject(hFont, sizeof(lf), &lf);*/

	while (++i<nCount)
	{
		CString str = m_ds[i];

		CSize size = dc.GetTextExtent(str);
		rc.right = rc.left + size.cx + rect.Height();
		if (i == m_nIndex)
		{
			dc.SelectObject(&m_brSel);
			dc.SetTextColor(GetSysColor(COLOR_WINDOWTEXT));
		}
		else if (i == m_nTrack)
		{
			dc.SelectObject(&m_brTrack);
			dc.SetTextColor(GetSysColor(COLOR_WINDOWTEXT));
		}
		else
		{
			dc.SetTextColor(GetSysColor(COLOR_HIGHLIGHTTEXT));
			dc.SelectObject(&m_br);
		}
		//dc.SelectObject(i == m_nIndex ? &m_brSel:&m_br); 

		dc.Rectangle(rc);
		dc.DrawText(str, rc, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
	 
		rc.left = rc.right;
	}

 }
//	dc.TextOut(0, 0, _T("测试系统颜色"));
cpp 复制代码
int CLadderCtrl::GetIndex(CPoint point)
{
	CClientDC dc(this);
	dc.SelectStockObject(DEFAULT_GUI_FONT);
	int i = -1, nCount = (int)m_ds.GetCount();
	CRect rect;
	GetClientRect(rect);
	CRect rc{ 0,0,0,rect.bottom };

	while (++i < nCount)
	{
		CString str = m_ds[i];

		CSize size = dc.GetTextExtent(str);
		rc.right = rc.left + size.cx + rect.Height();
		dc.SelectObject(i == m_nIndex ? &m_brSel : &m_br);
		if (rc.PtInRect(point))
		{
			return i;
		}
		rc.left = rc.right;
	}
	return -1;
}
cpp 复制代码
void CLadderCtrl::OnLButtonDown(UINT nFlags, CPoint point)  //和上面算法一致 这个用来算出位置
{
	int nIndex = GetIndex(point);
	if (nIndex != m_nIndex)  //去除无效点击 
	{
		m_nIndex = nIndex;
		Invalidate(FALSE);
	}

	CWnd::OnLButtonDown(nFlags, point);
}
cpp 复制代码
void CLadderCtrl::OnMouseMove(UINT nFlags, CPoint point)
{
	int nIndex = GetIndex(point);
	if (nIndex == m_nIndex)   
		return;
		
	if (m_nTrack!=nIndex)   
	{
		m_nTrack = nIndex;
		Invalidate(FALSE);
	}
	CWnd::OnMouseMove(nFlags, point);
}

矩形边框的三态按钮控件 CToolTipCtrl开发动态提示

本次只添加了与静态提示不同之处的代码

CMainDlg.h

cpp 复制代码
#include "CLadderCtrl.h"
class CMainDlg : public CDialogEx
{
// 构造
	CLadderCtrl m_ladd;	
	CToolTipCtrl m_tip;
public:
	CMainDlg(CWnd* pParent = nullptr);	// 标准构造函数
	
public:
	afx_msg void OnSize(UINT nType, int cx, int cy);
	virtual BOOL PreTranslateMessage(MSG* pMsg);
	afx_msg BOOL SetTipText(UINT id, NMHDR* pHdr, LRESULT* pResult);
	//反射型消息 手动创建的 一消息一函数一控件 一ID
	afx_msg BOOL OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message);
};

CMainDlg.cpp

设置动态文字的函数

cpp 复制代码
BOOL CMainDlg::SetTipText(UINT id, NMHDR* pHdr, LRESULT* pResult){
	int nIndex = m_ladd.GetTrack();
	if (nIndex < 0 || nIndex >= m_ladd.GetItemCount())
		return FALSE;
	TOOLTIPTEXT* pText = (TOOLTIPTEXT*)pHdr;
	if (pText->uFlags & TTF_IDISHWND){
		auto nID = (int)pHdr->idFrom;
		CString str = m_ladd.GetItemText(nIndex);
		_tcscpy_s(pText->szText, 80, str);
		return TRUE;
	}
	return FALSE;
	return 0;
}
cpp 复制代码
BOOL CMainDlg::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message){
	switch (pWnd->GetDlgCtrlID())	{
	case 8888:
	{
		static int nTrack = -1; // 用来看它是否切换 全局变量
		int n = m_ladd.GetTrack();
		if (nTrack !=n ){	 //切换算法
			nTrack = n;
			m_tip.Update();
		}
	}
		break;
	}
	return CDialogEx::OnSetCursor(pWnd, nHitTest, message);
}

CLadderCtrl.h

cpp 复制代码
class CLadderCtrl : public CWnd{
	CStringArray m_ds;
	int m_nIndex{};
	int m_nTrack{};
	CBrush m_br, m_brSel,m_brTrack; //三态按钮 没有选中色(普通) 选中色 放在上面的框色(追踪态)
	CBrush m_brBack;
	int GetIndex(CPoint point);
public:
	int GetTrack() const{
		return m_nTrack; //拿到你追踪到的是几号
	}
	CString GetItemText(int nIndex) const{
		return m_ds[nIndex]; //拿到的对应的索引来获取文字
	}
	CLadderCtrl();

CLadderCtrl.cpp

cpp 复制代码
int CLadderCtrl::GetIndex(CPoint point)
{
	CClientDC dc(this);
	dc.SelectStockObject(DEFAULT_GUI_FONT);
	int i = -1, nCount = (int)m_ds.GetCount();
	CRect rect;
	GetClientRect(rect);
	CRect rc{ 0,0,0,rect.bottom };

	while (++i < nCount)
	{
		CString str = m_ds[i];

		CSize size = dc.GetTextExtent(str);
		rc.right = rc.left + size.cx + rect.Height();
		dc.SelectObject(i == m_nIndex ? &m_brSel : &m_br);
		if (rc.PtInRect(point))
		{
			return i;
		}
		rc.left = rc.right;
	}
	return -1;
}
cpp 复制代码
void CLadderCtrl::OnLButtonDown(UINT nFlags, CPoint point)  //和上面算法一致 这个用来算出位置
{
	int nIndex = GetIndex(point);
	if (nIndex != m_nIndex)  //去除无效点击 
	{
		m_nIndex = nIndex;
		Invalidate(FALSE);
	}
	CWnd::OnLButtonDown(nFlags, point);
}
cpp 复制代码
void CLadderCtrl::OnMouseMove(UINT nFlags, CPoint point)
{
	int nIndex = GetIndex(point);
	//if (nIndex == m_nIndex)   
	//	return;
	if (m_nTrack!=nIndex)   
	{
		m_nTrack = nIndex;
		Invalidate(FALSE);
	}
	CWnd::OnMouseMove(nFlags, point);
}

附录

cpp 复制代码
CDC::FillSolidRect:无边框的纯色填充
GetSysColor获取系统常用的颜色。
GetStockObject(API) 获取系统预定义对象
CDC::SelectStockObject:选择系统预定义对象。
来自于API:GetStockObject
cpp 复制代码
GDI对象的构造函数:CFile的构造函数
a)CPen的构造函数:
CPen(int nPenStyle, int nWidth, COLORREF crColor);
CPen pen;pen.CreatePen(PS_XXX...);
两句合成一句就是:CPen pen(PS_XXX...);


b)CBrush的构造函数
	CBrush(COLORREF crColor);             // CreateSolidBrush
	CBrush(int nIndex, COLORREF crColor); // CreateHatchBrush
	explicit CBrush(CBitmap* pBitmap);          // CreatePatternBrush

MouseMove 进入任意控件都不会理你 除非回到主窗口范围内

OnSetCursor 不论在什么控件都会显示出来 还可以switch getDlgCtrlID 任何控件都会执行

相关推荐
乐之者v4 分钟前
leetCode43.字符串相乘
java·数据结构·算法
A懿轩A1 小时前
C/C++ 数据结构与算法【数组】 数组详细解析【日常学习,考研必备】带图+详细代码
c语言·数据结构·c++·学习·考研·算法·数组
古希腊掌管学习的神1 小时前
[搜广推]王树森推荐系统——矩阵补充&最近邻查找
python·算法·机器学习·矩阵
云边有个稻草人1 小时前
【优选算法】—复写零(双指针算法)
笔记·算法·双指针算法
机器视觉知识推荐、就业指导1 小时前
C++设计模式:享元模式 (附文字处理系统中的字符对象案例)
c++
半盏茶香1 小时前
在21世纪的我用C语言探寻世界本质 ——编译和链接(编译环境和运行环境)
c语言·开发语言·c++·算法
忘梓.2 小时前
解锁动态规划的奥秘:从零到精通的创新思维解析(3)
算法·动态规划
Ronin3052 小时前
11.vector的介绍及模拟实现
开发语言·c++
✿ ༺ ོIT技术༻2 小时前
C++11:新特性&右值引用&移动语义
linux·数据结构·c++
字节高级特工2 小时前
【C++】深入剖析默认成员函数3:拷贝构造函数
c语言·c++