ValidationAttribute属性で入力値検証


# はじめに
WPFにはDataAnnotationsという仕組みがあって、入力値の検証ができる属性が実装されています。プロパティに属性をつけて、ユーザー入力に対して検証ルールに合致しなければ、エラーを通知するというものです。本記事では、どんな検証ができるのか紹介します。

#まずは、DataAnnotationsを参照設定に加えます。

```csharp
using System.ComponentModel.DataAnnotations;
```

#プロパティの定義と初期化
ReactivePropertyのSetValidateAttributeメソッドをつかいます。

```csharp
public class SampleViewModel: ViewModelBase
{

    public SampleViewModel()
    {
        // ユーザーからの入力を受けるプロパティ
        this.Input = new ReactiveProperty<string>()
            .SetValidateAttribute(() => this.Input)
            .AddTo(this.disposedValue);
    }

    public ReactiveProperty<string> Input { get; }

}

```

#さまざまな入力値検証

## Required
必須入力。入力されているかどうかをチェックします。

```csharp
[Required()]
public ReactiveProperty<string> Input { get; }
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684408/306009d3-75da-c57e-3c74-198fef711ee1.png)

引数にErrorMessageを指定すれば、任意のメッセージを表示することができます。

```csharp
[Required(ErrorMessage = "何か入力してください。")]
public ReactiveProperty<string> Input { get; }
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684408/9023a2af-ca5d-9961-824f-3c643f065528.png)


## StringLength
文字列長。引数に文字数を設定。入力値が設定した文字数以内かチェックします。

```csharp
[StringLength(10)]
 public ReactiveProperty<string> Input { get; }
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684408/33b9a0da-2074-4c90-7f4d-94b064a364ca.png)


## Range
数値の範囲。引数に最小値と最大値を設定する。入力値が最小値～最大値の範囲内かどうかをチェックします。

```csharp
[Range(0,5)]
public ReactiveProperty<string> Input { get; }
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684408/c8aae470-bf5f-f812-6c26-704ff562fce4.png)
型を指定すれば、その型で範囲を指定できます。

```csharp
[Range(typeof(DateTime),"2020/10/1", "2020/10/5")]
public ReactiveProperty<string> Input { get; }
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684408/74b6503e-0599-9dea-12e7-59e14fc0429a.png)



##RegularExpression
正規表現。引数に正規表現のルールを設定する。入力値が正規表現に一致するかチェックしします。

```csharp
[RegularExpression("[a-d]+")]
public ReactiveProperty<string> Input { get; }
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684408/4957f25c-e214-40e6-8d00-6352cf95ff9f.png)


## CustomValidation
カスタム検証。引数にクラスの型とクラスが持っているメソッド名を指定する。メソッド内で任意の条件を実装することが可能。

```csharp
[CustomValidation(typeof(CustomValidateMethods), "Test")]
public ReactiveProperty<string> Input { get; }
```

```CustomValidateMethods.cs
public static class CustomValidateMethods
{

    public static ValidationResult Test(object value, ValidationContext context)
    {
        var input = value?.ToString() ?? string.Empty;
        // 何らかの条件
        var result = input == "123";
        return result ? ValidationResult.Success : new ValidationResult("エラーです。");
    }
}

```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684408/fbe89540-e45c-2b43-cd13-4aade9dfdd08.png)

## ValidationAttribute属性を継承
基底クラスのValidationAttributeクラスを継承すると、自前の検証クラスを作ることができます。

```csharp
[MyValidation("abc","abcって入力してください。")]
public ReactiveProperty<string> Input { get; }
```

```MyValidationAttribute.cs
[AttributeUsage(AttributeTargets.Property)]
public class MyValidationAttribute: ValidationAttribute
{
    public string Answer { get; }

    public MyValidationAttribute(string ansewer, string errorMessage = "")
    {
        this.Answer = ansewer;
        if (!string.IsNullOrEmpty(errorMessage)) this.ErrorMessage = errorMessage;
    }

    protected override ValidationResult IsValid(object value, ValidationContext context)
    {
        var input = value?.ToString() ?? string.Empty;
        // 何らかの条件
        var result = input == this.Answer;
        return result ? ValidationResult.Success : new ValidationResult(this.ErrorMessage);
    }
}
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684408/e55023e5-5612-f4e2-0ab7-7d46d6f10e2e.png)


#【おまけ】ボタンとの連携
入力値の検証をボタンの活性・非活性を組み合わせることができます。
エラーがなくなるまで、ボタンは押させない！

```csharp
    public ReactiveCommand Execute { get; }
```

```csharp
    this.Execute = new[]
    {
        this.Input.ObserveHasErrors
    }
    .CombineLatestValuesAreAllFalse()
    .ToReactiveCommand()
    .AddTo(this.disposedValue);
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684408/9643390e-41a7-cd08-e1b3-8afd420b25bf.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684408/b57a488a-a127-746e-e231-6a1d3e6b29c0.png)

#最後に
他にもいろいろValidationの属性はあるかと思いますが、とりあえずここまで。

以上。