我正在尝试在我的 MVVM 项目中实现验证,并在 Model 上完成验证。下面是一个最小的例子:
看法:
Window x:Class="Test.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Test"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<local:VM x:Key="VM"/>
</Window.Resources>
<Grid DataContext="{StaticResource VM}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Label Grid.Row="0" Grid.Column="0" Content="Number: "/>
<TextBox Grid.Row="0" Grid.Column="1" Text="{Binding Number, UpdateSourceTrigger=PropertyChanged, ValidatesOnNotifyDataErrors=True}"
ToolTip="{Binding Path=(Validation.Errors)/ErrorContent, RelativeSource={RelativeSource Mode=Self}}"/>
<Label Grid.Row="1" Grid.Column="0" Content="Limit: "/>
<TextBox Grid.Row="1" Grid.Column="1" Text="{Binding Limit, UpdateSourceTrigger=PropertyChanged, ValidatesOnNotifyDataErrors=True}"
ToolTip="{Binding Path=(Validation.Errors)/ErrorContent, RelativeSource={RelativeSource Mode=Self}}"/>
</Grid>
</Window>
视图模型:
public class ViewModel : INotifyPropertyChanged, INotifyDataErrorInfo
{
private Model _model = new Model();
public int Number
{
get { return this._model.Number; }
set
{
if (value != this._model.Number)
this._model.Number = value;
}
}
public int Limit
{
get { return this._model.Limit; }
set
{
if (value != this._model.Limit)
this._model.Limit = value;
}
}
public ViewModel()
{
this._model.PropertyChanged += (sender, args) => this.PropertyChanged?.Invoke(sender, args);
this._model.ErrorsChanged += (sender, args) => this.ErrorsChanged?.Invoke(sender, args);
}
public bool HasErrors => this._model.HasErrors;
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
public event PropertyChangedEventHandler PropertyChanged;
public IEnumerable GetErrors(string propertyName) => this._model.GetErrors(propertyName);
}
模型:
public class Model : INotifyPropertyChanged, INotifyDataErrorInfo
{
private int _number;
public int Number
{
get => _number;
set
{
if (_number != value)
{
_number = value;
OnPropertyChanged();
//ValidateNumber();
}
}
}
private int _limit;
public int Limit
{
get => _limit;
set
{
if (_limit != value)
{
_limit = value;
OnPropertyChanged();
ValidateNumber();
}
}
}
private void ValidateNumber()
{
var propertyName = nameof(Number);
if (Number > Limit)
_errors[propertyName] = new[] { "Number cannot be larger than Limit." };
else
{
if (_errors.ContainsKey(propertyName))
_errors.Remove(propertyName);
}
OnErrorChanged(propertyName);
}
private Dictionary<string, IEnumerable<string>> _errors = new Dictionary<string, IEnumerable<string>>();
public bool HasErrors => _errors.Values.Any(err => err != null);
public event PropertyChangedEventHandler PropertyChanged;
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
public IEnumerable GetErrors(string propertyName) => _errors.ContainsKey(propertyName) ? _errors[propertyName] : Enumerable.Empty<object>();
private void OnPropertyChanged([CallerMemberName] string propertyName = null) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
private void OnErrorChanged([CallerMemberName] string propertyName = null) => ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(propertyName));
}
从示例中,Number
属性应该在任何时候被验证Limit
,并且验证错误Number
应该显示在视图上。但是,可以观察到验证方法正在执行,但Number
视图上的字段没有显示验证错误。
从这篇文章来看,似乎INotifyDataErrorInfo
应该支持这种跨属性验证。