【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,今天的內容就說到這裏了。
【Win 10 應用開發】UI Composition 劄記(四):繪制圖形