共用方式為


ObservableValidator

ObservableValidator是實作 介面的INotifyDataErrorInfo基類,可支援驗證公開給其他應用程式模組的屬性。 它也會繼承自 ObservableObject,因此也會實作 INotifyPropertyChangedINotifyPropertyChanging 。 它可以當做需要支援屬性變更通知和屬性驗證之各種物件的起點。

平臺 API:ObservableValidatorObservableObject

運作方式

ObservableValidator 具有下列主要功能:

  • 它提供的 INotifyDataErrorInfo基底實作,以 ErrorsChanged 公開事件和其他必要的 API。
  • 它提供一系列額外的 SetProperty 多載(在基 ObservableObject 類所提供的多載之上),可提供自動驗證屬性並引發必要事件的能力,再更新其值。
  • 它會公開許多 TrySetProperty 多載,這與 類似 SetProperty ,但只有在驗證成功時,才能夠更新目標屬性,並傳回產生的錯誤(如果有的話),以進行進一步檢查。
  • 它會公開 ValidateProperty 方法,如果尚未更新特定屬性的值,但驗證會相依於已更新的另一個屬性值,則手動觸發特定屬性的驗證會很有用。
  • 它會公開 ValidateAllProperties 方法,這個方法會自動執行目前實例中所有公用實例屬性的驗證,前提是它們已套用至少一個 [ValidationAttribute]
  • 它會公開 ClearAllErrors 方法,在重設系結至使用者可能想要再次填入之表單的模型時很有用。
  • 它提供一些建構函式,允許傳遞不同的參數來初始化將用來驗證屬性的 ValidationContext 實例。 使用可能需要其他服務或選項才能正常運作的自定義驗證屬性時,這特別有用。

Simple 屬性

以下範例說明如何實作支援變更通知和驗證的屬性:

public class RegistrationForm : ObservableValidator
{
    private string name;

    [Required]
    [MinLength(2)]
    [MaxLength(100)]
    public string Name
    {
        get => name;
        set => SetProperty(ref name, value, true);
    }
}

我們在這裡呼叫 SetProperty<T>(ref T, T, bool, string)ObservableValidator公開的方法,而且設定為 的其他 bool 參數 true 表示我們也想要在其值更新時驗證屬性。 ObservableValidator 會使用套用至 屬性的所有檢查,自動在每個新值上執行驗證。 然後,其他元件(例如 UI 控制件)可以與 viewmodel 互動,並修改其狀態,以反映 ViewModel 中目前存在的錯誤,方法是登錄 ErrorsChanged 並使用 GetErrors(string) 方法來擷取已修改之每個屬性的錯誤清單。

自訂驗證方法

有時候驗證屬性需要 ViewModel 才能存取其他服務、數據或其他 API。 根據案例和所需的彈性層級而定,將自定義驗證新增至屬性的方式有不同。 以下是如何使用 [CustomValidationAttribute] 型別來指出需要叫用特定方法來執行屬性的其他驗證的範例:

public class RegistrationForm : ObservableValidator
{
    private readonly IFancyService service;

    public RegistrationForm(IFancyService service)
    {
        this.service = service;
    }

    private string name;

    [Required]
    [MinLength(2)]
    [MaxLength(100)]
    [CustomValidation(typeof(RegistrationForm), nameof(ValidateName))]
    public string Name
    {
        get => this.name;
        set => SetProperty(ref this.name, value, true);
    }

    public static ValidationResult ValidateName(string name, ValidationContext context)
    {
        RegistrationForm instance = (RegistrationForm)context.ObjectInstance;
        bool isValid = instance.service.Validate(name);

        if (isValid)
        {
            return ValidationResult.Success;
        }

        return new("The name was not validated by the fancy service");
    }
}

在此情況下,我們有一個靜態 ValidateName 方法,會透過插入至 viewmodel 的服務,對 Name 屬性執行驗證。 這個方法會 name 接收使用中的屬性值和 ValidationContext 實例,其中包含 viewmodel 實例、正在驗證的屬性名稱,以及選擇性地使用或設定服務提供者和一些自定義旗標。 在此情況下,我們會從驗證內容擷取 RegistrationForm 實例,而從該處,我們會使用插入的服務來驗證 屬性。 請注意,此驗證會在其他屬性中指定的驗證旁邊執行,因此我們可以隨意結合自定義驗證方法和現有的驗證屬性,但我們想要。

自訂驗證屬性

執行自定義驗證的另一種方式是實作自定義 [ValidationAttribute] ,然後將驗證邏輯插入覆 IsValid 寫的方法。 相較於上述方法,這可提供額外的彈性,因為它可讓您輕鬆地在多個位置重複使用相同的屬性。

假設我們想要根據屬性相對於相同 viewmodel 中另一個屬性的相對值來驗證屬性。 第一個步驟是定義自定義 [GreaterThanAttribute],如下所示:

public sealed class GreaterThanAttribute : ValidationAttribute
{
    public GreaterThanAttribute(string propertyName)
    {
        PropertyName = propertyName;
    }

    public string PropertyName { get; }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        object
            instance = validationContext.ObjectInstance,
            otherValue = instance.GetType().GetProperty(PropertyName).GetValue(instance);

        if (((IComparable)value).CompareTo(otherValue) > 0)
        {
            return ValidationResult.Success;
        }

        return new("The current value is smaller than the other one");
    }
}

接下來,我們可以將此屬性新增至 viewmodel:

public class ComparableModel : ObservableValidator
{
    private int a;

    [Range(10, 100)]
    [GreaterThan(nameof(B))]
    public int A
    {
        get => this.a;
        set => SetProperty(ref this.a, value, true);
    }

    private int b;

    [Range(20, 80)]
    public int B
    {
        get => this.b;
        set
        {
            SetProperty(ref this.b, value, true);
            ValidateProperty(A, nameof(A));
        }
    }
}

在此情況下,我們有兩個數值屬性必須位於特定範圍中,而且彼此之間具有特定關聯性(A 必須大於 B)。 我們在第一個屬性上新增了新的 [GreaterThanAttribute] ,我們也在 setter B中新增了 ValidateProperty 對 的呼叫,以便在A每當B變更時再次進行驗證(因為其驗證狀態相依於它)。 我們只需要 ViewModel 中的這兩行程式代碼來啟用此自定義驗證,我們也會獲得可重複使用的自定義驗證屬性,在應用程式中的其他 ViewModel 中也很有用。 此方法也有助於程式代碼模組化,因為驗證邏輯現在與 viewModel 定義本身完全分離。

範例

  • 查看 範例應用程式 (適用於多個 UI 架構),以查看 MVVM 工具組的運作情形。
  • 您也可以在單元測試中找到更多範例。