【WPF】viewを作成する際のテンプレートコード【C#】

# 概要
xamlで新しくwindow (view) を作成する際の、テンプレートとなるサンプルコードを記載してみました。

# 仕様
- Windowに「OK」と「キャンセル」ボタンが配置されており、どちらを押下してもWindowが閉じる挙動です。
- 使用する側 (viewやviewModelをnewし、`ShowDialog()`する側) で「OK」と「キャンセル」どちらのボタンが押されたかを判定することができます。
- Window右上の「×」ボタンが押された場合も、「キャンセル」ボタンと同じように扱われます。

# コード
### View
```xml:View.xaml
<Window
    x:Class="Hoge.Views.View"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="サンプルビュー"
    Width="800"
    Height="400"
    ResizeMode="NoResize">
    <Grid>
        <!--  OK、キャンセルボタン  -->
        <StackPanel
            Orientation="Horizontal">
            <Button
                Width="90"
                Height="20"
                Click="OKButton_Click"
                Command="{Binding OKCommand}"
                Content="OK"/>
            <Button
                Width="90"
                Height="20"
                Click="CancelButton_Click"
                Command="{Binding CancelCommand}"
                Content="Cancel"/>
        </StackPanel>
    </Grid>
</Window>
```

```C#:View.xaml.cs
    /// <summary>
    /// View.xaml の相互作用ロジック
    /// </summary>
    public partial class View : Window
    {
        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="vm">ViewModel</param>
        public View(ViewModel vm)
        {
            InitializeComponent();
            this.DataContext = vm;
        }

        /// <summary>
        /// OKボタンクリック
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OKButton_Click(object sender, RoutedEventArgs e) => this.Close();

        /// <summary>
        /// キャンセルボタンクリック
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void CancelButton_Click(object sender, RoutedEventArgs e) => this.Close();
    }
```
### ViewModel
```C#
    public class ViewModel : BindableBase
    {
        /// <summary>
        /// キャンセルしたか
        /// </summary>
        public bool IsCancel { get; private set; } = true;

        /// <summary>
        /// OKボタン
        /// </summary>
        public ICommand OKCommand => _okCommand ?? (_okCommand = new DelegateCommand(() =>
        {
            try
            {
                IsCancel = false;
            }
            catch (Exception ex)
            {
                //エラー処理
            }
        }));
        private ICommand _okCommand;

        /// <summary>
		/// キャンセルボタン
		/// </summary>
		public ICommand CancelCommand => _cancelCommand ?? (_cancelCommand = new DelegateCommand(() =>
        {
            try
            {
                IsCancel = true;
            }
            catch (Exception ex)
            {
                //エラー処理
            }
        }));
        private ICommand _cancelCommand;
        
    }
 ```

:::note
ViewModelの`BindableBase`や`DelegateCommand`はprismを使用しています。
:::


 ### 使用方法

 ```C#
    var vm = new ViewModel();

    // 画面起動
    new View(vm).ShowDialog();
    
    if (vm.IsCancel)
    {
        // 「キャンセル」や右上の「×」ボタンが押下された場合の処理
    }
 ```

 # 終わりに
ViewModelの`IsCancel`の初期値が`true`になっているため、「×」ボタンでも「キャンセル」ボタンと同様の扱いになります。
※なので、厳密にはキャンセルボタンを押下した際の`IsCancel = true;`を入れる処理は不要です。
また、「OK」ボタン押下しても、例えば内部チェック等に引っかかり、再度この画面を表示する場合にViewModelを使いまわすと、もちろん`IsCancelの`値が`false`になっているため、「×」ボタン押下時の挙動が変わってしまいます。なのでその場合は、再度ViewModelをnewするか、仕様的にそれが難しいならViewModelに`IsCancel`の値を初期値の`true`に戻すメソッドを用意し、再度表示する場合はそのメソッドを通す形で対応できます。この形だと、例えばViewで押下されている沢山のボタンをViewModelで保持している場合だと、前回の押下状態を保持したまま再度Viewを表示できます。

以下はViewを再表示する場合に、ViewModelを使いまわしているサンプルコードです。

```C#:ViewModel.cs
    /// <summary>
    /// キャンセルしたか
    /// </summary>
    public bool IsCancel { get; private set; } = true;

    /// <summary>
    /// キャンセルフラグをデフォルト値に設定する
    /// </summary>
    public void InitIsCancelFlag() => IsCancel = true;
```

### 使用方法
```C#
    private void SampleMethod()
    {
        ViewModel vm = null;
        while (true)
        {
            if (vm == null)
            {
                // 初回起動する場合
                vm = new ViewModel();
            }
            else
            {
                // 再度起動する場合
                vm.InitIsCancelFlag();
            }

            // 画面起動
            new View(vm).ShowDialog();

            if (vm.IsCancel)
            {
                // 「キャンセル」や右上の「×」ボタンが押下された場合の処理
                
                return;
            }

            // 画面の入力項目のエラーチェック
            var result = IsValid(vm, out string message);
            if (result) break;

            // エラーメッセージ表示
            MessageBox.Show(message, "チェック結果", MessageBoxButton.OK, MessageBoxImage.Warning);
        }        
    }
```