CSVのクラスマッピングの定義をC#スクリプトで記述する (その3: ValidationRule)
この記事は、以下の記事の続きです
- CSVのクラスマッピングの定義をC#スクリプトで記述する - pierre3のブログ
この記事のサンプルコードはこちら
目次
入力可能なデータの条件を指定できるようにする
前回は、CsvHelperのテキスト変換処理(TypeConverter
)ラップしたConverterを設定することで、DataGridColumn の変換処理をカスタマイズしました。
今回は、DataGridColumnの検証(Validation)機能を利用して、カラム毎に入力値の制限を付けられるようにしたいと思います。
C#スクリプト側の記述
スクリプト側に公開するインターフェースに以下のメソッドを追加して、カラム(データ格納クラスのプロパティ)毎に入力制限を設定できるようにします。
public interface ICsvEditorConfigurationHost { void AddValidation<TType,TMember>( Expression<Func<TType, TMember>> memberSelector, Func<TMember, bool> validation, string errorMessage); }
- 第1引数: 式木(デリゲート)でデータ格納クラスの対象プロパティを指定
- 第2引数:「対象プロパティの値を受け取りboolを返すデリゲート」で、入力可能な値の条件を指定
- 第3引数: 入力制限に引っかかった場合のエラーメッセージを指定
スクリプト内での記述例は以下の通り。(前回の記事と同じサンプルデータを使用)
//生年月日は今日以前のみ AddValidation<Person, DateTime>(prop => prop.Birthday, m => m <= DateTime.Today, "未来の日付は入力できません"); //お小遣いは ¥0 以上 ¥10,000 以下 AddValidation<Person, int>(prop => prop.PocketMoney, m => (m >= 0) && (m <= 10000), "入力可能な範囲は ¥0~¥10,000 です。");
AddValidation
メソッドの実装は以下の通りです。 引数に渡した設定値をプロパティ名をキーとした辞書に登録します。
public class CsvEditorConfigurationHost { public IDictionary<string, ColumnValidation> ColumnValidations { get; } = new Dictionary<string, ColumnValidation>(); public void AddValidation<TType, TMember>(Expression<Func<TType, TMember>> memberSelector, Func<TMember, bool> validation, string errorMessage) { //式木からプロパティ名を取得 MemberExpression memberExpression = null; if (memberSelector.Body.NodeType == ExpressionType.Convert) { var body = (UnaryExpression)memberSelector.Body; memberExpression = body.Operand as MemberExpression; } else if (memberSelector.Body.NodeType == ExpressionType.MemberAccess) { memberExpression = memberSelector.Body as MemberExpression; } if (memberExpression == null) { throw new ArgumentException("Not a member access", nameof(memberSelector)); } //プロパティ名をキーにして辞書に登録 ColumnValidations.Add(memberExpression.Member.Name, new ColumnValidation(m => validation((TMember)m), errorMessage)); } } //設定値格納用 public class ColumnValidation { public Func<object, bool> Validation { get; } public string ErrorMessage { get; } public ColumnValidation(Func<object, bool> validation, string errorMessage) { Validation = validation; ErrorMessage = errorMessage; } }
DataGridColumn に ValidationRuleを設定する
スクリプトで指定された条件からDataGridColumn
にバインドするValidationRule
クラスを作成します。
public class DataGridColumnValidationRule : ValidationRule { private Func<object, bool> isValidate; private object errorContent; public DataGridColumnValidationRule(Func<object, bool> isValidate, object errorContent) { if (isValidate == null) { throw new ArgumentNullException(nameof(isValidate)); } if (errorContent == null) { throw new ArgumentNullException(nameof(errorContent)); } ValidationStep = ValidationStep.ConvertedProposedValue; this.isValidate = isValidate; this.errorContent = errorContent ?? "invalid value"; } public override ValidationResult Validate(object value, CultureInfo cultureInfo) { return (isValidate(value)) ? ValidationResult.ValidResult : new ValidationResult(false, errorContent); } }
前回のConverterの指定と同様にAutoGeneratingColumn
イベントのハンドラ内でDataGridColumnのBindingオブジェクトにValidationRuleを追加します。
public class MainWindow { private void DataGrid_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e) { if (VM == null) { return; } var converter = VM.GetDataGridColumnConverter(e.PropertyName); if (converter == null) { return; } if (!string.IsNullOrEmpty(converter.HeaderName)) { e.Column.Header = converter.HeaderName; } var textColumn = e.Column as DataGridTextColumn; if (textColumn != null) { textColumn.EditingElementStyle = (Style)Resources["textColumnStyle"]; } var binding = (e.Column as DataGridBoundColumn)?.Binding as Binding; if (binding != null) { binding.Converter = converter; //VMからValidationRuleを取得してBindingに設定 var validationRule = VM.GetDataGridColumnValidation(e.PropertyName); if (validationRule != null) { binding.ValidationRules.Add(validationRule); } } } } public class MainWindowViewModel { public DataGridColumnValidationRule GetDataGridColumnValidation(string propertyName) { ColumnValidation columnValidaiton; if (host.ColumnValidations.TryGetValue(propertyName, out columnValidaiton)) { return new DataGridColumnValidationRule(columnValidaiton.Validation, columnValidaiton.ErrorMessage); } return null; } }
実行例
「生年月日」と「お小遣い」に入力可能範囲外の値を入力してみます。
期待通りセルの背景が赤くなり、ツールチップには指定したエラーメッセージが表示されています。
まとめ
これで入力ミスも柔軟にチェックすることが出来るようになりました。