XAML MVVM社区工具包:使用具有非字符串属性的ObservableValidator

kkih6yb8  于 2023-06-19  发布在  其他
关注(0)|答案(3)|浏览(115)

我有一个WPF应用程序,它使用ObservableValidator来处理使用数据注解的属性验证。这对于string属性非常有效。例如:

public class LoginViewModel : ObservableValidator
{
    [ObservableProperty]
    [NotifyDataErrorInfo]
    [Required]
    [StringLength(64, MinimumLength = 8)]
    private string username = String.Empty;

    [RelayCommand]
    private void LogIn()
    {
        ValidateAllProperties();

        if(HasErrors)
        {
            return;
        }
        
        // Log in the user
    }

    // ...
}

当我使用<TextBox Text="{Binding Username, ValidatesOnNotifyDataErrors=True}" />Username属性绑定到一个文本框时,我会在视图中自动获得验证消息,这些消息会出现在文本框周围!
但是,我不知道如何处理需要验证非字符串属性的情况。例如:

public class User : ObservableValidator
{
    [ObservableProperty]
    [NotifyDataErrorInfo]
    [Required]
    [Range(0, 200)]
    private int age = 10;
}

如果我使用与<TextBox Text="{Binding Age, ValidatesOnNotifyDataErrors=True}" />相同的方法,并且用户输入的值不是像"12aaa"这样的整数,则绑定上的默认值转换器将抛出一个异常,表明"12aaa" cannot be converted into an integer。这些值转换异常无法从我的视图模型中检测到,因为绑定引擎从不更新属性值。
因此,即使用户输入了无效数据,调用ValidateAllProperties()也会将HasError设置为false
我看到了几种处理这种无法检测到的错误的方法:
1.防止用户输入无效数据。这在一开始似乎是可行的,但是对于更复杂的类型(例如,用户输入TimeSpan)变得更难。
1.为每个非字符串属性添加字符串字段。即使这样做,我也不知道如何将验证错误从类型化属性传播到非类型化属性,以便它们显示在相应的文本框周围。
1.可能还有其他我没想到的选择。
对于MVVM Toolkit的ObservableValidator,有没有处理非字符串属性转换错误的推荐方法?提前感谢您的帮助!

tyu7yeag

tyu7yeag1#

您应该尝试自定义验证方法或自定义验证属性。

llmtgqce

llmtgqce2#

如何使用CustomValidation

public partial class LoginViewModel : ObservableValidator
{
    public LoginViewModel()
    {
        ValidateAllProperties();
    }

    [ObservableProperty]
    [NotifyDataErrorInfo]
    [Required]
    [StringLength(64, MinimumLength = 8)]
    private string? username = String.Empty;

    [ObservableProperty]
    [NotifyDataErrorInfo]
    [Required]
    [Range(0, 200)]
    [CustomValidation(typeof(LoginViewModel), nameof(ValidateAge))]
    private string? age = "10";

    public static ValidationResult ValidateAge(string age, ValidationContext context)
    {
        if (int.TryParse(age, out int number))
        {
            return ValidationResult.Success;
        }

        return new("Validation failed/show error message");
    }

    [RelayCommand]
    private void LogIn()
    {
        ValidateAllProperties();

        if (HasErrors)
        {
            return;
        }

        // Log in the user
    }
}
bpzcxfmw

bpzcxfmw3#

这是一个运行时异常,不是由您使用的库引发的。在 * 将值赋给源属性之前 * 抛出异常(当绑定引擎尝试从目标类型string转换为源类型int时)。该异常与数据验证无关。
TextBox.Text的类型为stringUser.Age的类型为int。引发异常的原因是绑定试图将TextBox中的string值赋给int属性。没有从stringint的隐式转换。如果源属性类型与目标属性类型不同(并且没有隐式类型转换),Binding标记扩展将使用默认转换器,例如:可以将int值发送/分配给string绑定目标属性。但这仅在存在默认转换的情况下有效。
例如,"123"是纯数字,可以使用默认转换器转换为int。但是"123abc"是字母数字的,到int的标准转换失败,绑定引擎抛出异常。
您可以:
1.使User.Age的类型为string
1.添加string类型的User.AgeText属性以绑定到TextBox,并在数据模型中将其转换为int(例如使用属性设置器中的int.TryParse
1.实现一个IValueConverter,将绑定目标值从string转换为int,例如:使用int.TryParse
1.扩展TextBox并实现例如NumericTextBox,其中输入从string转换为int,或者验证输入以确保有效的数值(以便默认转换器可以处理它)。值本身的验证(例如如果数值在特定范围内)仍然在数据源(视图模型类)中实现

相关问题