WPF/UWP などの XAML 系実装で使用する ViewModel は OSS(Livet, Prism ReactiveProperty) などを使用しない場合、INotifyPropertyChanged 周りの実装が冗長で、繰り返しが面倒なので軽減策の紹介したいと思います。
アプローチ方法は以下の通りです。
- 共通基底クラスを使用
- プロパティはスニペット化
確認環境
- VisualStudio2019
- C# 9.0
Bindableクラス
まず ViewModel の共通基底クラスとして Bindableクラスを以下のように宣言します。
using System; using System.ComponentModel; using System.Runtime.CompilerServices; /// <summary> /// ViewModel の共通基底クラス /// </summary> public abstract class Bindable : INotifyPropertyChanged, IDisposable { // INotifyPropertyChanged impl public event PropertyChangedEventHandler PropertyChanged; /// <summary> /// 指定した名称で <see cref="INotifyPropertyChanged.PropertyChanged"/> を呼び出します。 /// </summary> protected virtual void RaisePropertyChanged([CallerMemberName] string propertyName = "") { if (this.PropertyChanged == null) { return; } this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } #region IDisposable private bool disposedValue; public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (!disposedValue) { if (disposing) { PropertyChanged = null; } disposedValue = true; } } #endregion }
スニペットの作成
次に以下のコードを propvm.snippet としてファイルに保存し、以下のメニューからスニペットを VisualStudio にインポートします。
// インポート方法
> 画面上部の ツール > コード スニペット マネージャー
> [言語] > CSharp を選択 > インポートボタンを押す
スニペットは以下の通りです。
<?xml version="1.0" encoding="utf-8" ?> <CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet"> <CodeSnippet Format="1.0.0"> <Header> <Title>Bindable Property</Title> <Shortcut>propvm</Shortcut> <Description>Property for INotifyPropertyChanged</Description> <Author>Takap</Author> </Header> <Snippet> <Declarations> <Literal> <ID>valueType</ID> <Default>int</Default> <ToolTip>公開する変数の型で置き換えます。</ToolTip> </Literal> <Literal> <ID>propName</ID> <Default>Sample</Default> <ToolTip>公開するプロパティの名前で置き換えます。</ToolTip> </Literal> <Literal> <ID>fieldName</ID> <Default>sample</Default> <ToolTip>内部で使用する変数の名前で置き換えます。</ToolTip> </Literal> </Declarations> <Code Language="csharp"><![CDATA[private $valueType$ _$fieldName$; public $valueType$ $propName$ { get => _$fieldName$; set { if ($valueType$.Equals(_$fieldName$, value)) { _$fieldName$ = value; this.RaisePropertyChanged(); } } }$end$]]></Code> </Snippet> </CodeSnippet> </CodeSnippets>
使い方
Bindable が継承されているクラス上で使用する前提です。
インポート後にエディター上で propvm > Tab > Tab と入力すると以下のようにコードが展開されるので必要個所を入力します。
public class MyViewModel : Bindable // Bindable を継承していること { // propvm > Tab > Tab で展開されるコード // 型、フィールド名、プロパティ名の順に入力する private int _sample; public int Sample { get => _sample; set { if (int.Equals(_sample, value)) { _sample = value; this.RaisePropertyChanged(); // Bindable を継承してないとエラー } } }
以上