共用方式為


教學:打造一個簡單的 Win2D 應用程式

本教學課程介紹 Win2D 的一些基本繪圖功能。 您將瞭解如何:

  • 把 Win2D 加入 WinUI(C#)應用程式。
  • 繪製文字和幾何。
  • 套用篩選效果。
  • 以動畫顯示您的 Win2D 內容。
  • 遵循 Win2D 最佳做法。

請參考「Win2D NuGet」套件

  1. 建立一個新的 WinUI 應用程式,並新增 Microsoft.Graphics.Win2D Nuget 套件。

將 Win2D CanvasControl 新增至應用程式的 XAML

  1. 若要使用 Win2D,您需要在某處繪製圖形。 在 XAML 應用程式中,最簡單的方法是在您的 XAML 頁面上新增一個 CanvasControl

在繼續之前,請先確認專案的架構選項設定為 x86x64,而不是 Any CPU。 Win2D 會在 C++中實作,因此使用 Win2D 的項目必須以特定 CPU 架構為目標。

  1. 在 Solution Explorer 中雙擊MainWindow.xaml,以移動到你的專案。 這會開啟檔案。 為了方便起見,您可以按兩下 [設計工具] 索引標籤中的 [XAML] 按鈕;這會隱藏可視化設計工具,並保留程式代碼檢視的所有空間。

  2. 新增控制項之前,您必須先告訴 XAML CanvasControl 的定義位置。 要做到這一點,請前往 Window 元素的定義,並加入這個指令:xmlns:canvas="using:Microsoft.Graphics.Canvas.UI.Xaml"。 您的 XAML 現在看起來應該像這樣:

<Window
    ...
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:canvas="using:Microsoft.Graphics.Canvas.UI.Xaml"
    mc:Ignorable="d">
  1. 現在,將 canvas:CanvasControl 作為子元素新增到根 Grid 元素。 為控件指定名稱,例如「canvas」。 您的 XAML 現在看起來應該像這樣:
<Grid>
    <canvas:CanvasControl x:Name="canvas"/>
</Grid>
  1. 接著,為 Draw 事件定義事件處理器。 每當您的應用程式需要繪製或重新繪製其內容時,CanvasControl 就會引發Draw。 最簡單的方法是讓 Visual Studio 自動完成來協助你。 在 CanvasControl 定義中,開始輸入事件處理程式的新屬性 Draw
<canvas:CanvasControl x:Name="canvas" Draw="canvas_Draw" />

在 Win2D 中繪製您的第一個字

  1. 現在,讓我們前往 C# 的後端程式碼。 從Solution Explorer開啟MainWindow.xaml.cs

  2. C# 檔案頂端有各種命名空間定義。 新增下列命名空間:

using Windows.UI;
using System.Numerics;
using Microsoft.Graphics.Canvas;
using Microsoft.Graphics.Canvas.Effects;
  1. 接下來,您應該會看到下列由 AutoComplete 插入的空白事件處理程式:
private void canvas_Draw(
    Microsoft.Graphics.Canvas.UI.Xaml.CanvasControl sender,
    Microsoft.Graphics.Canvas.UI.Xaml.CanvasDrawEventArgs args)
{
}

(如果您在上一個步驟中未使用 AutoComplete,請立即新增此程序代碼。

  1. CanvasDrawEventArgs參數會暴露一個成員,DrawingSession,其類型為 CanvasDrawingSession。 這堂課提供了 Win2D 中大部分基本繪圖功能:它有 CanvasDrawingSession.DrawRectangleCanvasDrawingSession.DrawImage,以及繪製文字所需的方法 CanvasDrawingSession.DrawText

將下列程式碼加入 canvas_Draw 方法:

args.DrawingSession.DrawText("Hello, World!", 100, 100, Colors.Black);

第一個自變數 "Hello, World!"是您想要 Win2D 顯示的字串。 兩個 「100」 會告訴 Win2D 將文字向右和向下位移 100 個 DIP(與裝置無關的圖元)。 最後,Colors.Black 定義文字的色彩。

  1. 現在您已準備好執行您的第一個 Win2D 應用程式。 按 F5 鍵進行編譯和啟動。 您應該會看到一個空白的視窗,上面有黑色的“Hello, world!”。

正確處置 Win2D 資源

  1. 在繼續繪製其他類型的內容之前,您應該先新增一些程式代碼,以確保您的應用程式避免記憶體流失。 大多數以.NET語言撰寫並使用 Win2D 控制項如 CanvasControl 的 Win2D 應用程式,都需要遵循以下步驟。 嚴格來說,您簡單的 「Hello, world」 應用程式不會受到影響,但這是一般遵循的好作法。

如需詳細資訊,請參閱 避免記憶體流失

  1. 打開 MainWindow.xaml 並找到包含你 CanvasControl 的 XAML 元素。 它應是檔案中的第一個元素。

  2. 新增 Unloaded 事件的處理程式。 您的 XAML 看起來應該像這樣:

<Page
    ...
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:canvas="using:Microsoft.Graphics.Canvas.UI.Xaml"
    mc:Ignorable="d"
    Unloaded="Page_Unloaded">
  1. 移至 MainWindow.xaml.cs 並尋找 Page_Unloaded 事件處理程式。 新增下列程式碼:
void Page_Unloaded(object sender, RoutedEventArgs e)
{
    this.canvas.RemoveFromVisualTree();
    this.canvas = null;
}
  1. 如果您的應用程式包含多個 Win2D 控制件,則您必須針對包含 Win2D 控制件的每個 XAML 頁面重複上述步驟。 您的應用程式目前只有一個 CanvasControl,這樣就足夠了。

繪製一些圖形

  1. 將 2D 幾何新增至您的應用程式同樣容易。 將下列程式碼新增至 canvas_Draw的最後:
args.DrawingSession.DrawCircle(125, 125, 100, Colors.Green);
args.DrawingSession.DrawLine(0, 0, 50, 200, Colors.Red);

這兩種方法的自變數類似於 DrawText。 圓形是由中心點(125、125)、半徑(100)和色彩(綠色)所定義。 線條是由開頭 (0, 0), 結尾 (50, 200) 和色彩 (紅色) 所定義。

  1. 現在,按 F5 執行應用程式。 您應該會看到 “Hello, world!” 以及綠色圓圈和紅色線條。

您可能想知道如何控制更進階的繪圖選項,例如線條粗細和虛線,或更複雜的填滿選項,例如使用筆刷。 Win2D 提供所有這些選項和更多選項,並可讓您在想要時輕鬆使用它們。 所有 Draw(...) 方法都提供許多可接受額外參數的重載,例如 CanvasTextFormat(字型族、大小等)以及 CanvasStrokeStyle(破折號、點、端帽等)。 可以隨意探索 API 的範圍,以了解更多關於這些選項的資訊。

動態產生繪圖參數

  1. 現在,讓我們透過繪製一堆具有隨機色彩的圖形和文字,來新增一些品種。

將下列程式代碼新增至 MainWindow 類別頂端。 這是協助程式功能,可在繪製時產生您將使用的隨機值:

Random rnd = new Random();
private Vector2 RndPosition()
{
    double x = rnd.NextDouble() * 500f;
    double y = rnd.NextDouble() * 500f;
    return new Vector2((float)x, (float)y);
}

private float RndRadius()
{
    return (float)rnd.NextDouble() * 150f;
}

private byte RndByte()
{
    return (byte)rnd.Next(256);
}
  1. 修改您的 canvas_Draw 方法,以使用這些隨機參數來繪製:
private void canvas_Draw(
    Microsoft.Graphics.Canvas.UI.Xaml.CanvasControl sender,
    Microsoft.Graphics.Canvas.UI.Xaml.CanvasDrawEventArgs args)
{
    args.DrawingSession.DrawText("Hello, World!", RndPosition(), Color.FromArgb(255, RndByte(), RndByte(), RndByte()));
    args.DrawingSession.DrawCircle(RndPosition(), RndRadius(), Color.FromArgb(255, RndByte(), RndByte(), RndByte()));
    args.DrawingSession.DrawLine(RndPosition(), RndPosition(), Color.FromArgb(255, RndByte(), RndByte(), RndByte()));
}

讓我們來分析 DrawText 如何變化。 "Hello, World!" 與之前相同。 x 和 y 位移參數已取代為由 產生的單一 RndPosition。 最後,Color.FromArgb 可讓您使用 A、R、G 和 B 值來定義色彩,而不是使用預先定義的色彩。 是指A,也就是Alpha或不透明度層級,於此情況下,您總是會選擇完全不透明(255)。

DrawCircleDrawLine 的運作方式與 DrawText類似。

  1. 最後,將繪圖程式碼包裝成 for loop。 您最終應該得到下列 canvas_Draw 程式代碼:
for (int i = 0; i < 100; i++)
{
    args.DrawingSession.DrawText("Hello, World!", RndPosition(), Color.FromArgb(255, RndByte(), RndByte(), RndByte()));
    args.DrawingSession.DrawCircle(RndPosition(), RndRadius(), Color.FromArgb(255, RndByte(), RndByte(), RndByte()));
    args.DrawingSession.DrawLine(RndPosition(), RndPosition(), Color.FromArgb(255, RndByte(), RndByte(), RndByte()));
}
  1. 再次執行應用程式。 您應該會看到一堆含有隨機位置和大小的文字、線條和圓形。

將影像效果套用至您的內容

影像效果也稱為篩選效果,是套用至像素數據的圖形轉換。 飽和度、色調旋轉和高斯模糊是一些常見的影像效果。 圖片效果可以串聯在一起,產生精緻的視覺效果,且不需多費力氣。

你使用影像效果的方式是提供一個原始影像(你起始的內容),建立像 GaussianBlurEffect 這類效果,設定屬性如 BlurAmount,然後用 DrawImage 繪製效果輸出。

要為文字和形狀套用影像效果,首先需要將內容渲染成 CanvasCommandList。 此物件可作為您效果的輸入。

  1. canvas_Draw 方法變更為以下程式碼:
CanvasCommandList cl = new CanvasCommandList(sender);

using (CanvasDrawingSession clds = cl.CreateDrawingSession())
{
    for (int i = 0; i < 100; i++)
    {
        clds.DrawText("Hello, World!", RndPosition(), Color.FromArgb(255, RndByte(), RndByte(), RndByte()));
        clds.DrawCircle(RndPosition(), RndRadius(), Color.FromArgb(255, RndByte(), RndByte(), RndByte()));
        clds.DrawLine(RndPosition(), RndPosition(), Color.FromArgb(255, RndByte(), RndByte(), RndByte()));
    }
}

您可以從 CanvasDrawingSession 取得用來繪製的 CanvasDrawEventArgs,一樣,您可以從 CanvasDrawingSession建立 CanvasCommandList。 唯一的差別在於,當您將繪圖繪製到命令清單的繪圖會話(clds)時,您不會直接渲染到 CanvasControl。 相反地,命令清單是中繼物件,可儲存轉譯結果以供日後使用。

您可能已經注意到包裝命令清單繪圖會話的 using 區塊。 繪圖會話會實作 IDisposable,並且必須在完成渲染時釋放(程式區塊 using 會這麼做)。 您從 CanvasDrawingSession 取得的 CanvasDrawEventArgs 會自動為您關閉,但您必須處置您明確建立的任何繪圖會話。

  1. 最後,將下列程式代碼新增至 GaussianBlurEffect 方法的結尾,以定義 canvas_Draw
GaussianBlurEffect blur = new GaussianBlurEffect();
blur.Source = cl;
blur.BlurAmount = 10.0f;
args.DrawingSession.DrawImage(blur);
  1. 再次執行應用程式。 您應該會看到線條、文字和圓形呈現模糊不清的外觀。

使用 CanvasAnimatedControl 建立應用程式動畫

. Win2D 可以讓您即時更新內容並製作動畫,例如,在每個畫面中變更高斯模糊效果的半徑。 為了做到這一點,你會使用 CanvasAnimatedControl

CanvasControl 最適合大部分靜態圖形內容 ,只有在需要更新或重新繪製內容時,才會引發 Draw 事件。 如果您持續變更內容,您應該考慮改用 CanvasAnimatedControl。 兩個控件的運作方式非常類似,但 CanvasAnimatedControl 會定期引發 Draw 事件:預設會呼叫每秒 60 次。

  1. 若要切換至 ,請移至 CanvasAnimatedControlMainPage.xaml,刪除 CanvasControl 行,並將它取代為下列 XAML:
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <canvas:CanvasAnimatedControl x:Name="canvas" Draw="canvas_DrawAnimated" CreateResources="canvas_CreateResources"/>
</Grid>

就像使用 CanvasControl 一樣,讓 AutoComplete 為您建立 Draw 事件處理程式。 預設情況下,Visual Studio會將此處理程序命名為 canvas_Draw_1,因為 canvas_Draw 已經存在;這裡我們將方法重新命名為 canvas_AnimatedDraw,以明確表示這是不同的事件。

此外,你還要處理一個新事件,CreateResources。 再次讓 AutoComplete 建立處理程式。

現在您的應用程式將以每秒 60 幀的速度重新繪製,因此您可以一次性建立 Win2D 的視覺資源,並在每幀中重複使用,這樣會更有效率。 建立 CanvasCommandList,並在內容保持靜態時,每秒 60 次將 300 個項目繪製到其中是沒有效率的。 CreateResources 是當 Win2D 判斷您需要重新建立視覺資源時才引發的事件,例如在載入頁面時。

  1. 切換回 MainPage.xaml.cs。 尋找您的 canvas_Draw 方法,它應該看起來像這樣:
private void canvas_Draw(
    Microsoft.Graphics.Canvas.UI.Xaml.CanvasControl sender,
    Microsoft.Graphics.Canvas.UI.Xaml.CanvasDrawEventArgs args)
{
    CanvasCommandList cl = new CanvasCommandList(sender);
    using (CanvasDrawingSession clds = cl.CreateDrawingSession())
    {
        for (int i = 0; i < 100; i++)
        {
            clds.DrawText("Hello, World!", RndPosition(), Color.FromArgb(255, RndByte(), RndByte(), RndByte()));
            clds.DrawCircle(RndPosition(), RndRadius(), Color.FromArgb(255, RndByte(), RndByte(), RndByte()));
            clds.DrawLine(RndPosition(), RndPosition(), Color.FromArgb(255, RndByte(), RndByte(), RndByte()));
        }
    }

    GaussianBlurEffect blur = new GaussianBlurEffect();
    blur.Source = cl;
    blur.BlurAmount = 10.0f;
    args.DrawingSession.DrawImage(blur);
}

此現有繪製程式代碼中絕大多數不需要與每個框架一起執行:包含文字、線條和圓形的命令清單會與每個框架保持相同,而唯一變更的是模糊半徑。 因此,您可以將此「靜態」程式代碼移至 CreateResources

首先,剪下(CTRL+X)canvas_Draw 的所有內容,但保留最後一行(args.DrawingSession.DrawImage(blur);)。 您現在可以刪除 canvas_Draw 的其餘部分,因為不再需要:請記得 CanvasAnimatedControl 有自己的不同 Draw 事件。

  1. 找到自動產生的 canvas_CreateResources 方法:
private void canvas_CreateResources(
    Microsoft.Graphics.Canvas.UI.Xaml.CanvasAnimatedControl sender, 
    Microsoft.Graphics.Canvas.UI.CanvasCreateResourcesEventArgs args)
{}

將先前剪下的程式代碼貼到此方法中(CTRL+V)。 接下來,將 GaussianBlurEffect 宣告移至方法主體之外,讓變數成為MainPage類別的成員。 您的程式代碼現在看起來應該如下所示:

GaussianBlurEffect blur;
private void canvas_CreateResources(
    Microsoft.Graphics.Canvas.UI.Xaml.CanvasAnimatedControl sender,
    Microsoft.Graphics.Canvas.UI.CanvasCreateResourcesEventArgs args)
{
    CanvasCommandList cl = new CanvasCommandList(sender);
    using (CanvasDrawingSession clds = cl.CreateDrawingSession())
    {
        for (int i = 0; i < 100; i++)
        {
            clds.DrawText("Hello, World!", RndPosition(), Color.FromArgb(255, RndByte(), RndByte(), RndByte()));
            clds.DrawCircle(RndPosition(), RndRadius(), Color.FromArgb(255, RndByte(), RndByte(), RndByte()));
            clds.DrawLine(RndPosition(), RndPosition(), Color.FromArgb(255, RndByte(), RndByte(), RndByte()));
        }
    }

    blur = new GaussianBlurEffect()
    {
        Source = cl,
        BlurAmount = 10.0f
    };
}
  1. 現在您可以以動畫顯示高斯模糊。 尋找 canvas_DrawAnimated 方法,並新增下列程序代碼:
private void canvas_DrawAnimated(
    Microsoft.Graphics.Canvas.UI.Xaml.ICanvasAnimatedControl sender,
    Microsoft.Graphics.Canvas.UI.Xaml.CanvasAnimatedDrawEventArgs args)
{
    float radius = (float)(1 + Math.Sin(args.Timing.TotalTime.TotalSeconds)) * 10f;
    blur.BlurAmount = radius;
    args.DrawingSession.DrawImage(blur);
}

這會讀取 CanvasAnimatedDrawEventArgs 提供的總經過時間,並用此計算所需的模糊量;正弦函數隨時間提供了有趣的變化。 最後,GaussianBlurEffect 已完成渲染。

  1. 執行應用程式以觀看模糊內容在過程中的變化。

恭喜您完成本快速入門教學課程! 希望您已瞭解如何使用 Win2D 來建立豐富的動畫視覺效果場景,只需幾行 C# 和 XAML 程式代碼即可。