WPFでEF(Entitiy Framework)を使っていると、リスト表示には楽なのだが、データを反映するためにはINotifyPropertyChangedを継承させなくてはいけなくて、そこが面倒くさい。いちいち、ViewModel クラスで EF のクラスをくるめば良いのだが、それを自動化したい。
特にマスター管理用の画面なんかを作るときは、ASP.NET MVCのように足場を自動化させておきたいというのもある。
EFで出力されるクラスは、下記のようなクラスなので、
1 2 3 4 5 6 7 | public partial class 社員名 { public int 社員ID { get ; set ; } public Nullable< int > 部署ID { get ; set ; } public Nullable< int > 営業担当FLG { get ; set ; } public string 社員名1 { get ; set ; } } |
MVVM対応のINotifyPropertyChangedインターフェースを継承したこんなクラスにしたいわけだ。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | public partial class 社員名 : ObservableObject { private int _社員ID ; public int 社員ID { get { return _社員ID; } set { SetProperty( ref _社員ID, value, nameof(社員ID)); } } private Nullable< int > _部署ID ; public Nullable< int > 部署ID { get { return _部署ID; } set { SetProperty( ref _部署ID, value, nameof(部署ID)); } } private Nullable< int > _営業担当FLG ; public Nullable< int > 営業担当FLG { get { return _営業担当FLG; } set { SetProperty( ref _営業担当FLG, value, nameof(営業担当FLG)); } } private string _社員名1 ; public string 社員名1 { get { return _社員名1; } set { SetProperty( ref _社員名1, value, nameof(社員名1)); } } } |
こうしておくと、リストビューで一覧表示をさせておいて、選択時にテキストボックスへ表示、そしてテキストボックスでの変更がリストビューに反映される、というところが自動化される。
Model1.ttを直接書き替える
元のクラスにスクリプトを通して、MVVM対応のクラスを作ろうかとも思ったのだが、もっと簡単に EF で出力される Model1.tt を直接書き替えてしまう。T4 で書かれている Model1.tt はファイルに保存した途端にテンプレートに従って各クラスが出力される。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | public class CodeStringGenerator { ... public string Property(EdmProperty edmProperty) { return string .Format( CultureInfo.InvariantCulture, // ★ここを書き替え @"private {1} _{2} ; {0} {1} {2} {{ get {{ return _{2}; }} set {{ SetProperty( ref _{2}, value, nameof({2})); }} }}" , // "{0} {1} {2} {{ {3}get; {4}set; }}", Accessibility.ForProperty(edmProperty), _typeMapper.GetTypeName(edmProperty.TypeUsage), _code.Escape(edmProperty), _code.SpaceAfter(Accessibility.ForGetter(edmProperty)), _code.SpaceAfter(Accessibility.ForSetter(edmProperty))); } ... public string EntityClassOpening(EntityType entity) { return string .Format( CultureInfo.InvariantCulture, // ★ここを書き替え "{0} {1}partial class {2}{3} : ObservableObject " , Accessibility.ForType(entity), _code.SpaceAfter(_code.AbstractOption(entity)), _code.Escape(entity), _code.StringBefore( " : " , _typeMapper.GetTypeName(entity.BaseType))); } ... } |
大ざっぱだが、CodeStringGeneratorクラスのPropertyメソッドとEntityClassOpeningメソッドの内容を書き替えてしまう。
EntityClassOpening は、エンティティのクラスを出力するところなので、INotifyPropertyChangedインターフェースを実装した「ObservableObject」クラスを継承するように書き替える。ObservableObjectクラスは別途自前で用意しておく。
Property メソッドは、プロパティを出力するところなので、SetProperty メソッドを呼び出して変更通知が飛ぶようにする。
これを保存すると、各エンティティのクラスが、
1 2 3 4 5 6 7 8 9 | public partial class 社員名 : ObservableObject { private int _社員ID ; public int 社員ID { get { return _社員ID; } set { SetProperty( ref _社員ID, value, nameof(社員ID)); } } ... } |
のように書き変わる。
のような画面が、バインドだけで作れるようになる。