共用方式為


建置簡單的 Win2D 應用程式

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

  • 將 Win2D 新增至 C# XAML Windows 專案。
  • 繪製文字和幾何。
  • 套用篩選效果。
  • 以動畫顯示您的 Win2D 內容。
  • 遵循 Win2D 最佳做法。

設定開發電腦

請確定為您的電腦安裝好所有必要的工具:

建立新的 Win2D 專案

請遵循 Win2D "Hello, World!" 快速入門中的步驟,使用 Win2D 建立新的專案,以及新增 Win2D NuGet 套件的參考。 您可以使用 WinUI 3 (Windows 應用程式 SDK) 或 通用 Windows 平台 (UWP)。

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

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

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

  1. 在 [方案總管] 中按兩下以瀏覽至專案中的 MainPage.xaml。 這會開啟該檔案。 為了方便起見,您可以按兩下 [設計工具] 索引標籤中的 [XAML] 按鈕;這會隱藏視覺效果設計工具,並將所有空間保留給程式碼檢視。

  2. 新增控制項之前,您必須先告訴 XAML,要將 CanvasControl 定義到哪裡。 若要這樣做,請移至 Page 元素的定義,並新增此指示詞:xmlns:canvas="using:Microsoft.Graphics.Canvas.UI.Xaml"。 您的 XAML 現在看起來應該像這樣:

<Page
    ...
    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 Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <canvas:CanvasControl x:Name="canvas"/>
</Grid>
  1. 接下來,定義 Draw 事件的事件處理常式。 每當您的應用程式需要繪製或重新繪製其內容時,CanvasControl 就會引發 Draw。 最簡單的方式,就是讓 Visual Studio AutoComplete 協助您。 在 CanvasControl 定義中,開始輸入 Draw 事件處理常式的新屬性:
<canvas:CanvasControl x:Name="canvas" Draw="canvas_Draw" />

注意

Draw=" 輸入之後,Visual Studio 應該會彈出一個方塊,提示您讓它自動填入事件處理常式的正確定義。 按 TAB 以接受 Visual Studio 的預設事件處理常式。 這也會自動在您程式碼的後台新增格式正確的事件處理常式方法 (`MainPage.xaml.cs``)。 如果您未使用 AutoComplete,請不要擔心;您可以在下一個步驟手動新增事件處理常式方法。

在 Win2D 中繪製您的第一個文字

  1. 現在,讓我們前往 C# 程式碼的後台。 從 [方案總管] 開啟 MainPage.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 語言撰寫並且使用像 CanvasControl 這類 Win2D 控制項的 Win2D 應用程式,都必須遵循下列步驟。 嚴格來說,您簡單的 "Hello, world" 應用程式不會受到影響,但這是可以普遍遵循的好作法。

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

  1. 開啟 MainPage.xaml 並尋找包含 CanvasControl 的 Page 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. 移至並 MainPage.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) 和色彩 (Green) 所定義。 線條是由開始 (0, 0)、結尾 (50, 200) 和色彩 (Red)+ 所定義。

  1. 現在,按下 F5 執行應用程式。 您應該會看到 "Hello, world!" 伴隨一個綠色圓圈和一段紅色線條。

您可能想知道如何控制更進階的繪圖選項,例如線條粗細和虛線,或是更複雜的填滿選項,例如使用筆刷。 Win2D 提供所有這些選項和更多選項,並可讓您在想要時輕鬆使用它們。 Draw(...) 方法全都提供許多多載,可接受其他參數,例如 CanvasTextFormat (字型系列、大小等) 和 CanvasStrokeStyle (虛線、點線、端點樣式等)。 您可以隨意探索 API 介面,以深入了解這些選項。

動態產生繪圖參數

  1. 現在,讓我們透過繪製一堆具有隨機色彩的圖形和文字,來加入一些變化。

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

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 位移參數已取代為單一 System.Numerics.Vector2,此值是由 RndPosition 產生。 最後,相較於使用預先定義的色彩,Color.FromArgb 可讓您使用 A、R、G 和 B 值來定義色彩。 A 是 Alpha,或稱為不透明度等級;在此案例中,您一律想設為完全不透明 (255)。

DrawCircleDrawLine 運作方式類似 DrawText

  1. 最後,將您的繪圖程式碼包裝在 for 迴圈中。 您應該使用下列 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()));
    }
}

如同您從 CanvasDrawEventArgs 中取得 CanvasDrawingSession 來用以繪製,您也可以從 CanvasCommandList 中建立 CanvasDrawingSession。 唯一的差別在於,當您繪製至命令清單的繪圖工作階段 (clds),您不會直接轉譯為 CanvasControl。 相對的,命令清單是中繼物件,可儲存轉譯結果以供後續使用。

您可能已經注意到,using 區塊包裝了命令清單的繪圖工作階段。 繪圖工作階段會實作 IDisposable,而且必須在完成轉譯時處置 (using 區塊做了這個工作)。 您自動從 CanvasDrawEventArgs 取得的 CanvasDrawingSession 會為您關閉,但您必須處置您明確建立的任何繪圖工作階段。

  1. 最後,將下列程式碼新增至 canvas_Draw 方法的結尾,以定義 GaussianBlurEffect
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. 若要切換至 CanvasAnimatedControl,請移至 MainPage.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 視覺效果資源並在每個畫面重複使用,會更有效率。 當內容保持靜態時,每秒 60 次建立 CanvasCommandList 並繪製 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 程式碼,建立豐富的動畫視覺效果場景。