文章目录
- GDI对象画笔和画刷来开发控件
-
- 梯形边框的按钮控件
- [矩形边框的三态按钮控件 CToolTipCtrl开发动静态提示](#矩形边框的三态按钮控件 CToolTipCtrl开发动静态提示)
-
- CMainDlg.h
- CMainDlg.cpp
- CLadderCtrl.h
- [CLadderCtrl.cpp: 实现文件](#CLadderCtrl.cpp: 实现文件)
- [矩形边框的三态按钮控件 CToolTipCtrl开发动态提示](#矩形边框的三态按钮控件 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 任何控件都会执行