本文告诉大家如何在 WPF 使用 SharpDX 做绘制,本文是入门级博客
本文是一个系列
先介绍一下 SharpDx ,一个底层封装的 DirectX 库,支持 AnyCpu ,支持 Direct3D9, Direct3D11, Direct3D12,Direct2D1。支持 win32 程序和商店程序。
环境
需要 .NET 4.5 和以上的环境才可以使用。
安装
首先安装 SharpDX 的库,需要安装下面几个库
创建工厂
使用 SharpDX 和 DirectX 一样,开始都需要创建工厂,然后创建RenderTarget,之后才可以显示基础图形。
先引用命名
using D2D = SharpDX.Direct2D1;using WIC = SharpDX.WIC;using DW = SharpDX.DirectWrite;using DXGI = SharpDX.DXGI;
需要在 Loaded 之后添加代码
var factory = new D2D.Factory();
创建 RenderTarget
创建 RenderTarget 可以尝试 WindowRenderTarget ,因为是入门博客,我不告诉大家如何使用其他几个 RenderTarget ,如果想知道,请自己多去看博客。
创建 WindowRenderTarget 需要参数 RenderTargetProperties ,HwndRenderTargetProperties。所以需要先创建这两个。
创建 RenderTargetProperties 需要参数 PixelFormat ,请看下面
var pixelFormat = new D2D.PixelFormat(DXGI.Format.B8G8R8A8_UNorm, D2D.AlphaMode.Straight);
var renderTargetProperties = new D2D.RenderTargetProperties(D2D.RenderTargetType.Default, pixelFormat, 96, 96, D2D.RenderTargetUsage.None, D2D.FeatureLevel.Level_DEFAULT);
RenderTargetProperties 需要的参数是 RenderTargetType ,PixelFormat,dpiX,dpiY,RenderTargetUsage,FeatureLevel,参数大家看命名就知道是做什么的,在这里就不告诉大家。
创建 HwndRenderTargetProperties 请看下面代码
var hwndRenderTargetProperties = new D2D.HwndRenderTargetProperties(); hwndRenderTargetProperties.Hwnd = new WindowInteropHelper(this).Handle;
现在尝试创建 RenderTarget 请看代码
var renderTarget = new D2D.WindowRenderTarget(factory, renderTargetProperties, hwndRenderTargetProperties);
因为需要拿到 RenderTarget 进行画基础图形,一般把 RenderTarget 放在字段。
public MainWindow() { InitializeComponent();
Loaded += (s, e) => { var factory = new D2D.Factory();
var pixelFormat = new D2D.PixelFormat(DXGI.Format.B8G8R8A8_UNorm, D2D.AlphaMode.Straight);
var hwndRenderTargetProperties = new D2D.HwndRenderTargetProperties(); hwndRenderTargetProperties.Hwnd = new WindowInteropHelper(this).Handle; hwndRenderTargetProperties.Hwnd = new WindowInteropHelper(this).Handle; hwndRenderTargetProperties.PixelSize = new Size2((int)ActualWidth, (int)ActualHeight);
var renderTargetProperties = new D2D.RenderTargetProperties(D2D.RenderTargetType.Default, pixelFormat, 96, 96, D2D.RenderTargetUsage.None, D2D.FeatureLevel.Level_DEFAULT);
_renderTarget = new D2D.WindowRenderTarget(factory, renderTargetProperties, hwndRenderTargetProperties); }; }
private D2D.RenderTarget _renderTarget;
这里的 PixelFormat 使用 B8G8R8A8_UNorm 的意思是每个元素包含4个8位无符号分量,分量的取值范围在[0,1]区间内的浮点数,因为不是任何类型的数据都能存储到纹理中的,纹理只支持特定格式的数据存储。这里的 BGRA 的意思分别是 蓝色(Blue)、绿色(Green)、红色(Red)和 alpha(透明度),其他可以选的格式
- DXGI_FORMAT_R32G32B32_FLOAT:每个元素包含3个32位浮点分量。
- DXGI_FORMAT_R16G16B16A16_UNORM:每个元素包含4个16位分量,分量的取值范围在[0,1]区间内。
- DXGI_FORMAT_R32G32_UINT:每个元素包含两个32位无符号整数分量。
- DXGI_FORMAT_R8G8B8A8_UNORM:每个元素包含4个8位无符号分量,分量的取值范围在[0,1]区间内的浮点数。
- DXGI_FORMAT_R8G8B8A8_SNORM:每个元素包含4个8位有符号分量,分量的取值范围在[−1,1] 区间内的浮点数。
- DXGI_FORMAT_R8G8B8A8_SINT:每个元素包含4个8位有符号整数分量,分量的取值范围在[−128, 127] 区间内的整数。
- DXGI_FORMAT_R8G8B8A8_UINT:每个元素包含4个8位无符号整数分量,分量的取值范围在[0, 255]区间内的整数
更多概念请看DirectX11 Direct3D基本概念 - CSDN博客
画圈
现在就和 WPF 使用 Direct2D1 画图入门 差不多方法来画圈,如何可以画出来,那么就是成功使用 SharpDX 。不要问我为什么用画圈来判断是否可以使用 SharpDX,因为在所有基础的 draw 只有椭圆最耗性能。
还是使用 CompositionTarget 来知道什么时候刷新,在函数添加下面代码
CompositionTarget.Rendering += CompositionTarget_Rendering;
private void CompositionTarget_Rendering(object sender, EventArgs e) { }
因为画椭圆需要三个参数,第一个是 D2D.Ellipse ,第二个是 Brush ,第三个是线条宽度。
因为 Brush 需要使用刚才的工厂创建,如果不使用工厂创建会异常
先创建 SolidColorBrush 然后创建 D2D.Ellipse
var ellipse = new D2D.Ellipse(new RawVector2(100,100),10,10 );
var brush = new D2D.SolidColorBrush(_renderTarget, new RawColor4(1, 0, 0, 1));
Ellipse 的三个参数是圆心和两个半径,上面是画出半径是 10 的圆。
RawColor4 就是 rgba ,颜色是从 0 到 1 ,对应 WPF 的 RGB 从 0 到 255 ,所以需要转换。
准备好几个参数,可以尝试画出来,在画之前需要使用 BeginDraw 。为什么需要调用这个函数,因为实际上调用 Draw 是不会立刻画出来,而是创建绘制命令,如果渲染是 CPU 渲染,那么就会根据命令让 CPU 在内存渲染。如果渲染 GPU 渲染,就会把命令发到 GPU ,让他渲染。
private void CompositionTarget_Rendering(object sender, EventArgs e) { var ellipse = new D2D.Ellipse(new RawVector2(100, 100), 10, 10);
var brush = new D2D.SolidColorBrush(_renderTarget, new RawColor4(1, 0, 0, 1)); _renderTarget.BeginDraw();
_renderTarget.DrawEllipse(ellipse, brush, 1);
_renderTarget.EndDraw(); }
重新告诉大家如何画出。首先拿到窗口,在 WPF 能创建的 WindowRenderTarget 最简单是拿到窗口。因为通过几个属性设置如何渲染,在哪渲染,所以还需要多使用几个属性才可以创建 D2D.WindowRenderTarget 。因为需要一个时机对 WindowRenderTarget 画出,所以我就使用 CompositionTarget 对他进行画出。
上面很多参数都没有详细说明,具体请看这位大佬的博客
简化一下的全部代码如下
using PInvoke;
using SharpDX;using SharpDX.Direct2D1;
using System.Windows;using System.Windows.Interop;using SharpDX.Mathematics.Interop;using D2D = SharpDX.Direct2D1;using DXGI = SharpDX.DXGI;using System.Windows.Media;using System.Reflection;
namespace LifafaheqearNearkairliraywal;
public class Program{ [STAThread] public static void Main(string[] args) { Application application = new Application(); application.Startup += (s, e) => { application.MainWindow.Show(); };
Window window = new Window(); D2DRender render = new D2DRender(); window.Loaded += (s, e) => { render.Init(window); };
application.MainWindow=window; application.Run(); }}
class D2DRender{ public void Init(Window window) { _window = window;
var factory = new D2D.Factory();
var pixelFormat = new D2D.PixelFormat(DXGI.Format.B8G8R8A8_UNorm, D2D.AlphaMode.Ignore);
var renderTargetProperties = new D2D.RenderTargetProperties ( // 默认的行为就是尝试使用硬件优先,否则再使用软件 D2D.RenderTargetType.Default, // 像素格式,对于当前大多数显卡来说,选择 B8G8R8A8 是完全能支持的 // 而且也方便和其他框架,如 WPF 交互 pixelFormat, dpiX: 96, dpiY: 96, D2D.RenderTargetUsage.None, D2D.FeatureLevel.Level_DEFAULT ); var hwndRenderTargetProperties = new D2D.HwndRenderTargetProperties(); hwndRenderTargetProperties.Hwnd = new WindowInteropHelper(window).Handle; ActualWidth = (int)window.ActualWidth; ActualHeight = (int)window.ActualHeight; hwndRenderTargetProperties.PixelSize = new Size2(ActualWidth, ActualHeight);
var renderTarget = new D2D.WindowRenderTarget(factory, renderTargetProperties, hwndRenderTargetProperties); _renderTarget = renderTarget;
window.SizeChanged -= Window_SizeChanged; window.SizeChanged += Window_SizeChanged;
AddRendering(); }
private int ActualWidth { set; get; } private int ActualHeight { set; get; }
private void AddRendering() { CompositionTarget.Rendering -= CompositionTarget_Rendering; CompositionTarget.Rendering += CompositionTarget_Rendering; }
public void CompositionTarget_Rendering(object? sender, EventArgs e) { Render(); }
private void Window_SizeChanged(object sender, SizeChangedEventArgs e) { ArgumentNullException.ThrowIfNull(_window); ArgumentNullException.ThrowIfNull(_renderTarget);
var window = _window;
ActualWidth = (int) window.ActualWidth; ActualHeight = (int) window.ActualHeight;
_renderTarget.Resize(new Size2(ActualWidth, ActualHeight)); }
public void Render() { var renderTarget = _renderTarget; if (renderTarget == null) { throw new InvalidOperationException(); }
renderTarget.BeginDraw();
renderTarget.Clear(new RawColor4(0.2f,0.5f,0.5f,1));
var width = Random.Shared.Next(100, 200); var height = width; var maxWidth = ActualWidth - width; var maxHeight = ActualHeight - height;
var x = Random.Shared.Next(width, maxWidth); var y = Random.Shared.Next(height, maxHeight);
var ellipse = new D2D.Ellipse(new RawVector2(x, y), width, height);
using var brush = new D2D.SolidColorBrush(_renderTarget, new RawColor4(1, 0, 0, 1));
renderTarget.FillEllipse(ellipse, brush);
renderTarget.EndDraw(); }
private D2D.WindowRenderTarget? _renderTarget; private Window? _window;}
可以通过如下方式获取本文的源代码,先创建一个空文件夹,接着使用命令行 cd 命令进入此空文件夹,在命令行里面输入以下代码,即可获取到本文的代码
git initgit remote add origin https://gitee.com/lindexi/lindexi_gd.gitgit pull origin a582f328a967af32635cba6d08720341431c9c24
以上使用的是 gitee 的源,如果 gitee 不能访问,请替换为 github 的源。请在命令行继续输入以下代码
git remote remove origingit remote add origin https://github.com/lindexi/lindexi_gd.gitgit pull origin a582f328a967af32635cba6d08720341431c9c24
获取代码之后,进入 LifafaheqearNearkairliraywal 文件夹

原文链接: http://blog.lindexi.com/post/WPF-%E4%BD%BF%E7%94%A8-SharpDX
本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。 欢迎转载、使用、重新发布,但务必保留文章署名 林德熙 (包含链接: https://blog.lindexi.com ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我 联系。