WPFのView – View Model – Modelでのパラメータ設定時のViewからのパラメータの設定とエラーチェック方法の一つの考え方。
初めに
Modelないにある動作パラメータをViewから設定させる場合、このMSDNの記事によると、大体次の2つのパターンが考えられるそうだ。
- View Modelラッパーからモデルインスタンスを直接公開し、モデルクラスにINotifyPropertyChangedインターフェイスを実装させる。
- WPF, .NET Framework 4の動的オブジェクトおよび動的メソッドディスパッチを使用する。
モデルインスタンスを直接公開した場合、モデル側にINotifyPropertyChangedインターフェースを用意するのはまあいいとして、エラーチェックのためINotifyDataErrorInfoなどのエラー通知のインターフェースをも用意するのはちょっと違うのかなと思った。
そこで、次のような実装にし、パラメータの設定とチェックに関して、モデル側にはINotifyPropertyChanged, INotifyDataErrorInfoがなくてもいいような構造にしてみた。
基本的な考え方
モデルインスタンスは直接公開せず、ただ2の対応を行った場合も、型変換に難があったため、ViewModelに設定用のラッパーメソッドを設けた。
ただし、パラメータのチェックはモデル側で行い、そのチェック処理とエラー内容を表す情報はメソッドで返すようにする。
クラス構造
構造自体は特に代わり映えもない。Prismを使用しているため、ViewModelの親はBindableBaseを使用している。
クラスメソッド
Modelには、パラメータチェック用のisValidメソッドを設ける。このメソッドでは、モデル内のパラメータをすべてチェックし、エラーがあった場合、メンバ名とエラーを返すようにしている。
ViewModelには、ラッパー用のparameterアクセサを設ける。
パラメータアクセス時の処理方法(シーケンス)
実装
class Model {
public int parameter { set; get; }
/// <summary>
/// 設定内容の値チェック
/// </summary>
/// <returns>属性名とエラーメッセージのペアを返す</returns>
public Dictionary<string, string> isValid() {
var ret = new Dictionary<string, string>();
parameterのチェック
if(エラー) {
ret[nameof(parameter)] = エラーメッセージ;
}
return ret;
}
}
class ViewModel
public class FileParameterViewModel : BindableBase, INotifyDataErrorInfo {
Model model = new Model();
private ErrorsContainer<string> ErrorsContainer { get; }
public bool HasErrors {
get => ErrorsContainer.HasErrors;
}
public IEnumerable GetErrors(string propertyName) {
return this.ErrorsContainer.GetErrors(propertyName);
}
/// <summary>
/// パラメータのチェックを行い、エラーがある場合、エラーコンテナに詰め込む
/// </summary>
private void check() {
var result = parameter_.isValid();
ErrorsContainer.ClearErrors();
foreach(var data in result) {
List<string> work = new List<string>();
work.Add(data.Value);
ErrorsContainer.SetErrors(data.Key, work);
RaisePropertyChanged(data.Key);
}
}
/// <summary>
/// データの設定用メソッド
/// </summary>
private void setProperty<T>(T value, [CallerMemberName] string propertyName = null) where T : IComparable {
PropertyInfo property = parameter_.GetType().GetProperty(propertyName);
if (property == null || (!property.CanRead && !property.CanWrite)) {
return;
}
var target = (property.GetValue(parameter_)) as IComparable;
if (target != null && value.CompareTo(target) != 0) {
property.SetValue(parameter_, value);
RaisePropertyChanged(propertyName);
check();
}
}
public int parameter {
set => setProperty(value);
get => model.parameter;
}
}
これで、ViewModelのparameterをViewにバインドすることで、もしエラーがあった場合、View側に表示される。
最後に
WPFを使ったプログラムを作ろうと思ったら、どんどん深みにはまっていっている。特にMVVMは今まで利用したこともない考え方だったので、何が正解なのか、不正解なのか、やっていいこと、悪いことが全く分からず、五里霧中な状態。
一応、何かちょっとだけ、見えてきたような感じ。
コメント