【Win 10 应用开发】UI Composition 札记(四):绘制图形

使用 Win 2D 组件,就可以很轻松地绘制各种图形,哪怕你没有 D2D 相关基础,也不必写很复杂的 C++ 代码。

先来说说如何获取 Win 2D 组件。很简单,创建 UWP 应用项目后,你打开“解决方案资源管理器”窗口,然后在【引用】节点上右击,从快捷菜单中选择【管理 Nuget 程序包】命令,在打开的窗口中搜索“Win 2D”,然后安装带有 uwp 标识的那个就可以了。

顺便说一下,nuget 的包缓存在你的用户文件夹下,就是系统盘下的 \users\xxx,xxx是你登录系统的用户名,在文件夹下有个 .nuget 目录,\packages 子目录下就是缓存的包,大小取决你安装的组件,大的时候 4、5 个G也有的。

在你的应用项目中,VS 只是创建了一个 JSON 文件来描述你引用的组件,Win 2D 添加引用成功后,你的引用列表应该是这样的。

你如果看到 Win2D.uwp 这个项目,那就没问题了。

不过,你得注意,直接双击它是无法在“对象浏览器”中查看的,你可以这样:打开“对象浏览器”窗口,然后把浏览的子集改为“我的解决方案”,这样,你就能看到你当前项目中所有引用的组件的类型结构了。

现在,你就能看到 Win2D 库的基本内容了。

Microsoft.Graphics.Canvas 以及它的子命名空间都是 Win2D 组件中的类型。

其实,使用 Win2D 组件,你完全可以很简单地绘制各种玩意儿,因为在 Microsoft.Graphics.Canvas.UI.Xaml 命名空间下,直接就提供了一些控件,你可以直接用到 XAML 文档中,然后要画什么就用代码画就行了。比如,我介绍两个比较典型的。

* CanvasControl —— 可以绘制各种你想要的东东,它就相当于一块画布,用代码绘制时须处理 Draw 事件,然后就在事件处理代码中随便 draw。

* CanvasAnimatedControl —— 跟上面的那个家伙差不多,只是它可以在你绘制的内容上产生动画。

大伙会看到,这两个控件都有一个 CreateResources 事件,用来干什么鸟的呢?它的作用是这样的,你可以在处理这个事件的代码中实例化一些资源,这些资源一般在绘制过程不会频繁改动的,比如加载的某个图片,某个画刷等。

下面给大伙简单演示一下 CanvasControl 控件的用法。

在 XAML 文档中先要引入命名空间。

<Page
    ……    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    ……
    xmlns:canvas="using:Microsoft.Graphics.Canvas.UI.Xaml">

</Page>

然后就可以用了。

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <canvas:CanvasControl Draw="OnDraw"/>
    </Grid>

接着处理 Draw 事件,我们画上几笔试试手。要画东东,我们要用到 Microsoft.Graphics.Canvas 命名空间下的另一个类——CanvasDrawingSession,它公开了许多 Draw 和 Fill 方法,Draw 是画出某个东东,Fill 是填充一个区域。

        private void OnDraw(Microsoft.Graphics.Canvas.UI.Xaml.CanvasControl sender, Microsoft.Graphics.Canvas.UI.Xaml.CanvasDrawEventArgs args)
        {
            using (CanvasDrawingSession dss = args.DrawingSession)
            {
                // 画线
                dss.DrawLine(12f, 15f, 335f, 408f, Colors.DarkBlue, 5.5f);
                // 画矩形
                dss.DrawRectangle(55f, 190f, 465f, 369f, Colors.Gold, 8f);
                // 画圆
                dss.DrawEllipse(350f, 350f, 200f, 260f, Colors.Orange, 5f);
            }
        }

随便画几下,纯属鬼画符。效果如下图所示。

但是,我们今天的主题是跟 UI Composition 有关的,虽然上述绘图法很超逸,却不是咱们今天的主题。我们下面要做的,是使用 Composition API 来呈现自己绘制的内容。

大伙伴们可以思考一下,要在 Composition 组装的对象上画东西,需要什么?前面咱们说过,你得有个 Brush,在 Composition API 中,只有一个画刷可以呈现自己绘制的内容,那就是 CompositionSurfaceBrush。

要创建 CompositionSurfaceBrush 实例容易,调用一下 Compositor.CreateSurfaceBrush 方法就行了。但问题的核心不在此,而在于,CompositionSurfaceBrush 画刷创建后是空的,要能够呈现内容,还得需要给 Surface 属性赋个值,它是一个实现了 ICompositionSurface 接口的类型,在 Composition API 中,实现了该接口的只有一个类:CompositionDrawingSurface。然而,你看到了,这个类是没有构造函数公开的,它是由 CompositionGraphicsDevice 类的 CreateDrawingSurface 方法创建的。

好,现在我们可以理一下思路了。

1、你必须想方设法得到一个 CompositionGraphicsDevice 实例,可该类没公开的构造函数,咋办?所以才要使用 Win2D,稍后再说,Win2D 会有办法获得这个实例的。

2、调用 CompositionGraphicsDevice 实例的 CreateDrawingSurface 或 CreateDrawingSurface2 方法创建 CompositionDrawingSurface

实例。

3、在新创建的 CompositionDrawingSurface 实例上画东西。这个也要用到 Win2D。

4、使用 Compositor 的静态方法直接创建 CompositionSurfaceBrush 对象,并与上面画好的 CompositionDrawingSurface 关联。

5、把这个画刷(CompositionSurfaceBrush)与可视化元素关联,比如,SpriteVisual类就有一个 Brush 属性。

如此一来,我们找到了两个难点:a、如何创建 CompositionGraphicsDevice ; b、如何在 Surface 上画东西(此 Surface 非彼 Surface)。

借助 Win2D,可以解决以上两个难题。我们不讲理论的,下面用实例来说明。

首先,在 XAML 文档中随便声明一个元素,只要是 UIElement 的子类就行。

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <Canvas Name="myCvs"/>
    </Grid>

记得分配一个名字,待会在代码中要访问。

随后,我们就可以开始作画了。

        void DrawSomething()
        {
            // 获取 XAML 元素上的 Visual
            Visual canvasVisual = ElementCompositionPreview.GetElementVisual(myCvs);
            // 获取 Compositor 对象
            Compositor compos = canvasVisual.Compositor;
            // 创建 GraphicsDevice
            CompositionGraphicsDevice graphicsDevice = CanvasComposition.CreateCompositionGraphicsDevice(compos, CanvasDevice.GetSharedDevice());
            // 创建 Surface
            // 画布的大小
            SizeInt32 cvsize = new SizeInt32
            {
                Width = 600,
                Height = 400
            };
            CompositionDrawingSurface surface = graphicsDevice.CreateDrawingSurface2(cvsize, Windows.Graphics.DirectX.DirectXPixelFormat.B8G8R8A8UIntNormalized, Windows.Graphics.DirectX.DirectXAlphaMode.Premultiplied);
            // 开始绘画
            using (CanvasDrawingSession dss = CanvasComposition.CreateDrawingSession(surface))
            {
                // 刷墙,把墙面刷成黑色
                dss.Clear(Colors.Black);
                // 画一个圆
                dss.DrawEllipse(210f, 190f, 80f, 80f, Colors.Yellow, 4f);
                // 再画一个椭圆
                dss.DrawEllipse(230f, 200f, 160f, 95f, Colors.SkyBlue, 3.5f);
                // 填充一块区域
                dss.FillRectangle(400f, 250f, 150f, 100f, Colors.Pink);
            }
            // 创建 surface 画刷
            CompositionSurfaceBrush sfbrush = compos.CreateSurfaceBrush(surface);
            // 创建一个支持画刷的可视化对象
            SpriteVisual newVisual = compos.CreateSpriteVisual();
            // 设置画刷
            newVisual.Brush = sfbrush;
            // 注意要设置可视化对象的大小
            ExpressionAnimation anim = compos.CreateExpressionAnimation();
            anim.Expression = "parn.Size";
            anim.SetReferenceParameter("parn", canvasVisual);
            newVisual.StartAnimation("Size", anim);
            // 最后别忘了把新的可视化对象插入对象树
            ElementCompositionPreview.SetElementChildVisual(myCvs, newVisual);
        }

我们上篇中讲过的,与 XAML 交互,使用 ElementCompositionPreview辅助类可以获得与 XAML 元素对应的 Visual 实例,这样我们也能得到相关的 Compositor 对象了。

注意创建 CompositionGraphicsDevice 实例要借助 Win2D 的 CanvasComposition 类(位于 Microsoft.Graphics.Canvas.UI.Composition 命名空间),在调用 CreateCompositionGraphicsDevice 方法时,你除了得提供关联的 Compositor 对象外,还得有一个兼容的 CanvasDevice 对象。这个 CanvasDevice 对象有个很TMD简单的获取方法,就是直接调用它的 GetSharedDevice 静态方法。

好了,有了 CompositionGraphicsDevice 实例,那创建 CompositionDrawingSurface对象就好办了,就像这样。

 CompositionDrawingSurface surface = graphicsDevice.CreateDrawingSurface2(cvsize, Windows.Graphics.DirectX.DirectXPixelFormat.B8G8R8A8UIntNormalized, Windows.Graphics.DirectX.DirectXAlphaMode.Premultiplied);

CreateDrawingSurface 和 CreateDrawingSurface2 都可以,带“2”的是使用整数来表示像素值。

调用这个方法最麻烦的后面两个参数,它们都是枚举值,如果值设置不当会发生异常,所以,如果出错了,你就得调整了。一般来说,在屏幕上显示的东东,我们会选择 BGRA 的顺序,因为这个顺序呈现出来不会偏色,尤其是图像,如果用 RGBA 就会偏色。B8G8R8A8 表示都用8位的值,也就是一个字节,这个我们平时处理一般图形也够用了(即32位颜色)。

接下来就是用 CanvasDrawingSession 来画你想画的各种玩意儿,画完后要创建一个 CompositionSurfaceBrush 对象,并且要与刚刚画好的 surface 对象关联。

最后用这个画刷填充一个支持画刷的可视化对象即可,如 SpriteVisual。记住,一定要设置对象的 Size 属性,因为默认值是0,不然它不会显示出来的,这里呢,我直接用一个表达式动画,让它的大小跟随 Canvas 的大小。

这个方法封装好后,可以在适当的地方调用,以绘制内容,比如页面类的构造函数中。

        public MainPage()
        {
            this.InitializeComponent();
            DrawSomething();
        }

好了,看看效果吧。

OK,今天的内容就说到这里了。

时间: 2024-08-29 05:45:30

【Win 10 应用开发】UI Composition 札记(四):绘制图形的相关文章

【Win 10 应用开发】打印UI元素

原文:[Win 10 应用开发]打印UI元素 Windows App支持将UI界面进行打印的功能,这与浏览器中的打印网页的用途相近,其好处就是“所见即所得”,直接把界面上呈现的内容打印下来,比重新创建打印图像方便得多. 要在通用App中实现打印,主要依靠以下几个类型: PrintManager:位于Windows.Graphics.Printing命名空间,主要负责显示打印对话框,设置打印源等操作.在使用时,首先调用GetForCurrentView静态方法得到一个PrintManager实例:

【Win 10应用开发】如何知道当前APP在哪个平台设备上运行

[Win 10应用开发]如何知道当前APP在哪个平台设备上运行 在做Win10开发的时候,我们可能经常会需要获得当前程序在在哪个平台设备上运行,用于UI和相关API的调用,那么可以通过什么方式知道当前APP运行的平台呢? 今天这里提供两个方法给大家做参考: 方法一:DeviceFamily 通过Windows.System.Profile.AnalyticsInfo.VersionInfo.DeviceFamily,来获取当前的平台设备,目前只可以得到两个值Windows.Mobile或Wind

【Win 10应用开发】实现全屏播放的方法

原文:[Win 10应用开发]实现全屏播放的方法 有人会问,以前的MediaElement控件不是有现成的一排操作按钮吗?而且可以直接进入全屏播放.是的,我们知道,以往的Store App都是在全屏模式下运行的,只要MediaElement控件填满整个窗口,就等于全屏播放了,但是,Win10应用是窗口化的,将MediaElement控件的IsFullWindow属性设置为true后,就会这样: 从上面的截图看,MediaElement控件只是覆盖整个窗口而已,并没有实现全屏.那有办法让它全屏播放

【Win 10 应用开发】Toast通知激活应用——前台&amp;后台

原文:[Win 10 应用开发]Toast通知激活应用--前台&后台 老周最近热衷于讲故事,接下来还是讲故事时间. 有人问我:你上大学的时候,有加入过学生会吗?读大学有没有必要加入学生会? 哎哟,这怎么回答呢,从短期来说,加入学生会有点用,至少可以娱乐一下,运气好的话,说不定能遇到红颜知己,但这概率相当低.从长远发展看嘛,是没什么用.老周当年读了四年本科,在学生会混了四年,什么名堂也没混出来. 一方面老周向来不求虚名,所以也没去参选所谓的什么部长.主席之类的,这些“官衔”听起来很高大上,实际上很

【Win 10应用开发】如何知道UAP在哪个平台上运行

原文:[Win 10应用开发]如何知道UAP在哪个平台上运行 面向22世纪的现代化应用程序可以同时在多种设备上运行,于是有朋友会有一个疑问:有时候,我们还真的需要判断一下,UAP应用程序在哪个平台上运行.尽管大多情况下我们不必要这样做,但某些特殊情况还得考虑.比如一串数据列表,我希望如果在桌面上运行时就以横向列表展现:但要是运行在手机上就以纵向列表展现. 也就是说,其实我们只需分析两种情况即可: 一.桌面.(台式机.笔记本.平板.游戏机.发广告专用机.导航器……) 二.移动环境.其实就是手机.

【Win 10 应用开发】RTM版的UAP项目解剖

原文:[Win 10 应用开发]RTM版的UAP项目解剖 Windows 10 发布后,其实SDK也偷偷地在VS的自定义安装列表中出现了,今天开发人员中心也更新了下载.正式版的SDK在API结构上和以前预览的时候是一样的,只是版本变成10240罢了,所以大家不要问老周有什么新的API. API虽然没变,但VS中的应用程序项目是有了新变化.毕竟以前都是预览的,而现在是“正规军”,以前练兵时都用土豆枪,现在都是真刀真枪干了,故而应用程序项目是有变化的. 以前老周跟大家讲的修改项目模板,去掉遥测类库的

iOS开发UI篇—Quartz2D使用(图形上下文栈)

iOS开发UI篇-Quartz2D使用(图形上下文栈) 一.qurza2d是怎么将绘图信息和绘图的属性绘制到图形上下文中去的? 说明: 新建一个项目,自定义一个view类和storyboard关联后,重写该类中的drowrect方法. 画线的三个步骤: (1)获取上下文 (2)绘图 (3)渲染 要求:画两条单独的线 代码和效果图: 1 - (void)drawRect:(CGRect)rect 2 { 3 //获取上下文 4 CGContextRef ctx=UIGraphicsGetCurre

【Win 10 应用开发】UI Composition 札记(三):与 XAML 集成

除了 DirectX 游戏开发,我们一般很少单独使用 UI Composition ,因此,与 XAML 互动并集成是必然结果.这样能够把两者的优势混合使用,让UI布局能够更灵活. 说到与 XAML 的集成,则我们必须先认识一位伙计,他非常重要,位于 Windows.UI.Xaml.Hosting 命名空间下,名叫 ElementCompositionPreview ,有了它,我们才可以在 XAML 元素与 Composition UI 元素之间游走.来看看它都公开了哪些成员. public s

【Win 10 应用开发】UI Composition 札记(一):视图框架的实现

在开始今天的内容之前,老周先说一个问题,这个问题记得以前有人提过的. 设置 Windows.ApplicationModel.Core.CoreApplicationView.TitleBar.ExtendViewIntoTitleBar 属性可以让应用窗口中的内容扩展到标题栏.简单地说,就是你的UI区域可以扩大,并填充到标题栏,这在开发自定义标题栏或弄个什么毛玻璃效果时很有用. 不过,这个 ExtendViewIntoTitleBar 属性有个“八阿哥”,一旦你设置之后,系统会对其进行记录,很