カスタムコントロールで自由に描画する
はじめに
WinUI 3 (Windows App SDK) での描画についてです。
WPF で用意されていた UIElement.OnRender() が WinUI 3 では用意されていないので、WinUI 3 でカスタムコントロールを作成してもそのままでは描画ができません。
Win2D を使うことで自由な描画(オーナードロー)ができました。
サンプルプログラムのソースコードは GitHub に上げてあります。
動画で見たい方はニコニコ動画をどうぞ。
プロジェクト作成
Template Studio for WinUI を使用して新規プロジェクトを作成します。プロジェクト名は TestCustomControlDraw にしました。
プロジェクトに Win2D をインストールします。[ツール → NuGet パッケージマネージャー → ソリューションの NuGet パッケージの管理]メニューで Win2D を検索するといくつか出てきますが、今回は一番新しい「Microsoft.Maui.Graphics.Win2D.WinUI.Desktop」を使用しました。
Windows App SDK 最新化
本稿執筆時点で、Windows App SDK の安定化最新バージョンは 1.3(1.3.230331000)ですが、Template Studio for WinUI は 1.2 を使用しています。[ツール → NuGet パッケージマネージャー → ソリューションの NuGet パッケージの管理]メニューの「更新プログラム」で最新化できます。
ただし、私の環境では、最新化後にアプリをビルドして実行するとクラッシュしました。一度 Visual Studio を終了し、
TestCustomControlDraw
TestCustomControlDraw.Core
フォルダー内の bin フォルダーと obj フォルダーを削除してから再度ビルドすることで対処できました(ソリューションのクリーンではダメでした)。
カスタムコントロール作成
ソリューションの Views フォルダー内に新しい項目を追加します。
「カスタムコントロール(WinUI 3)」テンプレートではなく「クラス」テンプレートで追加します。クラス名は CustomControl1 にしました。
CustomControl1 を Grid の派生にします。
internal class CustomControl1 : Grid
理由としては、描画のために Win2D の CanvasControl を使うのですが、CanvasControl は sealed なので派生させられません。Grid の子コントロールとして CanvasControl を持つ形にします。
新しい項目追加時にカスタムコントロールテンプレートを使わなかったのもこれが理由です。カスタムコントロールテンプレートを使うと Control 用の Generic.xaml が自動生成されますが無駄になります。
Loaded イベントで CanvasControl を追加します。
private void CustomControl1Loaded(Object sender, RoutedEventArgs args)
{
_canvasControl = new();
_canvasControl.Draw += CanvasControlDraw;
Children.Add(_canvasControl);
}
CanvasControl は描画が必要なタイミングで Draw イベントが生じるので、ここに好きな独自描画プログラムを書くことができます。今回はコントロールいっぱいに楕円を描きました。
private void CanvasControlDraw(CanvasControl canvasControl, CanvasDrawEventArgs args)
{
args.DrawingSession.Clear(Colors.Black);
Single x = (Single)ActualWidth / 2;
Single y = (Single)ActualHeight / 2;
args.DrawingSession.FillEllipse(x, y, x, y, Colors.Red);
}
メモリリーク防止のため、Unloaded イベントで CanvasControl を除去します。
private void CustomControl1Unloaded(Object sender, RoutedEventArgs args)
{
_canvasControl?.RemoveFromVisualTree();
_canvasControl = null;
}
XAML
MainPage に CustomControl1 を追加します。
<Page
x:Class="TestCustomControlDraw.Views.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:v="using:TestCustomControlDraw.Views"
mc:Ignorable="d">
<Grid x:Name="ContentArea">
<v:CustomControl1 />
</Grid>
</Page>
サンプルプログラム
サンプルプログラム(GitHub に上げてあります)を実行すると、ウィンドウ内に楕円が描かれます。
サンプルプログラムの動作の様子は動画でどうぞ。
おわりに
他の方法でオーナードローしている方などいらっしゃいましたら、コメントいただけると幸いです。
確認環境
項目 | 環境 |
---|---|
OS | Windows 11 Pro 22H2 |
Visual Studio | 2022 17.5.5 |
Windows App SDK | 1.3 |
Win2D | Microsoft.Maui.Graphics.Win2D.WinUI.Desktop 7.0.81 |
主な改訂履歴
- 2023/05/05 初版。
- 2024/04/06 パッケージについて追記。
Discussion