WPFでReactivePropertyを扱うプロジェクト

C# コンピュータ
C#
WPFの空のプロジェクトです。

プロジェクトの作成

mkdir プロジェクト名
cd プロジェクト名
dotnet new wpf
dotnet add package Microsoft.Xaml.Behaviors.Wpf
dotnet add package ReactiveProperty.WPF
code .

ソースコード

ファイル名:MainWindow.xaml

<Window x:Class="プロジェクト名.MainWindow"
        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:i="clr-namespace:Microsoft.Xaml.Behaviors;assembly=Microsoft.Xaml.Behaviors"
        xmlns:interactivity="clr-namespace:Reactive.Bindings.Interactivity;assembly=ReactiveProperty.WPF"
        xmlns:local="clr-namespace:プロジェクト名"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Window.DataContext>
        <local:MainWindowViewModel />
    </Window.DataContext>
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="Closed">
            <interactivity:EventToReactiveCommand Command="{Binding WindowClosedCommand}" />
        </i:EventTrigger>
    </i:Interaction.Triggers>
    <Grid>

    </Grid>
</Window>

ファイル名:MainWindowViewModel.cs

using System;
using System.ComponentModel;
using Reactive.Bindings;
using Reactive.Bindings.Extensions;
using System.Reactive.Disposables;

using System.Diagnostics;

namespace プロジェクト名;

public class MainWindowViewModel : INotifyPropertyChanged, IDisposable
{
    public event PropertyChangedEventHandler? PropertyChanged;
    protected virtual void OnPropertyChanged(string name)
    {
        if (PropertyChanged == null) return;
        PropertyChanged(this, new PropertyChangedEventArgs(name));
    }
    private CompositeDisposable Disposable { get; } = new ();
    public void Dispose() => Disposable.Dispose();
    public ReactiveCommand<EventArgs> WindowClosedCommand { get; }
    public MainWindowViewModel()
    {
        WindowClosedCommand = new ReactiveCommand<EventArgs>()
            .WithSubscribe(e => Disposable.Dispose());
    }

}

説明

久々にMVVMでアプリケーションを作ろうと思いプロジェクトを作成してみたところ、警告が多数出ていたのでそれを潰して行ったらこのようなソースコードが出来上がりました。ほぼ空のプロジェクトですがウィンドウをクローズするイベントでDispose()を呼ぶようにしています。Dispose()にたいする理解が不足しているので、これで良いか今一つ自身が無いですが、Dispose()を別にしてもアプリケーションの終了時に実行したい処理を記述する場所として用意してみました。

感想

C#でDesktopアプリケーションを作ろうと思うと、MVVM(WPF)はコード量が多いので昔ながらのWinFormsでプロジェクトを作成しがちです。WinFormsでコントロールの作成・配置関係が多くて面倒になってくるとXAMLを使いたくてWPFをコード・ビハインドでプロジェクトを組むようになります。せっかくWPFでプロジェクトを作成するのであれば再利用することを考えてMVVMでコードを記述するのがベターだと考えるようになります。という感じに何週目かのMVVMになります。
オブジェクト指向プログラミングの学習と並行して学習を進めていけたらよいなぁと考えています。

プロジェクトを作成するPowerShellスクリプト

<#
.SYNOPSIS
WPF(MVVM)のソースコードを作成

.DESCRIPTION
WPF(MVVM)のソースコードを作成

.EXAMPLE
New-MVVM.ps1 "WpfProgram1"

.PARAMETER ProjectName
プロジェクト名

#>
using namespace System.IO
using namespace System.Text

param(
    [string]
    $ProjectName = "WpfProgram1",
    [switch]
    $Help
)

if ($Help) {
    Get-Help $PSCommandPath
    Exit 1
}

if (Test-Path $ProjectName)
{
  Remove-Item $ProjectName -Filter "*.*" -Recurse
}
New-Item -Name $ProjectName -ItemType Directory
Set-Location $ProjectName

. dotnet.exe new wpf
. dotnet.exe add package Microsoft.Xaml.Behaviors.Wpf
. dotnet.exe add package ReactiveProperty.WPF

# テンプレート
$filename = Join-Path (Get-Location).Path "MainWindow.xaml"
$template = @"
<Window x:Class="$ProjectName.MainWindow"
        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:i="clr-namespace:Microsoft.Xaml.Behaviors;assembly=Microsoft.Xaml.Behaviors"
        xmlns:interactivity="clr-namespace:Reactive.Bindings.Interactivity;assembly=ReactiveProperty.WPF"
        xmlns:local="clr-namespace:$ProjectName"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Window.DataContext>
        <local:MainWindowViewModel />
    </Window.DataContext>
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="Closed">
            <interactivity:EventToReactiveCommand Command="{Binding WindowClosedCommand}" />
        </i:EventTrigger>
    </i:Interaction.Triggers>
    <Grid>

    </Grid>
</Window>
"@

$fs = New-Object StreamWriter($filename, $false)
$fs.Write($template)
$fs.Close()

$filename = Join-Path (Get-Location).Path "MainWindowViewModel.cs"
$template = @"
using System;
using System.ComponentModel;
using Reactive.Bindings;
using Reactive.Bindings.Extensions;
using System.Reactive.Disposables;

using System.Diagnostics;

namespace $ProjectName;

public class MainWindowViewModel : INotifyPropertyChanged, IDisposable
{
    public event PropertyChangedEventHandler? PropertyChanged;
    protected virtual void OnPropertyChanged(string name)
    {
        if (PropertyChanged == null) return;
        PropertyChanged(this, new PropertyChangedEventArgs(name));
    }
    private CompositeDisposable Disposable { get; } = new ();
    public void Dispose() => Disposable.Dispose();
    public ReactiveCommand<EventArgs> WindowClosedCommand { get; }
    public MainWindowViewModel()
    {
        WindowClosedCommand = new ReactiveCommand<EventArgs>()
            .WithSubscribe(e => Disposable.Dispose());
    }

}
"@

$fs = New-Object StreamWriter($filename, $false)
$fs.Write($template)
$fs.Close()

カレントディレクトリ下にプロジェクトディレクトリが作成されます。

コメント