在 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 = ,
Usage = Usage.RenderTargetOutput,
OutputHandle = dcRenderControl.Handle,
IsWindowed = true,
// 这里宽度和高度都是 0,表示自动获取。
ModeDescription = new ModeDescription(, , new Rational(, ), Format),
SampleDescription = new SampleDescription(, ),
SwapEffect = SwapEffect.Discard
};
this.swapChain = new SwapChain(dxgiDevice.GetParent<Adapter>().GetParent<DXGIFactory>(),
d3DDevice, swapChainDesc);
// 创建 BackBuffer。
this.backBuffer = Surface.FromSwapChain(this.swapChain, );
// 从 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

在 WinForm 中使用 Direct2D的更多相关文章

  1. 转载:WinForm中播放声音的三种方法

    转载:WinForm中播放声音的三种方法 金刚 winForm 播放声音 本文是转载的文章.原文出处:http://blog.csdn.net/jijunwu/article/details/4753 ...

  2. C# Winform 中如何实现音乐播放和视频播放

    C#  Winform 中如何实现音乐播放和视频播放 namespace WindowsFormsApplication1 { public partial class Form2 : Form { ...

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

    之前在写化学分子模型制作程序的时候,使用一种方法,将WINFORM控件嵌入到XNA窗体中,从而实现了即使用WINFORM窗体控件又使用XNA.最近在写另一个物理运动学课件制作程序,同样使用XNA,但从 ...

  4. winform中dataGridView单元格根据值设置新值,彻底解决绑定后数据类型转换的困难

    // winform中dataGridView单元格在数据绑定后,数据类型更改困难,只能迂回实现.有时候需要将数字变换为不同的文字描述,就会出现int32到string类型转换的异常,借助CellFo ...

  5. winform中ComboBox实现text和value,使显示和值分开,重写text和value属性

    winform的ComboBox中只能赋值text,显示和值是一样的,很多时候不能满足根本需要,熟悉B/S开发的coder最常用的就是text和value分开的,而且web下DropDownList本 ...

  6. winform中dataGridView隔行显示不同的背景色,鼠标移动上显示不同颜色,离开后变回原色

    winform中dataGridView隔行显示不同的背景色,鼠标移动上显示不同颜色,离开后变回原色 先设置奇数行颜色,这个有个自带的属性AlternatingRowsDefaultCellStyle ...

  7. winform中button点击后再点击其他控件致使button失去焦点,此时button出现黑色边线,去掉黑色边线的方法

    winform中button点击后再点击其他控件致使button失去焦点,此时button出现黑色边线,去掉黑色边线的方法 button的FlatAppearence属性下,设置BorderSize= ...

  8. 【接上一篇】winform中dataGridView高度和宽度自适应填充完数据的高度和宽度,即dataGridView根据数据自适应大小

    上一篇:winform中dataGridView高度自适应填充完数据的高度 winform中dataGridView高度自适应填充完数据的高度,就是dataGridView自身不产生滚动条,自己的高度 ...

  9. C# WinForm 中 MessageBox的使用详解

    1.C# WinForm 中 MessageBox的使用详解:http://www.cnblogs.com/bq-blog/archive/2012/07/27/2611810.html

随机推荐

  1. JAVA基础学习day20--IO流二-缓冲流、字节流

    一.缓冲流 1.1.字符流的缓冲区 缓冲区的出现是为了提高IO的读写效率 对应类 BufferedReader BufferedWriter 缓冲区要结合流才可以使用 在流的基础上对流的功能进行了增强 ...

  2. IOS开发关于测试的好的网址资源

    1. 高级自动化单元测试,推荐看LeanCloud 工程师的李智维的自动化单元测试的直播录影李智维的演示github 2.iOS开发-单元测试 这只是一篇简单的ios测试介绍 3.iOS单元测试 来自 ...

  3. ubuntu wireshark finish

    http://blog.csdn.net/cumirror/article/details/4694283 安装编译工具: $sudo apt-get install build-essential ...

  4. Effective Java 18 Prefer interfaces to abstract classes

    Feature Interface Abstract class Defining a type that permits multiple implementations Y Y Permitted ...

  5. Effective Java 26 Favor generic types

    Use generic types to replace the object declaration Add one or more type parameters to its declarati ...

  6. 修复 Java 内存模型,第 2 部分——Brian Goetz

    转自Java并发大师Brain Goetz:http://www.ibm.com/developerworks/cn/java/j-jtp03304/ (中文地址) http://www.ibm.co ...

  7. 在Windows8下安装SQL Server 2005无法启动服务

    因为尝鲜安装了Windows8,的确很不错,唯一的遗憾就是不支持Sql Server 2005的安装.找了很多办法,基本上都有缺陷.现在终于找到一种完全正常没有缺陷的办法了,和大家分享一下. 0.一定 ...

  8. c++基础回顾

    #include <iostream> #include <vector> #include <string> int main(int argc, const c ...

  9. Android之TelephonyManager类的方法详解

    TelephonyManager类主要提供了一系列用于访问与手机通讯相关的状态和信息的get方法.其中包括手机SIM的状态和信息.电信网络的状态及手机用户的信息.在应用程序中可以使用这些get方法获取 ...

  10. 汇编中call printf参数压栈时错误理解

    EAX, ECX,EDX,EBX均可以32bit,16bit,8bit访问,如下所示: <-------------------EAX------------------------>|& ...