MFC界面美化---自绘标题栏

article/2025/9/5 9:42:38

做MFC项目的时候,会经常遇到需要美化界面的时候。mfc的原始界面比较丑,今天主要记录一下对MFC标题栏重绘的过程,最终效果是这样的。

 1.因为要对标题栏进行重绘,我们要把MFC的原生标题栏给去掉,在MFC对话框的属性那里,

把边框的属性设置成None.

2. 去掉MFC的标题栏后,整个MFC界面鼠标拖动的时候无法让之移动,我们需要添加ON_WM_NCHITTEST()消息。

LRESULT CMFCDrawnTitleDlg::OnNcHitTest(CPoint point)
{// TODO: 在此添加消息处理程序代码和/或调用默认值UINT nHitTest = CDialogEx::OnNcHitTest(point);CRect rect;GetClientRect(&rect);rect.bottom = 70; // 表示界面的顶端到下面70像素的高度 鼠标在这个位置可以控制界面移动//函数参数point是相对于屏幕坐标的,需要将其转换为//客户区坐标才能使用PtInRect(),否则会因为坐标的不同使判断失误ScreenToClient(&point);if (rect.PtInRect(point))return nHitTest;
}

3.菜单栏的绘制,因为去掉了界面的边框,我们要自己绘制菜单栏。在OnPaint()函数里面进行实现

void CMFCDrawnTitleDlg::OnPaint()
{if (IsIconic()){CPaintDC dc(this); // 用于绘制的设备上下文SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);// 使图标在工作区矩形中居中int cxIcon = GetSystemMetrics(SM_CXICON);int cyIcon = GetSystemMetrics(SM_CYICON);CRect rect;GetClientRect(&rect);int x = (rect.Width() - cxIcon + 1) / 2;int y = (rect.Height() - cyIcon + 1) / 2;// 绘制图标dc.DrawIcon(x, y, m_hIcon);}else{CPaintDC dc(this);CRect rect;//------------------CRect tmprect;GetClientRect(&rect);//rect.bottom = 60;rect.bottom *= 0.1;/*int nCount = 165 - 115 + 186 - 158 + 190 - 115;int nIncrecs = (rect.right - rect.left) / nCount;*/dc.FillSolidRect(rect, RGB(45, 51, 60));dc.SetBkMode(TRANSPARENT);CRect rtBtnClo;//绘制文件菜单栏GetClientRect(&rtBtnClo);rtBtnClo.left = rtBtnClo.left + 10;rtBtnClo.right = rtBtnClo.left + 30;rtBtnClo.bottom *= 0.1;rtBtnClo.top = rtBtnClo.bottom - 20;m_rtBtnfile = rtBtnClo;dc.Rectangle(rtBtnClo);dc.FillSolidRect(m_rtBtnfile, RGB(45, 51, 59));dc.SetBkMode(TRANSPARENT);CString str1 = TEXT("文件");dc.SetTextColor(RGB(185, 192, 197));dc.DrawText(str1, m_rtBtnfile, DT_LEFT | DT_VCENTER);//CDialogEx::OnPaint();//绘制选项菜单栏GetClientRect(&rtBtnClo);rtBtnClo.left = rtBtnClo.left + 70;rtBtnClo.right = rtBtnClo.left + 30;rtBtnClo.bottom *= 0.1;rtBtnClo.top = rtBtnClo.bottom - 20;m_rtBtnSelect = rtBtnClo;dc.Rectangle(rtBtnClo);dc.FillSolidRect(m_rtBtnSelect, RGB(45, 51, 59));dc.SetBkMode(TRANSPARENT);str1 = TEXT("选项");dc.SetTextColor(RGB(185, 192, 197));dc.DrawText(str1, m_rtBtnSelect, DT_LEFT | DT_VCENTER);//绘制帮助菜单栏GetClientRect(&rtBtnClo);rtBtnClo.left = rtBtnClo.left + 130;rtBtnClo.right = rtBtnClo.left + 30;rtBtnClo.bottom *= 0.1;rtBtnClo.top = rtBtnClo.bottom - 20;m_rtBtnHelp = rtBtnClo;dc.Rectangle(rtBtnClo);dc.FillSolidRect(m_rtBtnHelp, RGB(45, 51, 59));dc.SetBkMode(TRANSPARENT);str1 = TEXT("帮助");dc.SetTextColor(RGB(185, 192, 197));dc.DrawText(str1, m_rtBtnHelp, DT_LEFT | DT_VCENTER);CDialogEx::OnPaint();}
}

4.标题栏的背景颜色绘制

背景颜色绘制也在OnPaint()中。具体是这段代码

        CRect tmprect;GetClientRect(&rect);//rect.bottom = 60;rect.bottom *= 0.1;dc.FillSolidRect(rect, RGB(45, 51, 60));dc.SetBkMode(TRANSPARENT);CRect rtBtnClo;

5.最大化,最小化,退出,按钮的绘制。

这块我的设计是对按钮进行重绘,在按钮上进行贴图,把最大化,最小化的图片贴在按钮上。

然后把按钮的位置放在右上角。

定义自己的Button变量

CMyButton m_btnExit;
CMyButton m_btnMax;
CMyButton m_btnMin;

在OnInitDialog里绘制

//退出按钮绘制CRect rtBtnClo;GetClientRect(&rtBtnClo);rtBtnClo.left = rtBtnClo.right - 20;m_btnExit.SetImagePath(_T(".\\res\\icon_popup_off.png"), _T(".\\res\\icon_popup_off.png"), _T(".\\res\\icon_popup_off.png"));m_btnExit.InitMyButton(rtBtnClo.left, 5, 16, 16, true);//最大化按钮绘制GetClientRect(&rtBtnClo);rtBtnClo.left = rtBtnClo.right - 50;m_btnMax.SetImagePath(_T(".\\res\\icon_square.png"), _T(".\\res\\icon_square.png"), _T(".\\res\\icon_square.png"));m_btnMax.InitMyButton(rtBtnClo.left, 5, 16, 16, true);//最小化按钮// GetClientRect(&rtBtnClo);rtBtnClo.left = rtBtnClo.right - 80;m_btnMin.SetImagePath(_T(".\\res\\icon_minimiz.png"), _T(".\\res\\icon_minimiz.png"), _T(".\\res\\icon_minimiz.png"));m_btnMin.InitMyButton(rtBtnClo.left, 5, 16, 16, true);

注意事项:把图片贴到按钮的时候 ,路过你的图片有空白区域  MFC会默认把空白区域填充成黑色,所以你的图片需要设计成空白区域填充成和你的界面的背景颜色是一样的

6.按钮美化,对按钮进行重绘。

#pragma once#include <afxwin.h>
#include "pch.h"
#include "framework.h"
// CMyButtonclass CMyButton : public CButton
{DECLARE_DYNAMIC(CMyButton)public:CMyButton();virtual ~CMyButton();protected://正常状态图像路径CString m_strNormalImgPath;//按下状态图像路径CString m_strPressImgPath;//悬浮状态图像路径CString m_strFloatImgPath;//正常状态图像CImage m_imgNormal;//按下状态图像CImage m_imgPress;//悬浮状态图像CImage m_imgFloat;//窗口背景图片CImage m_BkImg;public://设置按钮图片路径void SetImagePath(CString strNoramlImgPath, CString strPressImgPath, CString strFloatImgPath);//初始化按钮,主要是调整按钮的位置,处理透明色  bool InitMyButton(int nX/*左上角X坐标*/, int nY/*左上角Y坐标*/, int nW/*图像宽*/, int nH/*图像高*/, bool bIsPng/*是否是PNG图片*/);//自绘制函数  void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);//初始化窗口背景void SetBkImg(CString strBkImg);//释放图片资源,方便最大化 void ReleaseImg();
protected://光标是否在窗口内BOOL m_bIsInWnd;DECLARE_MESSAGE_MAP()
public:afx_msg void OnMouseMove(UINT nFlags, CPoint point);afx_msg void OnMouseHover(UINT nFlags, CPoint point);afx_msg void OnMouseLeave();};
// MyButton.cpp : 实现文件
//#include "pch.h"
#include "MyButton.h"// CMyButtonIMPLEMENT_DYNAMIC(CMyButton, CButton)CMyButton::CMyButton()
{m_bIsInWnd = FALSE;
}CMyButton::~CMyButton()
{
}BEGIN_MESSAGE_MAP(CMyButton, CButton)ON_WM_MOUSEMOVE()ON_WM_MOUSEHOVER()ON_WM_MOUSELEAVE()
END_MESSAGE_MAP()// CMyButton 消息处理程序//设置按钮图片路径
void CMyButton::SetImagePath(CString strNoramlImgPath, CString strPressImgPath, CString strFloatImgPath)
{m_strNormalImgPath = strNoramlImgPath;m_strPressImgPath = strPressImgPath;m_strFloatImgPath = strFloatImgPath;
}void CMyButton::SetBkImg(CString strBkImg)
{if (strBkImg.IsEmpty())return;m_BkImg.Load(strBkImg);
}//初始化按钮,主要是调整按钮的位置,处理透明色 
void CMyButton::ReleaseImg()
{if (m_imgNormal){m_imgNormal.Destroy();}if (m_imgPress){m_imgPress.Destroy();}if (m_imgFloat){m_imgFloat.Destroy();}
}
bool CMyButton::InitMyButton(int nX/*左上角X坐标*/, int nY/*左上角Y坐标*/, int nW/*图像宽*/, int nH/*图像高*/, bool bIsPng/*是否是PNG图片*/)
{HRESULT hr = 0;if (m_strNormalImgPath.IsEmpty())return false;if (m_strPressImgPath.IsEmpty())return false;if (m_strFloatImgPath.IsEmpty())return false;hr = m_imgNormal.Load(m_strNormalImgPath);int a = GetLastError();if (FAILED(hr))return false;hr = m_imgPress.Load(m_strPressImgPath);if (FAILED(hr))return false;hr = m_imgFloat.Load(m_strFloatImgPath);if (FAILED(hr))return false;if (bIsPng){if (m_imgNormal.GetBPP() == 32){int i = 0;int j = 0;for (i = 0; i < m_imgNormal.GetWidth(); i++){for (j = 0; j < m_imgNormal.GetHeight(); j++){byte * pbyte = (byte *)m_imgNormal.GetPixelAddress(i, j);pbyte[0] = pbyte[0] * pbyte[3] / 255;pbyte[1] = pbyte[1] * pbyte[3] / 255;pbyte[2] = pbyte[2] * pbyte[3] / 255;}}}if (m_imgPress.GetBPP() == 32){int i = 0;int j = 0;for (i = 0; i < m_imgPress.GetWidth(); i++){for (j = 0; j < m_imgPress.GetHeight(); j++){byte * pbyte = (byte *)m_imgPress.GetPixelAddress(i, j);pbyte[0] = pbyte[0] * pbyte[3] / 255;pbyte[1] = pbyte[1] * pbyte[3] / 255;pbyte[2] = pbyte[2] * pbyte[3] / 255;}}}if (m_imgFloat.GetBPP() == 32){int i = 0;int j = 0;for (i = 0; i < m_imgFloat.GetWidth(); i++){for (j = 0; j < m_imgFloat.GetHeight(); j++){byte * pbyte = (byte *)m_imgFloat.GetPixelAddress(i, j);pbyte[0] = pbyte[0] * pbyte[3] / 255;pbyte[1] = pbyte[1] * pbyte[3] / 255;pbyte[2] = pbyte[2] * pbyte[3] / 255;}}}}MoveWindow(nX, nY, nW, nH);return true;}
//自绘制函数  
void CMyButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{if (!lpDrawItemStruct)return;HDC hMemDC;HBITMAP bmpMem;HGDIOBJ hOldObj;bmpMem = CreateCompatibleBitmap(lpDrawItemStruct->hDC, lpDrawItemStruct->rcItem.right - lpDrawItemStruct->rcItem.left, lpDrawItemStruct->rcItem.bottom - lpDrawItemStruct->rcItem.top);if (!bmpMem)return;hMemDC = CreateCompatibleDC(lpDrawItemStruct->hDC);if (!hMemDC){if (bmpMem){::DeleteObject(bmpMem);bmpMem = NULL;}return;}hOldObj = ::SelectObject(hMemDC, bmpMem);int nW = lpDrawItemStruct->rcItem.right - lpDrawItemStruct->rcItem.left;int nH = lpDrawItemStruct->rcItem.bottom - lpDrawItemStruct->rcItem.top;RECT rectTmp = { 0 };rectTmp = lpDrawItemStruct->rcItem;MapWindowPoints(GetParent(), &rectTmp);if (m_BkImg.IsNull() == false)m_BkImg.Draw(hMemDC, 0, 0, rectTmp.right - rectTmp.left, rectTmp.bottom - rectTmp.top, rectTmp.left, rectTmp.top, rectTmp.right - rectTmp.left, rectTmp.bottom - rectTmp.top);if (lpDrawItemStruct->itemState & ODS_SELECTED){//按钮被选择  m_imgPress.AlphaBlend(hMemDC, 0, 0, nW, nH, 0, 0, nW, nH);}else{//默认状态  m_imgNormal.AlphaBlend(hMemDC, 0, 0, nW, nH, 0, 0, nW, nH);}::BitBlt(lpDrawItemStruct->hDC, 0, 0, nW, nH, hMemDC,0,0,SRCCOPY);SelectObject(hMemDC, hOldObj);if (bmpMem){::DeleteObject(bmpMem);bmpMem = NULL;}if (hMemDC){::DeleteDC(hMemDC);hMemDC = NULL;}return;
}void CMyButton::OnMouseMove(UINT nFlags, CPoint point)
{// TODO:  在此添加消息处理程序代码和/或调用默认值CButton::OnMouseMove(nFlags, point);if (!m_bIsInWnd){TRACKMOUSEEVENT       tme;tme.cbSize = sizeof(TRACKMOUSEEVENT);tme.dwFlags = TME_HOVER | TME_LEAVE;tme.dwHoverTime = 10;tme.hwndTrack = m_hWnd;_TrackMouseEvent(&tme);m_bIsInWnd = TRUE;}
}void CMyButton::OnMouseHover(UINT nFlags, CPoint point)
{// TODO:  在此添加消息处理程序代码和/或调用默认值HDC hMemDC;HBITMAP bmpMem;HGDIOBJ hOldObj;HDC hDC = ::GetDC(GetSafeHwnd());CRect rcItem;GetClientRect(&rcItem);if (hDC){bmpMem = CreateCompatibleBitmap(hDC, rcItem.Width(), rcItem.Height());if (!bmpMem){::ReleaseDC(GetSafeHwnd(), hDC);return;}hMemDC = CreateCompatibleDC(hDC);if (!hMemDC){if (bmpMem){::DeleteObject(bmpMem);bmpMem = NULL;}::ReleaseDC(GetSafeHwnd(), hDC);return;}hOldObj = ::SelectObject(hMemDC, bmpMem);RECT rectTmp = { 0 };rectTmp = rcItem;MapWindowPoints(GetParent(), &rectTmp);if (m_BkImg.IsNull() == false)m_BkImg.Draw(hMemDC, 0, 0, rectTmp.right - rectTmp.left, rectTmp.bottom - rectTmp.top, rectTmp.left, rectTmp.top, rectTmp.right - rectTmp.left, rectTmp.bottom - rectTmp.top);int nW = rcItem.right - rcItem.left;int nH = rcItem.bottom - rcItem.top;m_imgFloat.AlphaBlend(hMemDC, 0, 0, nW, nH, 0, 0,nW,nH);::BitBlt(hDC, 0, 0, nW, nH, hMemDC, 0, 0, SRCCOPY);SelectObject(hMemDC, hOldObj);if (bmpMem){::DeleteObject(bmpMem);bmpMem = NULL;}if (hMemDC){::DeleteDC(hMemDC);hMemDC = NULL;}::ReleaseDC(GetSafeHwnd(), hDC);}CButton::OnMouseHover(nFlags, point);
}void CMyButton::OnMouseLeave()
{// TODO:  在此添加消息处理程序代码和/或调用默认值CButton::OnMouseLeave();InvalidateRect(NULL);m_bIsInWnd = FALSE;
}


http://chatgpt.dhexx.cn/article/V76iZagl.shtml

相关文章

Windows界面编程:MFC

前言 大家之前在学C/C语言时&#xff0c;基本都是通过控制台实现“人机交流”的。但大家每次在写控制台程序时&#xff0c;看到黑框框应该都会有些许不爽吧&#xff1a;“输入输出为什么不能基于图形用户界面而非要使用文本用户界面呢&#xff1f;”事实上&#xff0c;在各个环…

MFC-登录界面(可修改密码)

登录界面的博客有很多&#xff0c;参考了这一篇文章&#xff0c;下面给出链接。 MFC做一个登录界面&#xff08;用户名&#xff0c;密码以及密码的明文密文切换&#xff09;_umarutyan的博客-CSDN博客 但这里无法修改密码&#xff0c;下面做出一些补充。 由于用不到数据库&a…

MFC界面工具BCGControlBar入门:如何使用工具栏编辑器

BCGControlBar ("Business Components Gallery ControlBar")是MFC扩展库&#xff0c;使您可以创建具有完全自定义选项&#xff08;功能区、可自定义工具栏、菜单等&#xff09;以及一组专业设计的丰富Microsoft Office和Microsoft Visual Studio的应用程序 GUI控件&a…

MFC程序中使用QT开发界面

如果你有一个现成的MFC项目在做维护&#xff0c;但是你厌倦了使用MFC繁琐的操作来做界面美化&#xff0c;或者你需要在这个项目中用到QT里面好用的某些功能&#xff1b;亦或者是你需要使用某些只能在MFC中使用的组件&#xff0c;但是界面这部分已经用QT做好了。那么这篇文章可能…

一、MFC登陆界面

一、新建项目 打开VS2022——>创建新项目——>选择MFC应用&#xff0c;点击下一步——>修改项目名称和位置&#xff0c;点击创建——>修改应用程序类型、项目样式经典菜单选项 二、添加预处理指令 VS建议采用带_s的函数&#xff0c;如scanf_s、strcpy_s&#xff0…

MFC基础知识与课程设计思路

引言 本文致力于提供MFC的相关知识&#xff0c;以方便大家更好地认识MFC的使用方法。介绍将会分为以下几个部分&#xff1a;MFC初始文件的理解、MFC我们所使用的框架理解、MFC的进阶用法、MFC我在使用过程中遇到的问题及解决方法。 MFC初始文件的理解 MFC的初始文件中有两项文…

MFC添加程序关闭时时的提示界面

文章目录 MFC一.简介二.方法 MFC 一.简介 在应用程序退出的时候&#xff0c;不能点击叉直接退出&#xff0c;我们想添加一个退出提示。在点击叉后&#xff0c;弹出是否确定退出的界面&#xff0c;如下面的界面&#xff0c;具体操作往下看 二.方法 找到我们需要操作的主界面…

【MFC】模拟采集系统——界面设计(17)

功能介绍 启动界面 开始采集&#xff1a; PS&#xff1a;不涉及 数据保存&#xff0c;重现等功能 界面设计 界面分为三块&#xff1a;顶部黑条带关闭按钮、左边对话框&#xff0c;右边的主界面 资源&#xff1a; 顶部黑条 top.bmp 2* 29 &#xff08;宽 * 高 像素点&…

MFC界面设计入门篇

点击C里的MFC再点击MFCApplication&#xff0c;到下面改名字和路径&#xff0c;然后OK 然后点击Next&#xff0c; 选择single document&#xff0c;MFCstandard&#xff0c;简体中文&#xff0c;然后Finish 这时候可以先直接运行&#xff0c;看看工程的样子&#x…

最最简单的几个Mac终端命令

几个简单的Mac终端命令 目录切换相关 cd空格/ 回到根目录cd空格… &#xff08;或者 cd空格…/&#xff09; 回到上一级目录cd空格. 回到当前目录pwd 显示从根目录到当前目录的完整目录 vi操作相关 注意&#xff1a;vi操作的文件如果不存在&#xff0c;则先自动创建一个该名字…

Mac 终端基本命令

基本命令 1、列出文件 ls 参数 目录名 例: 看看驱动目录下有什么:ls /System/Library/Extensions 参数 -w 显示中文&#xff0c;-l 详细信息&#xff0c; -a 包括隐藏文件 2、转换目录 cd 例&#xff1a;想到驱动目录下溜达一圈 cd /System/Library/Extension…

10需要知道Mac终端命令

如果你想进入web开发领域&#xff0c;知道什么是终端&#xff0c;如何使用终端是非常有益的。在今天的文章中&#xff0c;我们将介绍Mac终端命令的10个必要知识&#xff01; &#x1f642; 什么是终端&#xff08;Terminal&#xff09; 终端最基本的用途是可以浏览计算机的文…

Mac终端命令

Mac电脑安装程序&#xff0c;打开允许任何来源的方法&#xff0c;在终端执行命令行即可。 打开命令&#xff1a;sudo spctl --master-disable 关闭命令&#xff1a;sudo spctl --master-enable Mac终端打开文件 1.打开文件夹的命令很简单&#xff0c;使用 open 文件夹…

Mac终端命令失效( command not found)/

ls vi vim 输入完来一句command not found xx 心哇凉哇凉的&#xff0c;心态都崩了。 guolianggldeMacBook-Pro ~ % cat zsh: command not found: cat guolianggldeMacBook-Pro ~ % ls zsh: command not found: ls guolianggldeMacBook-Pro ~ % mdfind zsh: command not fo…

MacOS系统终端常用命令大全

MacOS系统终端是使用mac电脑的小伙伴需要或多或少了解的一个应用。熟悉并掌握一些基本的常用命令可以帮助我们快速的解决一些日常问题&#xff0c;提高工作效率&#xff0c;下面让小编带大家了解一下吧&#xff01; 基本概念 命令的构成&#xff1a;Command Name、Options、Ar…

Mac终端 常用命令

OSX 的文件系统 OSX 采用的Unix文件系统&#xff0c;所有文件都挂在跟目录 / 下面&#xff0c;所以不在要有Windows 下的盘符概念。 你在桌面上看到的硬盘都挂在 /Volumes 下。 比如接上个叫做 USBHD的移动硬盘&#xff0c;桌面上会显示出一个硬盘图标&#xff0c;它实际在哪…

mac 终端 常用命令

文件目录 首先要清楚几个文件目录&#xff1a; " / " &#xff1a;根目录 " ~ " &#xff1a;用户主目录的缩写。例如当前用户为hello&#xff0c;那么" ~ "展开来就是&#xff1a;/Users/hello " . " &#xff1a;当前目录 "…

RANSAC原理及直线拟合(python动态图解)

一、简介 随机采样一致性&#xff08;Random Sample Consensus&#xff0c;RANSAC&#xff09;由斯坦福国际研究院的Fischler和Bolles于1981年首次提出[1]。RANSAC算法是一种随机参数估计迭代算法&#xff1b;从一组包含异常数据的样本数据集中&#xff0c;通过迭代的方式&…

RANSAC初识

RANSAC算法&#xff1a;随机抽样一致算法&#xff08;random sample consensus,RANSAC&#xff09; 一个简单的例子是从一组观测数据中找出合适的二维直线。假设观测数据中包含局内点和局外点&#xff0c;其中局内点近似的被直线所通过&#xff0c;而局外点远离于直线。简单的…

RANSAC算法详解

RANSAC算法详解 给定两个点p1与p2的坐标&#xff0c;确定这两点所构成的直线&#xff0c;要求对于输入的任意点p3&#xff0c;都可以判断它是否在该直线上。初中解析几何知识告诉我们&#xff0c;判断一个点在直线上&#xff0c;只需其与直线上任意两点点斜率都相同即可。实际…