.NET MAUI RC3を試してみる（後編）

.NET MAUIアプリについて「[前編](https://qiita.com/hqf00342/items/d4a7a262cbec7b480cc8)」でコンパイル、「[中編](https://qiita.com/hqf00342/items/8859d273006554b7828d)」でサンプルコードのチェックをしました。今回は簡単なアプリを作って、レイアウトパネルの描画性能をWPFと比較してみます。

# サンプル

ボールが飛び跳ねる様子を描画し、どれくらいのボール数まで耐えられるかを見てみます。
下の画像はWPFでボール100個を描画した場合です。アップロードサイズの都合で 15fps の画像ですが実際はもっとスムースに動きます。
![アニメーションGIF版](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/54535/4861a38c-7686-a3de-e06c-573c74ca913d.gif)
[mp4版](https://user-images.githubusercontent.com/6317011/171355318-9fa57f57-7767-481f-a3c3-40ad2426c1f9.mp4)

# 描画方法

絶対位置指定のできるレイアウトパネルにボール(Ellipse)を大量描画します。WPFは `Canvas` 、 MAUI は `AbusoluteLayout` を使いました。
ボールはアニメーション機能を使わず、フレーム描画イベントごとに自前描写、位置を `v = v0 + gt` という式で更新するだけです。そのコードは単純ですので省略します。（欲しい方いればgithubにでもアップします）

**この先を読み進める前に・・・**

レイアウトパネルの性能を見るため`Canvas` や`AbusoluteLayout` を使ってますが、本来レイアウトパネルはOwnerDrawでアニメをするものではないです。
この後出てくる数字を見て 「ゲームじゃ使えねえ！」「MAUIなにこれ！？」と思わないでほしいです・・・。MAUIには[GraphicView](https://docs.microsoft.com/ja-jp/dotnet/maui/user-interface/controls/graphicsview)という描画機能があるので[GraphicViewで描写テスト](https://qiita.com/hqf00342/items/390f1dbb044fb7403cea)した記事は別途投稿します。

# WPFの場合

Xamlでキャンバスを張り付けボタンなどを事前に置いていきます。
実際のコードは座標指定やイベント処理が入ります。

```xml
<Window 省略 >
    <Canvas x:Name="canvas">
        <Button Content="Start" />
        <Slider x:Name="slider" />
        <TextBlock Text="{Binding ElementName=slider,Path=Value}" />
        <TextBlock x:Name="fps" />
    </Canvas>
</Window>
```

ボールはスライダーで個数指定させるためにC#で描画します。単純な丸いボールです。
```csharp
var ellipse = new Ellipse {
   Width = 5.0,
   Height = 5.0,
   Fill = Brushes.Black
};
canvas.Children.Add(ellipse);
```
ボタンを押されたら描画開始。WPFでは `CompositionTarget.Rendering`でフレーム毎に描画をしますので呼ばれるたびにボールの位置をアップデートし、fpsを計算します。

```csharp
private void Button_Click(object sender, RoutedEventArgs e)
{
    CompositionTarget.Rendering += CompositionTarget_Rendering;
}
private void CompositionTarget_Rendering(object? sender, EventArgs e)
{
    frame++;
    foreach (var ball in _ballBist)
    {
        ball.UpdatePos(frame);
    }
    fps.Text = CalcFps();
```

私のPCではボール1000個ぐらいまでは60fps、2000個で約30fpsになります。
![wpf.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/54535/29a75a1f-9232-b369-6b03-98c92adbf9ee.gif)
[mp4版](https://user-images.githubusercontent.com/6317011/171355631-172e6997-7fd1-4f3f-8020-4f3042a084bf.mp4)

# .NET MAUIの場合

MAUIには以下の4つのレイアウト([公式HPの説明](https://docs.microsoft.com/ja-jp/dotnet/maui/user-interface/layouts/))があります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/54535/41a3891c-567e-9be1-6ac0-421ea1224d29.png)

絶対座標系として `AbusoluteLayout` があるのでそれを使います。
位置は`AbsoluteLayout.LayoutBounds`添付プロパティで指定します。
```xml
<AbsoluteLayout x:Name="a">
    <Button Text="Start" AbsoluteLayout.LayoutBounds="400,5,200,33"/>
    <Slider x:Name="slider" AbsoluteLayout.LayoutBounds="400,50"/>
    <Label BindingContext="{x:Reference slider}" Text="{Binding Path=Value}" 
            AbsoluteLayout.LayoutBounds="400,80,200,25"/>
    <Label x:Name="fps" />
</AbsoluteLayout>
```
ボールのフレーム毎描画もWPFと同じく `CompositionTarget.Rendering` があったので使いました。もしかするとMAUIではもっと良い描画方法があるのかもしれません。

```csharp
private void Button_Clicked(object sender, EventArgs e)
{
    Microsoft.UI.Xaml.Media.CompositionTarget.Rendering += CompositionTarget_Rendering;
}

private void CompositionTarget_Rendering(object sender, object e)
{
    frame++;
    foreach (var ball in _list)
    {
        ball.UpdatePos(frame);
    }
    fps.Text = CalcFps();
}
```

MAUIでの動作状況は以下の通り。100個ぐらいで30fpsでした。
![mauia.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/54535/e11dbdbe-7ce5-2563-60f8-4a8f2238eb43.gif)
[mp4版](https://user-images.githubusercontent.com/6317011/171355750-7222a679-b925-40fe-8f9e-fa244c389a6b.mp4)

残念な結果でしたので[GraphicViewで描写テスト](https://qiita.com/hqf00342/items/390f1dbb044fb7403cea)した記事を別途投稿します。

# 終わりに

本当はMVVMやMVU機能を書きたかったのですがなかなか時間が取れずお遊びにしました。
MAUIはXAML、データバインディング等は引き継がれているので、Xamarin経験者はほぼそのまま、WPFを作ったことがある人もそれほど違和感なく作れると思います。AndroidやiOS向けにXamarinを使ってしていた人は移行すると思います。
しかし、Windows向けGUIアプリを作りたいだけの場合、WinFormsやWPFから移行する動機が小さいと思いました。画面遷移機能などはとても便利なのですが。

描写性能についてはちゃんと本来の描画機能を使ったもので再度作ってみたいと思います。