当前位置: 首页 > news >正文

MFC中使用GDI+ 自定义等待界面

MFC中使用GDI+ 自定义等待界面

在工作中我们总是会遇到等待任务完成的场景,我们可以设置Windows自带的鼠标光标是等待的,沙漏(⏳),今天介绍使用GDI+在MFC程序中自定义简单的等待界面。

第一步:新建一个MFC程序,然后再添加一个对话框,添加类CWaitInfoDlg如下:

ScreenShot_2025-11-15_142542_563

第二步:添加代码

1.加载GDI+库,并且编写自定义代码。WaitInfoDlg.h如下:

#pragma once
#include <afxcmn.h>
#include <gdiplus.h>
#include "resource.h"
#pragma comment(lib, "gdiplus.lib")
using namespace Gdiplus;// CWaitInfoDlg 对话框
class CWaitInfoDlg : public CDialog
{DECLARE_DYNAMIC(CWaitInfoDlg)public:CWaitInfoDlg(CWnd* pParent = NULL);   // 标准构造函数virtual ~CWaitInfoDlg();// 对话框数据enum { IDD = IDD_DIALOG_WAITINFO };protected:virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持DECLARE_MESSAGE_MAP()public:virtual BOOL OnInitDialog();void StartAnimation();      // 启动定时器void StopAnimation();       // 停定时器void SetType(int nType);             // 设置类型void SetTitle(CString strTitle);            // 设置标题
private:UINT  m_nAngle;       // 当前旋转角UINT  m_nFrame;          // 当前帧 0-59int m_nType;            // 等待效果类型CString m_strTitle;    // 标题
public:afx_msg void OnPaint();afx_msg void OnTimer(UINT_PTR nIDEvent);
};

WaitInfoDlg.cpp

#include "pch.h"
#include "WaitInfoDlg.h"// CWaitInfoDlg 对话框IMPLEMENT_DYNAMIC(CWaitInfoDlg, CDialog)CWaitInfoDlg::CWaitInfoDlg(CWnd* pParent /*=NULL*/): CDialog(CWaitInfoDlg::IDD, pParent)
{m_nAngle = 0;m_nType = 0;
}CWaitInfoDlg::~CWaitInfoDlg()
{}void CWaitInfoDlg::DoDataExchange(CDataExchange* pDX)
{CDialog::DoDataExchange(pDX);
}BEGIN_MESSAGE_MAP(CWaitInfoDlg, CDialog)ON_WM_PAINT()ON_WM_TIMER()
END_MESSAGE_MAP()// CWaitInfoDlg 消息处理程序
BOOL CWaitInfoDlg::OnInitDialog()
{CDialog::OnInitDialog();// TODO:  在此添加额外的初始化// 初始化 GDI+Gdiplus::GdiplusStartupInput gdiplusStartupInput;ULONG_PTR gdiplusToken;Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);// 让窗口支持分层透明SetWindowLong(m_hWnd, GWL_EXSTYLE,GetWindowLong(m_hWnd, GWL_EXSTYLE) | WS_EX_LAYERED);// 整窗透明(0-255)SetLayeredWindowAttributes(0, 200, LWA_ALPHA);SetWindowText(m_strTitle);  // 设置标题return TRUE;  // return TRUE unless you set the focus to a control// 异常: OCX 属性页应返回 FALSE
}void CWaitInfoDlg::OnPaint()
{CPaintDC dc(this); // device context for painting// TODO: 在此处添加消息处理程序代码// 不为绘图消息调用 CDialog::OnPaint()CRect rc;GetClientRect(&rc);//==== 1. 创建内存 DC + 位图(与屏幕同尺寸同色深)CDC memDC;memDC.CreateCompatibleDC(&dc);CBitmap memBmp;memBmp.CreateCompatibleBitmap(&dc, rc.Width(), rc.Height());CBitmap* pOldBmp = memDC.SelectObject(&memBmp);//==== 2. 在内存里整帧绘制(先清背景,再画水泡)memDC.FillSolidRect(&rc, RGB(255, 255, 255));   // 背景Gdiplus::Graphics g(memDC.m_hDC);               // 内存 DC 交给 GDI+g.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);int cx = rc.Width() / 2;int cy = rc.Height() / 2;int radius = min(cx, cy) - 10;switch (m_nType){case 0:{// 画 5 个扇形组成旋转环for (int i = 0; i < 5; i++){int start = m_nAngle + i * 72;Gdiplus::GraphicsPath path;path.AddArc(cx - radius, cy - radius, 2 * radius, 2 * radius, start, 60);path.AddArc(cx - radius + 10, cy - radius + 10, 2 * (radius - 10), 2 * (radius - 10), start + 60, -60);path.CloseFigure();Gdiplus::SolidBrush br(Gdiplus::Color(255 - i * 40, 100, 255));g.FillPath(&br, &path);}}break;case 1:{for (int i = 0; i < 18; i++){int angle = (m_nFrame + i * 10) * 2;double rad = angle * 3.1415926 / 180.0;int x = cx + (int)(radius * cos(rad));int y = cy + (int)(radius * sin(rad));int r = 10 + i * 2;Gdiplus::GraphicsPath path;path.AddEllipse(x - r, y - r, 2 * r, 2 * r);Gdiplus::SolidBrush br(Gdiplus::Color(255 - i * 40, 100, 255));g.FillPath(&br, &path);}}break;}//==== 3. 一次性把整帧拷到屏幕(无闪烁)dc.BitBlt(0, 0, rc.Width(), rc.Height(), &memDC, 0, 0, SRCCOPY);//==== 4. 清理memDC.SelectObject(pOldBmp);CDialog::OnPaint();
}void CWaitInfoDlg::OnTimer(UINT_PTR nIDEvent)
{// TODO: 在此添加消息处理程序代码和/或调用默认值if (nIDEvent == 1){switch (m_nType){case 0:{m_nAngle += 10;if (m_nAngle >= 360)m_nAngle = 0;break;}case 1:{m_nFrame = (m_nFrame + 1) % 20;break;}}Invalidate(FALSE);}CDialog::OnTimer(nIDEvent);
}void CWaitInfoDlg::StartAnimation()    
{SetTimer(1, 30, NULL);     
}
void CWaitInfoDlg::StopAnimation()     
{KillTimer(1);
}void CWaitInfoDlg::SetType(int nType)
{m_nType = nType;
}void CWaitInfoDlg::SetTitle(CString strTitle)
{m_strTitle = strTitle;
}

代码说明:

1.这里定义了两种等待显示界面,一种是旋转环,一种是旋转气泡;通过调用SetType设置,选择显示哪一种。

2.另外我们通过定时器来实现界面刷新。

3.绘制代码中,使用的GDI+中的Graphics类来绘制圆环和圆。

4.代码使用了双缓冲显示,解决界面闪烁的问题。

第三步:代码测试

自定义两个消息:

#define UM_TASKSTART (WM_USER + 100)
#define UM_TASKEND   (WM_USER + 101)

3.1 在CMainFrame.h中添加如下代码:

public:CWaitInfoDlg m_WaitInfoDlg;afx_msg LRESULT OnTaskStart(WPARAM wParam,LPARAM lParaam);afx_msg LRESULT OnTaskEnd(WPARAM wParam, LPARAM lParaam);afx_msg void OnTimer(UINT_PTR nIDEvent);

将自定义消息绑定到OnTaskStart、OnTaskEnd中:

BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)ON_WM_CREATE()ON_MESSAGE(UM_TASKSTART,&CMainFrame::OnTaskStart)ON_MESSAGE(UM_TASKEND, &CMainFrame::OnTaskEnd)ON_WM_TIMER()
END_MESSAGE_MAP()

在CMainFrame.cpp中实现自定义消息响应函数:

LRESULT CMainFrame::OnTaskStart(WPARAM wParam, LPARAM lParaam)
{if (m_WaitInfoDlg.m_hWnd == NULL){m_WaitInfoDlg.Create(IDD_DIALOG_WAITINFO, this);m_WaitInfoDlg.SetWindowPos(NULL, 0, 0, 500, 500, SWP_SHOWWINDOW);m_WaitInfoDlg.SetTitle(_T("任务处理中..."));m_WaitInfoDlg.SetType(1);m_WaitInfoDlg.ShowWindow(SW_SHOW);m_WaitInfoDlg.UpdateWindow();m_WaitInfoDlg.CenterWindow();m_WaitInfoDlg.StartAnimation();}return 0;
}LRESULT CMainFrame::OnTaskEnd(WPARAM wParam, LPARAM lParaam)
{// 任务结束,关闭界面SetTimer(0,500,NULL);return 0;
}

CMainFrame::OnTimer函数实现:

void CMainFrame::OnTimer(UINT_PTR nIDEvent)
{// TODO: 在此添加消息处理程序代码和/或调用默认值if (nIDEvent == 0){m_WaitInfoDlg.StopAnimation();m_WaitInfoDlg.DestroyWindow();KillTimer(0);}CFrameWnd::OnTimer(nIDEvent);
}

代码说明:

1.任务开始就调用Create函数创建等待对话框;任务结束,调用DestroyWindow函数销毁对话框。

简单的测试代码,在视图中响应菜单命令消息:

void CLoadingTestView::OnTasktest()
{// TODO: 在此添加命令处理程序代码::SendMessage(theApp.m_pMainWnd->m_hWnd,UM_TASKSTART,NULL,NULL);// 模拟任务处理int i = 0;while (i++ < 10000);::PostMessage(theApp.m_pMainWnd->m_hWnd,UM_TASKEND, NULL, NULL);}

代码运行效果:

1.圆环形等待界面。

LoadingTest_eQCGWbHEvx

2.水泡形等待界面。

LoadingTest_QQ3XouNtue

好了,今天的介绍就到这里了。

Demo地址:Gitee

http://www.dtcms.com/a/613490.html

相关文章:

  • 信息论(五):联合熵与条件熵
  • flume抽取kafka数据到kafka,数据无法从topicA抽取到topicB
  • 基于最小权限原则的云计算Amazon VPC多层应用安全架构设计
  • 11.2 FastGPT部署指南:Docker一键部署企业级RAG框架
  • 网站建设结课总结贵阳网络推广优化
  • 网络安全态势报告,网络安全风险评估报告文档
  • R包fastEnrich预开发一 -- 快速GO富集分析、自动化报告、优化气泡图
  • 企业做网站设置哪些模块网站存储空间
  • Java 集合面试核心:ArrayList/LinkedList 底层数据结构,HashMap扩容机制详解
  • 突破AI视频一致性瓶颈:“无废话”四步电影级工作流
  • Python 编程实战 · 实用工具与库 — Django 项目结构简介
  • Dify-SSE流式及速率限制实现
  • 【ros2】ROS2中添加资源文件(图片、声音、视频等)的完整指南
  • 数据分析笔记08:Python编程基础-数据类型与变量
  • 北仑网站网页建设个人网站实现与设计论文
  • 网站在线留言怎么做做网站大优惠
  • 温州做网站哪家比较好镇江公司网站建设
  • QT/C++使用QMessageBox实现一个简单的登陆窗口
  • 前端CI/CD 流程
  • Visual Basic 挑选颜色
  • Java性能调优工具篇:JMH基准测试与Profiler(JProfiler/Async-Profiler)使用指南
  • ASC学习笔记0020:用于定义角色或Actor的默认属性值
  • 第十篇 扫雷游戏 下(末版·精简)
  • 开发者获取Claude API Key 申请指南:从注册到 Python 调用的实战教程
  • pyinstaller 介绍
  • 建设网站与服务器专业网页设计哪家好
  • 【大语言模型 125】开放域对话实战:自然流畅的闲聊系统完全指南
  • FastAPI基础项目:实现用户管理系统,实现基本的搜索和增删改查功能
  • 小众做的好的网站手机下载工具app
  • Qt for HarmonyOS 3D图片轮播组件开源鸿蒙开发实战