在 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 = ,
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中播放声音的三种方法
转载:WinForm中播放声音的三种方法 金刚 winForm 播放声音 本文是转载的文章.原文出处:http://blog.csdn.net/jijunwu/article/details/4753 ...
- C# Winform 中如何实现音乐播放和视频播放
C# Winform 中如何实现音乐播放和视频播放 namespace WindowsFormsApplication1 { public partial class Form2 : Form { ...
- 另一种在WINFORM中使用XNA的方法
之前在写化学分子模型制作程序的时候,使用一种方法,将WINFORM控件嵌入到XNA窗体中,从而实现了即使用WINFORM窗体控件又使用XNA.最近在写另一个物理运动学课件制作程序,同样使用XNA,但从 ...
- winform中dataGridView单元格根据值设置新值,彻底解决绑定后数据类型转换的困难
// winform中dataGridView单元格在数据绑定后,数据类型更改困难,只能迂回实现.有时候需要将数字变换为不同的文字描述,就会出现int32到string类型转换的异常,借助CellFo ...
- winform中ComboBox实现text和value,使显示和值分开,重写text和value属性
winform的ComboBox中只能赋值text,显示和值是一样的,很多时候不能满足根本需要,熟悉B/S开发的coder最常用的就是text和value分开的,而且web下DropDownList本 ...
- winform中dataGridView隔行显示不同的背景色,鼠标移动上显示不同颜色,离开后变回原色
winform中dataGridView隔行显示不同的背景色,鼠标移动上显示不同颜色,离开后变回原色 先设置奇数行颜色,这个有个自带的属性AlternatingRowsDefaultCellStyle ...
- winform中button点击后再点击其他控件致使button失去焦点,此时button出现黑色边线,去掉黑色边线的方法
winform中button点击后再点击其他控件致使button失去焦点,此时button出现黑色边线,去掉黑色边线的方法 button的FlatAppearence属性下,设置BorderSize= ...
- 【接上一篇】winform中dataGridView高度和宽度自适应填充完数据的高度和宽度,即dataGridView根据数据自适应大小
上一篇:winform中dataGridView高度自适应填充完数据的高度 winform中dataGridView高度自适应填充完数据的高度,就是dataGridView自身不产生滚动条,自己的高度 ...
- C# WinForm 中 MessageBox的使用详解
1.C# WinForm 中 MessageBox的使用详解:http://www.cnblogs.com/bq-blog/archive/2012/07/27/2611810.html
随机推荐
- iOS打印Debug日志的方式
简单介绍以下几个宏: 1) __VA_ARGS__ 是一个可变参数的宏,这个可变参数的宏是新的C99规范中新增的,目前似乎只有gcc支持(VC6.0的编译器不支持).宏前面加上##的作用在于,当可变参 ...
- C# C/S WPF 远程操作服务器上面的文件
作的时候用的是WPF,需要做一个上传附件的功能,服务器上有一个文件夹,附件都上传到里面,只知道URL路径. 文件夹是在服务器上的IIS里面(就比如说你发布一个网站,把文件夹建在网站下面,当然这个网站啥 ...
- js检测浏览器型号
公司要求做内部统计,要求监控客服玩游戏使用的浏览器的型号,是火狐的.谷歌的.还是IE的等等. [code lang="javascript"] /**** * 目前识别范围 * M ...
- nodejs 安装及部署遇到的问题
Error: ENOENT, stat 'C:\Users\PC_Name\AppData\Roaming\npm PC_Name是机器名 解决方法:在Roaming文件夹下创建一个叫npm的空文件夹 ...
- mysql,sql server,oracle 唯一索引字段是否允许出现多个 null 值?
最近一个项目,涉及到sql server 2008,因为业务需求,希望建立一个唯一索引,但是发现在sql server中,唯一索引字段不能出现多个null值,下面是报错信息: CREATE UNIQU ...
- iOS -数据库网络之xml解析之远程解析XML
1.IOS中XML文件获取 //设置远程访问地址 NSURL *url=[NSURL URLWithString:@""]; //创建动态URL请求,并初 ...
- Python2
安装pycharm专业版,不要汉化 要想写的代码支持linux和2.0版本需要在开头加上注释 #/usr/bin/env python #-*- coding:utf-8 -*- 运算符 结果是值 算 ...
- Oracle VirtualBox 使用桥接网络完成主机和虚拟机之间的双向通讯
最近刚换了新的笔记本电脑,终于使用上intel i7处理器,可以使用硬件虚拟化技术安装系统.配置如下: 主机 ThinkPad P50s OS Window 10 虚拟机软件 Orac ...
- oracle 创建字段自增长——两种实现方式汇总
mysql等其他数据库中有随着记录的插入而表ID自动增长的功能,而oracle却没有这样的功能,我们有以下两种方式可以解决字段自增长的功能. 因为两种方式都需要通过创建序列来实现,这里先给出序列的创建 ...
- Linux find/grep命令
一.Find 1)批量删除文件 find . -name "*.h~" -exec rm '{}' \; 2)定位文件某一行 find / -name "demo.con ...