在 WinForm 中使用 Direct2D

在 C# 的 WinForm 应用中,界面的绘制使用的是 GDI+。不过在一些特别的应用中,可能需要用硬件加速来提高绘制的效率。下面就来介绍两种在 WinForm 应用中嵌入 Direct2D 的方法。

这里所谓的“嵌入”,指的是只有窗口的某一部分应用 Direct2D 绘制(用一些控件承载),而不是整个窗口都使用 Direct2D 绘制。这是一种混合方案,需要用硬件加速的部分由自己来绘制,其它部分仍然可以使用现有的 WinForm 技术。

至于 Direct2D 的类库,我仍然使用 SharpDX 类库,使用 SharpDX.Windows.RenderControl 控件承载 Direct2D 渲染。

一、使用 HwndRenderTarget

HwndRenderTarget(ID2D1HwndRenderTarget interface),在 SharpDX 中对应的类是 WindowRenderTarget,是将窗口句柄(hwnd)作为渲染目标的类,利用它可以非常容易的在窗口中嵌入 Direct2D 渲染。

它的用法非常简单,只要先创建一个 Direct2D 工厂(SharpDX.Direct2D1.Factory),接下来直接创建 WindowRenderTarget 实例,然后就可以使用了。其核心代码如下所示:

// 创建 Direct2D 单线程工厂。
Factory factory = new Factory(FactoryType.SingleThreaded);
// 渲染参数。
RenderTargetProperties renderProps = new RenderTargetProperties
{
    PixelFormat = D2PixelFormat,
    Usage = RenderTargetUsage.None,
    Type = RenderTargetType.Default
};
// 渲染目标属性。
HwndRenderTargetProperties hwndProps = new HwndRenderTargetProperties()
{
    // 承载控件的句柄。
    Hwnd = hwndRenderControl.Handle,
    // 控件的尺寸。
    PixelSize = new Size2(hwndRenderControl.ClientSize.Width, hwndRenderControl.ClientSize.Height),
    PresentOptions = PresentOptions.None
};
// 渲染目标。
hwndRenderTarget = new WindowRenderTarget(factory, renderProps, hwndProps)
{
    AntialiasMode = AntialiasMode.PerPrimitive
};

一般的用法,就是在控件的 Paint 事件中,调用 hwndRenderTarget 的相关方法进行绘制。需要特别注意的是,如果控件的大小发生了改变,必须调用 WindowRenderTarget.Resize 方法,重新调整渲染目标的尺寸才可以,否则会导致绘制结果被拉伸,引起失真。

这个方法的优点就是非常简单易用,而且基本上只要操作系统是 Windows 7 及更高版本,都可以正常绘制(在第 8 行设置 Type = RenderTargetType.Default,会根据情况自动选择硬件渲染或软件渲染),适用范围很广。

其缺点首先是不能在 Windows 8 的 Store app 中使用,其次是与 Direct2D 的一些高级功能不能很好的结合。Direct2D 的很多高级功能,如渲染特效,都是需要与 DeviceContext 结合使用的,而 HwndRenderTarget 不能直接使用 DeviceContext 的渲染结果。

二、使用 DeviceContext

DeviceContext 则是一个比较“万能”的类,它可以将结果绘制到任意的 Image 上,在离线渲染与多线程渲染中是非常有用的。

使用 DeviceContext 来绘制 Direct2D 的做法则要复杂很多,由于 DeviceContext 并不能直接将结果渲染到窗口句柄上,因此需要在 DeviceContext 和 hwnd 之间建立起联系,这里使用的是 DXGI 的 SwapChain

大致的步骤,是先利用 DeviceContext,将结果绘制到一块缓冲区中(BackBuffer 后台缓冲区),然后由 SwapChain 将后台缓冲区的内容,呈现到 hwnd 上,完成一次绘制。

创建时,需要创建 Direct3D Device、DXGI Device 和 Diect2D Device,这样才能创建 DeviceContext。接着再创建 SwapChain 和相应的 Surface 作为缓冲区,才能正常使用。相应的代码如下所示,由于会有很多重名类,因此我用 using 语句定义了很多类型别名,代码看起来会乱一些:

// 创建 Dierect3D 设备。
D3D11Device d3DDevice = new D3D11Device(DriverType.Hardware, DeviceCreationFlags.BgraSupport);
DXGIDevice dxgiDevice = d3DDevice.QueryInterface<D3D11Device1>().QueryInterface<DXGIDevice>();
// 创建 Direct2D 设备和工厂。
D2D1Device d2DDevice = new D2D1Device(dxgiDevice);
this.deviceContext = new DeviceContext(d2DDevice, DeviceContextOptions.None);

// 创建 DXGI SwapChain。
SwapChainDescription swapChainDesc = new SwapChainDescription()
{
    BufferCount = 1,
    Usage = Usage.RenderTargetOutput,
    OutputHandle = dcRenderControl.Handle,
    IsWindowed = true,
    // 这里宽度和高度都是 0,表示自动获取。
    ModeDescription = new ModeDescription(0, 0, new Rational(60, 1), Format),
    SampleDescription = new SampleDescription(1, 0),
    SwapEffect = SwapEffect.Discard
};
this.swapChain = new SwapChain(dxgiDevice.GetParent<Adapter>().GetParent<DXGIFactory>(),
    d3DDevice, swapChainDesc);
// 创建 BackBuffer。
this.backBuffer = Surface.FromSwapChain(this.swapChain, 0);
// 从 BackBuffer 创建 DeviceContext 可用的目标。
this.targetBitmap = new Bitmap1(this.deviceContext, backBuffer);
this.deviceContext.Target = targetBitmap;

绘制同样是在控件的 Paint 事件中绘制的,但要记得在最后调用 swapChain.Present 方法,令 SwapChain 将结果呈现在 hwnd 中。

在改变控件大小时,也要复杂很多。因为在调用 SwapChain 的 ResizeBuffers 方法之前,需要先将与 SwapChain 相关的资源全部释放掉,才能调整缓冲区的尺寸,最后还要重建相关的资源。

使用 DiviceContext 的优点很多,包括:

  • 可以用于 Windows Store apps。
  • DeviceContext 可以绘制到其它目标,只要简单的改变 Target 属性即可。
  • 可以创建 Direct2D 特效。
  • 可以使用多个 DeviceContext 来进行多线程绘制,可以参见这里

在下面的 Demo 中,我也简单的演示了其它 DeviceContext 的绘制结果,可以直接被绘制到界面中。

该方法的缺点,就是创建略微复杂,而且需要电脑在一定程度上支持 Direct3D 才可以(具体要支持到什么程度,还不清楚~)。

关于 Direct2D 特效的应用,可以参见《C# 使用 Direct2D 实现斜角效果》。Direct2D 在 WinForm 中的实际应用,可以参见我的拼图游戏

所有源代码和用到的类库都可以在这里下载。

原文链接:在 WinForm 中使用 Direct2D

时间: 2024-10-21 13:25:16

在 WinForm 中使用 Direct2D的相关文章

C# Winform中执行post操作并获取返回的XML类型的数据

/// <summary> /// 返回指定日期的订单数据 /// </summary> /// <param name="StartDate">起始日期</param> /// <param name="EndDate">结束日期</param> /// <returns>DataTable</returns> public System.Data.DataTable

另一种在WINFORM中使用XNA的方法

之前在写化学分子模型制作程序的时候,使用一种方法,将WINFORM控件嵌入到XNA窗体中,从而实现了即使用WINFORM窗体控件又使用XNA.最近在写另一个物理运动学课件制作程序,同样使用XNA,但从另一个角度实现了WINFORM控件和XNA共存,并且在编码上更简单一些. 一.创建XNA工程并添加窗体 向工程添加窗体MainForm,并修改GAME1为MainGame.我们把XNA内容绘制到MainForm上,其实绘制到任何有句柄的控件都可以,即使我们绘制到桌面也未尝不可.但更少的控件能够使我们

WPF与Winform中的不同(1)

1. 部分控件的Text属性,变成了 Content属性 如: winform中,Button.Text = "abc"; wpf中,Button.Content = "abcd"; 2. wpf中控件的控件属性,由 boolean类型的Visiable 改成 枚举类型的 Visibility winform中,Button.Visable = true (false); wpf中,Button.Visibility = System.Windows.Visibil

C#在WinForm中怎样让多行TEXTBOX的换行

要让一个Windows Form的TextBox显示多行文本就得把它的Multiline属性设置为true.  要让TextBox里面的文本换行大家往往会想到直接在要换行的地方加个转义的换行符"\n": [csharp] view plaincopyprint? this.textBoxDescription.Text = "操作说明\nESC\t最小化\nALT+F4\t退出\nShift+F6\t设置访问地址"; this.textBoxDescription.

C# WinForm中Show与ShowDialog

show()与showDialog()的区别 2010-06-05 14:22:51|  分类: c#学习|举报|字号 订阅 A.WinForm中窗体显示  显示窗体可以有以下2种方法:  Form.ShowDialog方法 (窗体显示为模式窗体)  Form.Show方法 (窗体显示为无模式窗体) 2者具体区别如下:  1.在调用Form.Show方法后,Show方法后面的代码会立即执行  2.在调用Form.ShowDialog方法后,直到关闭对话框后,才执行此方法后面的代码  3.当窗体显

Winform中打开网页页面的方法

1.首先比较简单的我们知道有类似的方法如下 System.Diagnostics.Process.Start("http://www.baidu.com"); 2.比较灵活一点,可以定义窗口大小,我们要实现网页中脚本打开页面的方法,即window.open 那么,我们必然会想,如何调用页面的脚本呢?其实可以利用WebBrowser来实现 //连接 string url ="http://www.baidu.com"; //定义脚本 string script =@&

怎样在winform中上传图片

http://jingyan.baidu.com/article/b7001fe157d6b60e7382dd7f.html 因为WinForm都是运行在本地的,而我们的网站一般都是布署在服务器上,运行在服务器上的,所以在网站上面上传文件,就好似于保存文件到本地.但在WinForm上就不一样了,本章我们简单举一个在WinForm利用WebService上传文件到服务器的方法 首先们先创建一个WebService服务,该服务包含一个UpdateFile方法,该方法接收两个byte[]与string

winform中进行post上传文件

winform中要上传文件到远程的服务器上面,我在本地用的是post方式传递数据,用的是HTTP协议,具体代码如下: 下面的代码就是一个上传的方法,参数需要路径和文件路径就可以了,我本地winform只需要提交post请求就可以了,止于对于post请求如何处理,那就是远程服务端的事情了. private string uploadFile(string uriAddress, string uploadfilePath) { HxSpecCore.SpectrumSet ss = new Spe

在Winform中播放视频等【DotNet,C#】

在项目中遇到过这样的问题,就是如何在Winform中播放视频.当时考察了几种方式,第一种是直接使用Windows Media Player组件,这种最简单:第二种是利用DirectX直接在窗体或者控件上绘图,这种比较复杂.于是采用的是第一种方法. 先从VS的工具箱里添加Windows Media Player组件,方式是打开工具箱,在最下面的空白处点击右键,选择项,然后在弹出的对话框里切换到Com组件标签项,找到“Windows Media Player”项并选中,确定后将在工具箱中看到多出一个