MVVM 社区工具包:将 ObservableValidator 与非字符串属性结合使用

MVVM Community Toolkit: Using ObservableValidator with non-string properties

提问人:hackeyzach 提问时间:6/17/2023 最后编辑:hackeyzach 更新时间:6/23/2023 访问量:1144

问:

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

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
    }

    // ...
}

当我使用 将属性绑定到文本框时,我会自动在我的视图中获取显示在文本框周围的验证消息!Username<TextBox Text="{Binding Username, ValidatesOnNotifyDataErrors=True}" />

但是,我不知道我应该如何处理需要验证非字符串属性的情况。例如:

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()HasErrorfalse

我看到了一些方法来处理这些无法检测到的错误:

  1. 防止用户输入无效数据。这起初似乎是可行的,但对于更复杂的类型(例如,用户输入的 )。TimeSpan
  2. 为每个非字符串属性添加字符串字段。即使您这样做,我也不知道如何将验证错误从类型化属性传播到非类型化对应项,以便它们显示在相应的文本框周围。
  3. 可能是我没有想到的其他选择。

是否有任何推荐的方法来处理 MVVM 工具包的非字符串属性的转换错误?提前感谢您的帮助!ObservableValidator

WPF 验证 XAML MVVM WINDOWS-COMMUNITY-TOOLKIT

评论

1赞 Andy 6/17/2023
Int 不是字符串。不要将其设置为 strin.empty。也许让它变得 int?和 null。但这当然是无效的。最初可能是 int 和 0。
0赞 hackeyzach 6/17/2023
@Andy很好的收获——当我将内容复制到 StackOverflow 时,这只是一个错别字。

答:

0赞 Lukasz Szczygielek 6/17/2023 #1

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

评论

0赞 Andy 6/17/2023
该值根本不会传输到 viewmodel。
0赞 user21970328 6/18/2023 #2

这是一个运行时异常,不是由您使用的库引发的。在将值分配给 source 属性之前(当绑定引擎尝试从目标类型转换为源类型时)将引发异常。该异常与数据验证无关。stringint

TextBox.Text是 的 类型 。 是 的 类型 。引发异常的原因是绑定尝试将值从 分配给属性。没有隐式强制转换 from to 。如果源属性类型与目标属性类型不同(并且没有隐式类型转换),则标记扩展将使用默认转换器,以便例如,可以将值发送/分配给绑定目标属性。但这仅在存在默认转换时才有效。stringUser.AgeintstringTextBoxintstringintBindingintstring

例如,是纯数字,可以转换为使用默认转换器。但是是字母数字,并且标准转换为失败,绑定引擎抛出异常。"123"int"123abc"int

您可以:

  1. 类型品牌User.Agestring
  2. 添加要绑定的类型的属性,并将其转换为数据模型中的属性,例如,使用 from the property setterUser.AgeTextstringTextBoxintint.TryParse
  3. 实现一个 将绑定目标值从 转换为 ,例如借助 。IValueConverterstringintint.TryParse
  4. 扩展和实现例如 其中,输入从 转换为 或验证输入以确保有效的数值(以便默认转换器可以处理它)。值本身的验证(例如,如果数值在特定范围内)仍然在数据源(视图模型类)中实现TextBoxNumericTextBoxstringint

评论

0赞 hackeyzach 6/23/2023
我相信选项 4 是我的应用程序的最佳方法。实施修复后,我会将其添加到原始帖子中。感谢您的帮助!
0赞 6/23/2023
我同意。我个人认为 a 是最方便的可重用解决方案。NumericTextBox
1赞 Sheriff 6/18/2023 #3

如何使用 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
    }
}

评论

0赞 6/18/2023
请注意,原始问题与数据验证无关。这是由于 TextBox.Text 字符串属性绑定到 int 属性 (Age) 而引发的无效强制转换异常。在将值分配给源属性之前(因此在值符合验证条件之前)将引发异常。