CommunityToolkit.Mvvmを使用したWPFのDataGridへの表示

WPFでDataGridにbindingして、表示・変更する方法についてです。
どのフレームワークを使うかや書き方が色々あるようで、調べた結果の自分メモです。
今回はCommunityToolkit.Mvvm（ver8）を使いました。

[追記]
設定について追記しました。
[メモリCPU使用率が高い](#メモリcpu使用率が高い)


# 準備
- vscode
- .net8
- `dotnet new wpf -n TestWpf`
- `dotnet add package CommunityToolkit.Mvvm`

# つくった内容
## フォルダ構成
MVVMとしたいので少しかえます。
これがいいのかよく分かってはないです。。。

```
C:.
│  App.xaml
│  App.xaml.cs
│  AssemblyInfo.cs
│  MainWindow.xaml
│  MainWindow.xaml.cs
│  TestWpf.csproj
├─.vscode
├─bin
├─Moedls
│      User.cs
└─ViewModels
        UserViewModel.cs
```

## MainWindow
### MainWindow.xaml.cs
こちらはなにも変更しないです。

```c#
using System.Windows;

namespace TestWpf;

/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }
}
```

### MainWindow.xaml
- 名前空間にvmを追加する
- そのvmをdatacontextに指定する
- 表示したい内容を追加してBinding する

```xml
<Window x:Class="TestWpf.MainWindow"
        xmlns:vm="clr-namespace:TestWpf.ViewModels"
        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:local="clr-namespace:TestWpf"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">

    <Window.DataContext>
        <vm:UserViewModel/>
    </Window.DataContext>

    <StackPanel>
        <Button Content="9999項目の追加" Command="{Binding AddClickCommand}" CommandParameter="test" />
        <Button Content="9999項目の削除" Command="{Binding RemoveClickCommand}" />
        <TextBlock Text="{Binding Name}" />
        <DataGrid ItemsSource="{Binding Users, Mode=OneWay}" IsReadOnly="True" />
    </StackPanel>
</Window>

```

## ViwModel
### UserViewModel.cs
- `[INotifyPropertyChanged]`、`[ObservableProperty]`、`[RelayCommand]`を付けることで、ソースジェネレートしてくれる
- Toolkit.mvvmのver8ではさらに必要な記述が減っているようですが、どういうことなのか分かっていないとなんでこんな書き方しているのて思ってしまうので注意

```c#
using System.Collections.ObjectModel;
using System.Diagnostics;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using TestWpf.Models;

namespace TestWpf.ViewModels;

[INotifyPropertyChanged]
internal partial class UserViewModel
{
    [ObservableProperty]
    private string _Name;
    [ObservableProperty]
    public ObservableCollection<User> _Users;

    public UserViewModel()
    {
        _Name = "テキスト";

        _Users =
            [new User() { Id = 1111, UserName = "aiue", Result = false },
            new User() { Id = 2222, UserName = "kakikuke", Result = true },];
    }

    [RelayCommand]
    private void AddClick(string Parameter)
    {
        if (Name == "テキスト")
        {
            Name = "クリックしました！";
        }
        else
        {
            Name = "テキスト";
            Users.Add(new User()
            {
                Id = 9999,
                UserName = "addaddadd",
                Result = true
            });
            Users[0].UserName = "チェンジname";
        }
        Debug.WriteLine($"クリックしました {Parameter}");
    }
    [RelayCommand]
    private void RemoveClick()
    {
        Users.Remove(
            Users.FirstOrDefault(x => x.Id == 9999)
        );
    }
}
```

## Model
### User.cs
- DataGridに反映する内容
- 内容変更を通知できるように`[INotifyPropertyChanged]`をつける

```c#
using CommunityToolkit.Mvvm.ComponentModel;

namespace TestWpf.Models;

[INotifyPropertyChanged]
public partial class User
{
    [ObservableProperty]
    public int _Id;
    [ObservableProperty]
    public string _UserName;
    [ObservableProperty]
    public bool _Result;
}
```

## 完成
![testwpf.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2383487/850ac423-6d19-1771-8c38-f4c8417dd161.gif)

# エラーとか
## ファイルが見つからないエラー
デバッグ実行した時に、ソースファイルが見つかりません。となる時がある。
実際には存在している。
もう一度やり直したり、`dotnet restore`とかするととりあえずは実行できるようになる。

```
CSC : error CS2001: ソース ファイル 'C:\.....\TestWpf\obj\Debug\net8.0-windows\MainWindow.g.cs' が見つかりませんでし
た。 [C:\....\TestWpf\TestWpf.csproj]
CSC : error CS2001: ソース ファイル 'C:\....\TestWpf\obj\Debug\net8.0-windows\App.g.cs' が見つかりませんでした。 [C:\....\TestWpf\TestWpf.csproj]
CSC : error CS2001: ソース ファイル 'C:\....\TestWpf\obj\Debug\net8.0-windows\GeneratedInternalTypeHelper.g.cs' が見
つかりませんでした。 [C:\....\TestWpf\TestWpf.csproj]
```

Microsoft.CodeAnalysis.LanguageServerが動き続けているせい？
Microsoft LearnにはRoslyn analyzerの診断があるとなっているので関係ありそう。
以下設定を追加してみることにしました。
```setting.json
{
  "dotnet.backgroundAnalysis.analyzerDiagnosticsScope": "none"
}
```

参考にした似たような事
- [C# Devkitを入れた後のVS codeのメモリ濫用を抑制する(?)](https://qiita.com/HiroIwasaki/items/82da73d4f799bf7cb823)
- [Configure live code analysis for .NET](https://learn.microsoft.com/en-us/visualstudio/code-quality/configure-live-code-analysis-scope-managed-code?view=vs-2022)

>C# and Visual Basic compiler diagnostics
**Roslyn analyzer** diagnostics, which includes:
Built-in IDE analyzers for code-style suggestions
Built-in CA analyzers for code-quality suggestions
External analyzer packages that are installed for projects in the current solution


## tmpファイルが増え続ける
デバッグ実行する度に、`\obj\Debug\net8.0-windows\`のフォルダ内にソースジェネレーターでつくられたと思われるtmpファイルが増え続けているようです。
増えないような設定が分からなかったので、デバッグビルド時に削除する処理を入れました。
`TestWpf.csproj`に追記します。
以下で、`obj\`フォルダ内のtmpファイルが削除されます。
削除したファイルを表示しますが、vscode標準の設定では、`Message Importance="High"`を入れないとコンソールに表示されないようでした。

```TestWpf.csproj
<Target Name="DeletetmpFiles" AfterTargets="build">
    <ItemGroup>
      <FilesToDelete Include="$(BaseIntermediateOutputPath)*tmp*" />
      <FilesToDelete Include="$(BaseIntermediateOutputPath)Debug\net8.0-windows\*tmp*" />
    </ItemGroup>
    <Delete Files="@(FilesToDelete)">
      <Output 
        TaskParameter="DeletedFiles"
        ItemName="FilesDeleted"/>
    </Delete>
    <Message Importance="High" Text="Files deleted: @(FilesDeleted)"/>
    <!-- フォルダを削除する場合 -->
    <!-- <RemoveDir Directories="$(BaseIntermediateOutputPath)" /> -->
</Target>
```

以下を参考にしました。
[Removing bin and obj folder in .NET](https://helloglenn.me/blog/dotnet-clean-bin-obj/)
[タスクを削除する](https://learn.microsoft.com/ja-jp/visualstudio/msbuild/delete-task?view=vs-2022)

ワイルドカードで指定するのに間違った場合、ルートフォルダが対象となるので、ItemGroupにてリストを作成してから、DeleteのFilesへ指定するのがいいようです。

> 警告
Delete タスクでワイルドカードを使用する場合は注意してください。 $(SomeProperty)\**\*.* や $(SomeProperty)/**/*.* などの式を使用すると、間違ったファイルを簡単に削除できます。プロパティが空の文字列に評価される場合は特に、Files パラメーターがドライブのルートに評価され、削除したいものよりもはるかに多く削除される場合があります。

>Delete タスクでワイルドカードを直接使用する代わりに、削除するファイルの ItemGroup を作成し、それに対して Delete タスクを実行します。 ただし、ItemGroup は慎重に配置してください。 ItemGroup をプロジェクト ファイルの最上位レベルに配置すると、ビルドの開始前の早い段階で評価されるため、ビルド プロセスの一部としてビルドされたファイルは含まれません。 そのため、削除する項目のリストを作成する ItemGroup を、Delete タスクの近くのターゲットに配置します。 ドライブのルートから始まるパスを持つ項目リストを作成しないように、プロパティが空でないことを確認する条件を指定します。

## メモリCPU使用率が高い
コード編集をしていると、CPU/メモリの使用率がかなり高くなる。
ファンの音もすごくなりノートPCが熱くなる。
いくつかのissueに似たよなことがありましたが、ソースジェネレーターを参照するとこうなってしまうようです。

- [Beta: Microsoft.CodeAnalysis.LanguageServer seems to leak. #5733](https://github.com/dotnet/vscode-csharp/issues/5733)
- [Microsoft.CodeAnalysis.LanguageServer has excessively high cpu usage #7571](https://github.com/dotnet/vscode-csharp/issues/7571)
- [Port source generator balanced mode to VSCode #75152](https://github.com/dotnet/roslyn/issues/75152)

以下を.csprojファイルのプロパティに追記しました。
が、あまり変わらない。。。
ソースジェネレーターが動き続けているっぽい。
デバッグの時にファイルが見つからないというのも、ソースジェネレーターがファイルを開いているからのようです。

```test.csproj
<RunAnalyzersDuringLiveAnalysis>false</RunAnalyzersDuringLiveAnalysis>
```

[.NET のソース コード分析を無効にする](https://learn.microsoft.com/ja-jp/visualstudio/code-quality/disable-code-analysis?view=vs-2022)


# 参考
- [MVVM Toolkit の機能](https://learn.microsoft.com/ja-jp/dotnet/architecture/maui/mvvm-community-toolkit-features)
- [WPF超絶入門から学び直し1【MVVM】](https://qiita.com/YouKnow/items/6a0fefdfa5fdc4727d9f)
- [.NET用 MVVM Toolkit v8でMVVMコードを短く](https://qiita.com/hqf00342/items/d12bb669d1ac6fed6ab6)
- [CommunityToolkit.Mvvm V8 入門](https://qiita.com/kk-river/items/d974b02f6c4010433a9e)
- [【.NET Community Toolkit】ソースジェネレータのファイルサイズ調査](https://qiita.com/thinva/items/79e64ad7d358b281f6da)