1. 程式人生 > >【Win 10 應用開發】UI Composition 劄記(四):繪制圖形

【Win 10 應用開發】UI Composition 劄記(四):繪制圖形

圖形 package 記得 aml 3.5 平時 surf 繪圖 str

使用 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,今天的內容就說到這裏了。

【Win 10 應用開發】UI Composition 劄記(四):繪制圖形