1.概要
目前在.NET8 Avalonia 11.0.10版本中有三种常见的数据验证方式:
IDataErrorInfo:这是一种较旧的数据验证方式,主要用于WPF和WinForms。在这种方法中,模型需要实现IDataErrorInfo接口,该接口包含一个索引器和一个Error属性。索引器用于获取特定属性的错误消息,Error属性用于获取对象级别的错误消息。
INotifyDataErrorInfo:这是一种更现代的数据验证方式,它允许异步验证并能提供多个错误消息。在这种方法中,模型需要实现INotifyDataErrorInfo接口,该接口包含一个ErrorsChanged事件和两个方法:GetErrors和HasErrors。GetErrors方法用于获取特定属性的错误消息,HasErrors属性用于确定对象是否有任何错误。
还有一种通过FluentValidation库进行数据验证的方式。
FluentValidation是一个流行的.NET库,用于构建强大、可维护的验证规则。它使用流畅的API创建验证规则,支持复杂的验证场景,并可以很好地与Avalonia一起工作。
使用FluentValidation,您可以在单独的验证类中定义规则,这样可以使模型类保持简洁。
在Avalonia中,最佳实践是使用INotifyDataErrorInfo接口进行数据验证。
2.详细内容
在以上的三种验证中我选择的是INotifyDataErrorInfo来实现,看到的一些示例实现的比较麻烦或者不够简洁我做了一些改进。
ValidateBindbleBase
因为INotifyDataErrorInfo需要在每个ViewModel或Model中进行数据验证,所以我将BindableBase和INotifyDataErrorInfo进行封装。这样继承了之后就不需要每次都编写相同的逻辑。
public class ValidateBindbleBase : BindableBase, INotifyDataErrorInfo
{
private readonly Dictionary<string, ICollection<string>> _validationErrors = new Dictionary<string, ICollection<string>>();
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
public bool HasErrors => _validationErrors.Count > 0;
public IEnumerable GetErrors(string propertyName)
{
//If is not returned, there will be two identical exception prompts on the view
return ;
}
protected void ValidateProperty<T>(string propertyName, T value)
{
if (value == ) return;
var context = new ValidationContext(this) { MemberName = propertyName };
var validationResults = new List<ValidationResult>();
Validator.TryValidateProperty(value, context, validationResults);
if (_validationErrors.ContainsKey(propertyName))
{
_validationErrors.Remove(propertyName);
}
HandleValidationResults(propertyName, validationResults);
}
private void HandleValidationResults(string propertyName, ICollection<ValidationResult> validationResults)
{
if (validationResults.Any())
{
_validationErrors.Add(propertyName, new List<string>());
foreach (var validationResult in validationResults)
{
_validationErrors[propertyName].Add(validationResult?.ErrorMessage);
}
ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(propertyName));
}
}
}
ViewModel
这里在ViewModel层做了一个简单的示例,ViewModel继承ValidateBindbleBase之后即获得了数据验证和数据通知的能力。再通过系统或自定义的验证特性进行附加,这样就可以复用验证规则避免出现大量的if else。
public class ViewModel : ValidateBindbleBase
{
private string _packetName;
[MinLength(2)]
[StringValidator]
public string PacketName
{
get => _packetName;
set
{
if (SetProperty(ref _packetName, value))
ValidateProperty(nameof(PacketName), value);
}
}
}
Validation
如果系统的验证特性不能满足需要,那么我们可以通过自定义特性继承ValidationAttribute,通过重写IsValid、FormatErrorMessage来实现自定义验证规则和验证错误提示。
public partial class StringValidator : ValidationAttribute
{
public override string FormatErrorMessage(string name)
{
return "The field is invaild!";
}
public override bool IsValid(object? value)
{
try
{
return StringRegex().IsMatch(value?.ToString());
}
catch
{
return false;
}
}
[GeneratedRegex(@"^[a-zA-Z0-9_\-\.\u4e00-\u9fa5]+$")]
private static partial Regex StringRegex();
}
View
在view层正常绑定即可。
<TextBox
Grid.Row="2"
Grid.Column="1"
Margin="5"
Text="{Binding CurrnetConfig.PacketName}" />
本文暂时没有评论,来添加一个吧(●'◡'●)