我正在构建一个 wpf 应用程序,我在我的类中实现了IDataErrorInfo
接口。一切正常,textbox
被正确绑定,带有错误消息的工具提示正确显示,边框在属性更改时变为红色。
但是,当我单击“保存”按钮(创建我的实体并将其保存到我的数据库中)时,即使我的文本框中的值错误,它也会将错误的值保存在数据库中。
过去我在属性设置器中使用了ArgumentException
然后,在保存按钮单击时,会显示一个消息框,其中包含try / catch中的错误消息,但未保存错误的值。 / p>
是否有类似的方法来实现像try / catch一样的消息框但使用IDataErrorInfo
?
我不是在寻找复杂的解决方案,因为我是初学者,这是我第一次尝试 IDataErrorInfo 。
答案 0 :(得分:2)
由于评论,我已将答案更新为更详细的答案。
首先, Newspeak是Ingsoc,Ingsoc是Newspeak XAML是MVVM,MVVM是XAML。
要编写与" Hello,world"不同的东西,您必须特别学习MVVM和命令。以下代码使用以下ICommand
实现:
public sealed class RelayCommand : ICommand
{
private readonly Action execute;
private readonly Func<bool> canExecute;
public RelayCommand(Action execute, Func<bool> canExecute)
{
this.execute = execute;
this.canExecute = canExecute;
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public bool CanExecute(object parameter)
{
return canExecute();
}
public void Execute(object parameter)
{
execute();
}
}
在实际应用程序中,WPF验证使用了一些众所周知的验证框架,并且几乎从未在需要验证的每个视图模型中手动实现。
以下使用data annotations在基类中实现的IDataErrorInfo
验证示例:
public abstract class ViewModel : INotifyPropertyChanged, IDataErrorInfo
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName]string propertyName = null)
{
// updating property-bound controls
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
// updating command-bound controls like buttons
CommandManager.InvalidateRequerySuggested();
}
private readonly ObservableCollection<ViewModelError> validationErrors = new ObservableCollection<ViewModelError>();
private void RemoveValidationErrors(string propertyName)
{
var propertyValidationErrors = validationErrors
.Where(_ => _.PropertyName == propertyName)
.ToArray();
foreach (var error in propertyValidationErrors)
{
validationErrors.Remove(error);
}
}
private string ValidateProperty(string propertyName)
{
// we need localized property name
var property = GetType().GetProperty(propertyName);
var displayAttribute = property.GetCustomAttribute<DisplayAttribute>();
var propertyDisplayName = displayAttribute != null ? displayAttribute.GetName() : propertyName;
// since validation engine run all validation rules for property,
// we need to remove validation errors from the previous validation attempt
RemoveValidationErrors(propertyDisplayName);
// preparing validation engine
var validationContext = new ValidationContext(this, null, null) { MemberName = propertyName };
var validationResults = new List<ValidationResult>();
// running validation
if (!Validator.TryValidateProperty(property.GetValue(this), validationContext, validationResults))
{
// validation is failed;
// since there could be several validation rules per property,
// validation results can contain more than single error
foreach (var result in validationResults)
{
validationErrors.Add(new ViewModelError(propertyDisplayName, result.ErrorMessage));
}
// to indicate validation error, it's enough to return first validation message
return validationResults[0].ErrorMessage;
}
return null;
}
public IEnumerable<ViewModelError> ValidationErrors
{
get { return validationErrors; }
}
public string this[string columnName]
{
get { return ValidateProperty(columnName); }
}
public string Error
{
get { return null; }
}
}
ViewModelError
只是一个容器:
public sealed class ViewModelError
{
public ViewModelError(string propertyName, string errorMessage)
{
PropertyName = propertyName;
ErrorMessage = errorMessage;
}
public string PropertyName { get; private set; }
public string ErrorMessage { get; private set; }
}
让我们看一下模型,它代表一些人物数据,相应的视图模型和视图:
a)模型
public class Person
{
public Guid Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
}
b)查看模型
public class PersonViewModel : ViewModel
{
private void HandleSave()
{
var person = new Person
{
Id = Guid.NewGuid(),
Name = Name,
Age = Age
};
// save person using database, web service, file...
}
private bool CanSave()
{
return !ValidationErrors.Any();
}
public PersonViewModel()
{
SaveCommand = new RelayCommand(HandleSave, CanSave);
}
[Display(Name = "Full name of person")]
[Required(AllowEmptyStrings = false)]
[MaxLength(50)]
public string Name
{
get { return name; }
set
{
if (name != value)
{
name = value;
OnPropertyChanged();
}
}
}
private string name;
[Display(Name = "Age of person")]
[Range(18, 65)]
public int Age
{
get { return age; }
set
{
if (age != value)
{
age = value;
OnPropertyChanged();
}
}
}
private int age;
public ICommand SaveCommand { get; private set; }
}
c)视图(WPF窗口)
<Window x:Class="Wpf_IDataErrorInfoSample.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:Wpf_IDataErrorInfoSample"
mc:Ignorable="d"
Title="MainWindow" Height="300" Width="400">
<Window.DataContext>
<local:PersonViewModel />
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- Validation summary -->
<ItemsControl ItemsSource="{Binding ValidationErrors}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type local:ViewModelError}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding PropertyName}"/>
<TextBlock Text=" : "/>
<TextBlock Text="{Binding ErrorMessage}"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<!-- Editable fields -->
<StackPanel Grid.Row="1" Margin="4, 10, 4, 4">
<!-- Name -->
<TextBlock Text="Name:"/>
<TextBox Text="{Binding Name, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}"/>
<!-- Age -->
<TextBlock Text="Age:"/>
<Slider Minimum="0" Maximum="120" TickPlacement="BottomRight" Value="{Binding Age, ValidatesOnDataErrors=True}"/>
</StackPanel>
<Button Grid.Row="2" Content="Save" Command="{Binding SaveCommand}"/>
</Grid>
</Window>
如果您运行此代码,初始图片将如下所示:
如您所见,如果存在验证错误,则会禁用按钮,并且您无法保存无效数据。如果您修复错误,按钮将变为启用状态,您可以单击它以保存数据: