需要从 ValidationBaseClass 公开 HasError 属性 以查看具有流畅验证的模型
Need to expose HasError property from ValidationBaseClass to view Model with fluent validation
我需要帮助在 SfTextInputLayout 中使用 SfTextBoxEx 实现 INotifyDataErrorInfo 接口。
WPF 与 MVVM 使用 Caliburn.Micro 框架。
请找到以下项目结构以了解问题。
1)
文件夹名称:基础设施
文件:ValidatedPropertyChangedBase.cs
--它包含 INotifyDataErrorInfo 的实现。
2)
文件夹名称:型号
文件:TestModel.cs
--包含两个继承自 ValidatedPropertyChangedBase 的属性
--还包含 class 用于使用 FluentValidation
验证属性
4)
文件夹名称:视图
文件:HomeView.xaml
--标准 window 有两个 SfTextInputLayout 和 SfTextBoxExt
4)
文件夹名称:ViewModel
文件:HomeViewModel.cs
--standard view model with Model as 属性 with Validation triggering.
-- 在这里,如果我在验证方法上放置断点,我可以看到返回的错误是正确的,但不知何故它不会触发 UI 来显示错误消息。
--我已经在将 属性 与文本框绑定时设置了 ValidatesOnNotifyDataError=true。
如有不妥请各位指点指点。
注意:我正在使用 syncfusion 控件,但我也尝试使用标准文本框,它没有触发 errorchanged 事件。
我还使用了 IntoifyDataError 实现,来自:https://www.thetechgrandma.com/2017/05/wpf-prism-inotifydataerrorinfo-and.html
--ValidatedPropertyChangedBase
public abstract class ValidatedPropertyChangedBase : PropertyChangedBase, INotifyDataErrorInfo
{
private readonly Dictionary<string, List<string>> _errors = new Dictionary<string, List<string>>();
public void SetError(string propertyName, string errorMessage)
{
if (!_errors.ContainsKey(propertyName))
_errors.Add(propertyName, new List<string> { errorMessage });
RaiseErrorsChanged(propertyName);
}
protected void ClearError(string propertyName)
{
if (_errors.ContainsKey(propertyName))
_errors.Remove(propertyName);
RaiseErrorsChanged(propertyName);
}
protected void ClearAllErrors()
{
var errors = _errors.Select(error => error.Key).ToList();
foreach (var propertyName in errors)
ClearError(propertyName);
}
public void RaiseErrorsChanged(string propertyName)
{
ErrorsChanged(this, new DataErrorsChangedEventArgs(propertyName));
}
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged = delegate { return; };
public bool HasErrors
{
get { return _errors.Any(x => x.Value != null && x.Value.Count > 0); }
}
public IEnumerable GetErrors(string propertyName)
{
if (String.IsNullOrEmpty(propertyName) ||
!_errors.ContainsKey(propertyName)) return null;
return _errors[propertyName];
}
}
--ViewModel
public class HomeViewModel : ValidatedPropertyChangedBase
{
private TestModel _model;
public TestModel Model
{
get { return _model; }
set
{
if (_model != value)
{
_model = value;
NotifyOfPropertyChange(() => Model);
}
}
}
public HomeViewModel()
{
Model = new TestModel();
}
public void ValidateData()
{
ClearAllErrors();
var validator = new TestModelValidation();
FluentValidation.Results.ValidationResult result = validator.Validate(Model);
foreach (var error in result.Errors)
{
SetError(error.PropertyName, error.ErrorMessage);
}
if (result.IsValid)
{
MessageBox.Show("Data good to save !");
}
}
}
--查看
<Window
x:Class="ValidationDemo.Views.HomeView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:convertor="clr-namespace:ValidationDemo.Infrastructure"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:ValidationDemo.Views"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:sf="http://schemas.syncfusion.com/wpf"
Title="HomeView"
Width="800"
Height="450"
mc:Ignorable="d">
<Window.Resources>
<Style
TargetType="sf:SfTextInputLayout">
<Setter Property="Width" Value="200" />
</Style>
</Window.Resources>
<StackPanel
HorizontalAlignment="Center"
VerticalAlignment="Center"
Orientation="Vertical">
<sf:SfTextInputLayout
Hint="First Name">
<sf:SfTextBoxExt
Text="{Binding Path=Model.FirstName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnNotifyDataErrors=True}" />
</sf:SfTextInputLayout>
<sf:SfTextInputLayout
Hint="Last Name">
<sf:SfTextBoxExt
Text="{Binding Path=Model.FirstName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnNotifyDataErrors=True}" />
</sf:SfTextInputLayout>
<TextBox
Text="{Binding Path=Model.FirstName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnNotifyDataErrors=True}" />
<TextBox
Text="{Binding Path=Model.FirstName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnNotifyDataErrors=True}" />
<Button
x:Name="ValidateData"
Content="Validate Data" />
</StackPanel>
--实施 Fluent 验证的模型:
public class TestModel : ValidatedPropertyChangedBase
{
private string _firstName;
public string FirstName
{
get { return _firstName; }
set
{
if (_firstName != value)
{
_firstName = value;
NotifyOfPropertyChange(() => FirstName);
}
}
}
private string _lastName;
public string LastName
{
get { return _lastName; }
set
{
if (_lastName != value)
{
_lastName = value;
NotifyOfPropertyChange(() => LastName);
}
}
}
}
public class TestModelValidation:AbstractValidator<TestModel>
{
public TestModelValidation()
{
RuleFor(t => t.FirstName)
.NotEmpty()
.WithMessage("Please enter first name");
RuleFor(t => t.FirstName)
.NotEmpty()
.WithMessage("Please enter last name");
}
}
能够找出问题,是通过控制从视图模型中公开 HasError。
ErrorText="{Binding RelativeSource={RelativeSource Mode=Self}, Path=InputView.(Validation.Errors), Converter={StaticResource EC}}"
HasError="{Binding RelativeSource={RelativeSource Mode=Self}, Path=InputView.(Validation.Errors), Converter={StaticResource ECO}}"
在带有转换器的控件上添加了属性并解决了问题。
我需要帮助在 SfTextInputLayout 中使用 SfTextBoxEx 实现 INotifyDataErrorInfo 接口。
WPF 与 MVVM 使用 Caliburn.Micro 框架。
请找到以下项目结构以了解问题。
1) 文件夹名称:基础设施 文件:ValidatedPropertyChangedBase.cs
--它包含 INotifyDataErrorInfo 的实现。
2) 文件夹名称:型号 文件:TestModel.cs --包含两个继承自 ValidatedPropertyChangedBase 的属性 --还包含 class 用于使用 FluentValidation
验证属性4) 文件夹名称:视图 文件:HomeView.xaml --标准 window 有两个 SfTextInputLayout 和 SfTextBoxExt
4) 文件夹名称:ViewModel 文件:HomeViewModel.cs --standard view model with Model as 属性 with Validation triggering. -- 在这里,如果我在验证方法上放置断点,我可以看到返回的错误是正确的,但不知何故它不会触发 UI 来显示错误消息。 --我已经在将 属性 与文本框绑定时设置了 ValidatesOnNotifyDataError=true。
如有不妥请各位指点指点。 注意:我正在使用 syncfusion 控件,但我也尝试使用标准文本框,它没有触发 errorchanged 事件。
我还使用了 IntoifyDataError 实现,来自:https://www.thetechgrandma.com/2017/05/wpf-prism-inotifydataerrorinfo-and.html
--ValidatedPropertyChangedBase
public abstract class ValidatedPropertyChangedBase : PropertyChangedBase, INotifyDataErrorInfo
{
private readonly Dictionary<string, List<string>> _errors = new Dictionary<string, List<string>>();
public void SetError(string propertyName, string errorMessage)
{
if (!_errors.ContainsKey(propertyName))
_errors.Add(propertyName, new List<string> { errorMessage });
RaiseErrorsChanged(propertyName);
}
protected void ClearError(string propertyName)
{
if (_errors.ContainsKey(propertyName))
_errors.Remove(propertyName);
RaiseErrorsChanged(propertyName);
}
protected void ClearAllErrors()
{
var errors = _errors.Select(error => error.Key).ToList();
foreach (var propertyName in errors)
ClearError(propertyName);
}
public void RaiseErrorsChanged(string propertyName)
{
ErrorsChanged(this, new DataErrorsChangedEventArgs(propertyName));
}
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged = delegate { return; };
public bool HasErrors
{
get { return _errors.Any(x => x.Value != null && x.Value.Count > 0); }
}
public IEnumerable GetErrors(string propertyName)
{
if (String.IsNullOrEmpty(propertyName) ||
!_errors.ContainsKey(propertyName)) return null;
return _errors[propertyName];
}
}
--ViewModel
public class HomeViewModel : ValidatedPropertyChangedBase
{
private TestModel _model;
public TestModel Model
{
get { return _model; }
set
{
if (_model != value)
{
_model = value;
NotifyOfPropertyChange(() => Model);
}
}
}
public HomeViewModel()
{
Model = new TestModel();
}
public void ValidateData()
{
ClearAllErrors();
var validator = new TestModelValidation();
FluentValidation.Results.ValidationResult result = validator.Validate(Model);
foreach (var error in result.Errors)
{
SetError(error.PropertyName, error.ErrorMessage);
}
if (result.IsValid)
{
MessageBox.Show("Data good to save !");
}
}
}
--查看
<Window
x:Class="ValidationDemo.Views.HomeView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:convertor="clr-namespace:ValidationDemo.Infrastructure"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:ValidationDemo.Views"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:sf="http://schemas.syncfusion.com/wpf"
Title="HomeView"
Width="800"
Height="450"
mc:Ignorable="d">
<Window.Resources>
<Style
TargetType="sf:SfTextInputLayout">
<Setter Property="Width" Value="200" />
</Style>
</Window.Resources>
<StackPanel
HorizontalAlignment="Center"
VerticalAlignment="Center"
Orientation="Vertical">
<sf:SfTextInputLayout
Hint="First Name">
<sf:SfTextBoxExt
Text="{Binding Path=Model.FirstName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnNotifyDataErrors=True}" />
</sf:SfTextInputLayout>
<sf:SfTextInputLayout
Hint="Last Name">
<sf:SfTextBoxExt
Text="{Binding Path=Model.FirstName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnNotifyDataErrors=True}" />
</sf:SfTextInputLayout>
<TextBox
Text="{Binding Path=Model.FirstName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnNotifyDataErrors=True}" />
<TextBox
Text="{Binding Path=Model.FirstName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnNotifyDataErrors=True}" />
<Button
x:Name="ValidateData"
Content="Validate Data" />
</StackPanel>
--实施 Fluent 验证的模型:
public class TestModel : ValidatedPropertyChangedBase
{
private string _firstName;
public string FirstName
{
get { return _firstName; }
set
{
if (_firstName != value)
{
_firstName = value;
NotifyOfPropertyChange(() => FirstName);
}
}
}
private string _lastName;
public string LastName
{
get { return _lastName; }
set
{
if (_lastName != value)
{
_lastName = value;
NotifyOfPropertyChange(() => LastName);
}
}
}
}
public class TestModelValidation:AbstractValidator<TestModel>
{
public TestModelValidation()
{
RuleFor(t => t.FirstName)
.NotEmpty()
.WithMessage("Please enter first name");
RuleFor(t => t.FirstName)
.NotEmpty()
.WithMessage("Please enter last name");
}
}
能够找出问题,是通过控制从视图模型中公开 HasError。
ErrorText="{Binding RelativeSource={RelativeSource Mode=Self}, Path=InputView.(Validation.Errors), Converter={StaticResource EC}}"
HasError="{Binding RelativeSource={RelativeSource Mode=Self}, Path=InputView.(Validation.Errors), Converter={StaticResource ECO}}"
在带有转换器的控件上添加了属性并解决了问题。