前言
在Windows Forms (WinForms) 应用程序开发过程中,动画绘制往往会占用大量的UI线程资源,导致应用程序卡顿甚至无响应。本文将介绍如何通过结合C++和C#,在WinForms应用中实现高效的动画绘制,从而提升应用性能。
为什么选择C++与C#结合?
C#是开发WinForms应用的主要语言,其强大的功能和易用性使其成为许多开发者的首选。然而,C#运行在.NET托管环境中,性能上较C++稍逊一筹。C++作为一种底层语言,可以直接生成机器码,具有更高的执行效率。因此,将复杂的动画绘制逻辑交给C++处理,可以大大减轻C# UI线程的负担,从而提升应用性能。
动画绘制的实现方案对比
C# 自己在Panel中调用动画
直接在C#中实现动画绘制非常直观和简洁,展示如何在Panel上绘制五角星:
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace AnimationDrawer
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
panel1.MouseClick += new MouseEventHandler(panel1_MouseClick);
}
private void Form1_Load(object sender, EventArgs e)
{
Task.Run(() =>
{
int i = 0;
while (true)
{
int x = new Random().Next(1000);
int y = new Random().Next(1000);
panel1_MouseClick(this, new MouseEventArgs(MouseButtons.Left, 1, x, y, 1));
Thread.Sleep(100);
}
});
}
private void panel1_MouseClick(object sender, MouseEventArgs e)
{
Task.Run(() =>
{
Stopwatch st = new Stopwatch();
st.Start();
// 在后台线程执行动画逻辑,减少UI线程负担
Invoke(new Action(() =>
{
Graphics g = panel1.CreateGraphics();
DrawStar(g, e.X, e.Y, 50);
g.Dispose();
}));
st.Stop();
Trace.WriteLine($"DrawStar {st.ElapsedMilliseconds} ms");
});
}
private void DrawStar(Graphics g, int x, int y, int radius)
{
const double PI = 3.14159265358979323846;
Point[] points = new Point[100000];
for (int i = 0; i < 100000; ++i)
{
double angle = i * PI / 5;
int r = (i % 2 == 0) ? radius : radius / 2;
points[i] = new Point(x + (int)(r * Math.Cos(angle - PI / 2)), y + (int)(r * Math.Sin(angle - PI / 2)));
}
g.DrawPolygon(Pens.Red, points);
}
}
}
这个示例比较简单,基本意思就是在Panel上的随机位置绘制一个五角星。程序启动后执行绘制动画。
监控执行结果:
。。。。
DrawStar 8025 ms
DrawStar 8010 ms
DrawStar 7900 ms
DrawStar 7860 ms
DrawStar 7981 ms
DrawStar 7959 ms
DrawStar 8174 ms
DrawStar 8210 ms
程序“[12332] AnimationDrawer.exe”已退出,返回值为 4294967295 (0xffffffff)。
因为绘制Point点比较多,接近10万,所以每次绘制消耗时间比较长。现在我们来看看C++编码如何实现
C++ 函数实现的动画
通过C++实现动画绘制,可以更有效地利用系统资源,减少UI线程的占用。以下是一个C++代码示例,用于绘制五角星:
编写C++部分代码:AnimationDrawer.cpp,和C#绘制一样,也是10万个Point.
// AnimationDrawer.cpp
#include "pch.h"
#include
#include
void DrawStar(HDC hdc, int x, int y, int radius)
{
const double PI = 3.14159265358979323846;
POINT points[100000];
for (int i = 0; i < 100000; ++i) {
double angle = i * PI / 5;
int r = (i % 2 == 0) ? radius : radius / 2;
points[i] = { x + static_cast(r * cos(angle - PI / 2)), y + static_cast(r * sin(angle - PI / 2)) };
}
Polygon(hdc, points, 10);
}
extern "C" __declspec(dllexport) void __stdcall DrawStarAt(HDC hdc, int x, int y)
{
DrawStar(hdc, x, y, 50); // 绘制半径为50的五角星
}
编译上述代码生成一个DLL文件(如AnimationDrawerCpp.dll),然后在C#中调用:
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace AnimationDrawer
{
public partial class Form1 : Form
{
[DllImport("AnimationDrawerCpp.dll", CallingConvention = CallingConvention.StdCall)]
public static extern void DrawStarAt(IntPtr hdc, int x, int y);
public Form1()
{
InitializeComponent();
panel1.MouseClick += new MouseEventHandler(panel1_MouseClick);
}
private void Form1_Load(object sender, EventArgs e)
{
Task.Run(() =>
{
int i = 0;
while (true)
{
int x = new Random().Next(1000);
int y = new Random().Next(1000);
panel1_MouseClick(this, new MouseEventArgs(MouseButtons.Left, 1, x, y, 1));
Thread.Sleep(100);
}
});
}
private void panel1_MouseClick(object sender, MouseEventArgs e)
{
Task.Run(() =>
{
Stopwatch st = new Stopwatch();
st.Start();
Graphics g = panel1.CreateGraphics();
IntPtr hdc = g.GetHdc();
DrawStarAt(hdc, e.X, e.Y);
g.ReleaseHdc(hdc);
g.Dispose();
st.Stop();
Trace.WriteLine($"DrawStarAt {st.ElapsedMilliseconds} ms");
});
}
}
}
这里,我们需要重点说明的是DrawStarAt函数的参数传递中hdc这个句柄的作用:
在 C# 中调用 C++ 方法并传递 Panel 控件的句柄(handle)用于实现跨语言的控件操作或者将 Windows 控件(如 Panel)的底层句柄传递给 C++ 代码进行操作。这里的句柄是一个指向控件在操作系统中唯一标识的标识符,它可以让底层的 C++ 代码直接与控件进行交互,例如绘制、操作、或者执行其他与控件相关的操作。
简单的说:句柄的传递,是C++绘制的代码得以在C#控件上绘制出来的核心,C#授权给C++代码操作控件进行重绘等操作。执行完毕后释放句柄。防止内存泄漏。
健康执行结果:
DrawStarAt 2 ms
DrawStarAt 2 ms
DrawStarAt 2 ms
DrawStarAt 2 ms
DrawStarAt 2 ms
DrawStarAt 3 ms
DrawStarAt 2 ms
DrawStarAt 2 ms
程序“[38600] AnimationDrawer.exe”已退出,返回值为 4294967295 (0xffffffff)。
性能对比与总结
从结果中,我们可以明显看出,双方执行代码的效率差距有多大。是因为,在性能方面,直接在C#中绘制动画会更多地占用UI线程,因为所有的绘制逻辑和动画计算都在C#的UI线程上执行。而通过调用C++函数实现的动画可以将一些计算和绘制的工作转移到C++代码中,从而减少C# UI线程的负担。
C# 在Panel中调用动画:
- 优点:代码编写和调试相对简单,所有逻辑都在C#中,无需处理跨语言调用的问题。
- 缺点:动画绘制直接在UI线程上进行,可以使用异步,但是过于频繁或者绘制耗时时,会阻塞UI线程,影响用户界面的响应速度。
C++ 函数实现的动画:
- 优点:动画逻辑和绘制由C++代码完成,可以提高性能,减少C# UI线程的负担。可以将复杂的计算和绘制逻辑转移到C++中,以减少C#中耗时的操作。
- 缺点:需要编写和调试C++代码,并处理跨语言调用,开发复杂度较高。
通过结合C++和C#,我们可以在WinForms应用中实现高效的动画绘制,提升用户体验。在实际开发中,可以根据项目需求选择合适的方案,以平衡性能和开发效率。如果你的应用也存在可以优化的空间,试试通过C++与C#结合实现高效动画绘制,提升WinForm应用性能,让用户体验更流畅!
如果本文对你有帮助,我将非常荣幸。
如果你对C++辅助C#绘图有其他的看法,欢迎留言交流。
如果你喜欢我的文章,谢谢三连,点赞,关注,转发吧!!!