我是WPF,Prism和MVVM的新手,但是我发现的信息很少。当涉及验证和显示验证错误时,我阅读了很多博客文章和文章,但似乎都不再起作用。
我要实现的目标是:
ViewModel(为简化起见,减去了其他字段)似乎有很多样板,也许我遗漏了一些东西?我是按照存储和处理错误的方式重新发明轮子吗?
namespace Configurator.ViewModels {
public class RegistrationViewModel : BindableBase, INotifyDataErrorInfo {
private string _email;
public string Email {
get { return _email; }
set { SetProperty(ref _email, value); }
}
public DelegateCommand RegisterCommand { get; private set; }
public RegistrationViewModel() {
RegisterCommand = new DelegateCommand(Register);
ClearAllErrors();
}
private void Register() {
ClearAllErrors();
if (String.IsNullOrWhiteSpace(Email)) {
SetError("Email", "We need your email address to register you.");
}
}
private readonly Dictionary<string, List<string>> _errors = new Dictionary<string, List<string>>();
public Dictionary<string, List<string>> Errors {
get { return _errors; }
}
public void SetError(string propertyName, string errorMessage) {
if (!_errors.ContainsKey(propertyName)) {
_errors.Add(propertyName, new List<string>());
}
_errors[propertyName].Add(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));
RaisePropertyChanged("Errors");
}
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged = delegate { return; };
public IEnumerable GetErrors(string propertyName) {
if (String.IsNullOrEmpty(propertyName) || !_errors.ContainsKey(propertyName)) {
return null;
}
return _errors[propertyName];
}
public bool HasErrors {
get { return _errors.Any(x => x.Value != null && x.Value.Count > 0); }
}
}
}
当涉及到视图时,我必须添加以下内容:
<UserControl.Resources>
<local:ErrorFormatter x:Key="ErrorFormatter" />
<local:ErrorPresent x:Key="ErrorPresent" />
</UserControl.Resources>
每个视图都需要吗?然后字段本身看起来像这样:
<Label Grid.Column="0" Grid.Row="4" Content="Email:" HorizontalContentAlignment="Right" Margin="6"/>
<TextBox Grid.Column="1" Grid.Row="4" x:Name="email" Margin="6" Text="{Binding Email}"/>
<TextBlock Grid.Column="1" Grid.Row="5" Margin="6,0,6,6" Foreground="Red"
Text="{Binding Errors, Converter={StaticResource ErrorFormatter}, ConverterParameter=Email}"
Visibility="{Binding Errors, Converter={StaticResource ErrorPresent}, ConverterParameter=Email}"/>
绑定到Errors
是否正确?我已经看到了一些示例,其中将其绑定到Errors[Email]
,但是如果该键不存在,则会引发异常。
这意味着我必须创建两个IValueConverter
:
public sealed class ErrorFormatter : IValueConverter {
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
var allErrors = (Dictionary<string, List<string>>)value;
var fieldName = (string)parameter;
if (allErrors.ContainsKey(fieldName) && allErrors[fieldName].Count > 0) {
foreach (string error in allErrors[fieldName]) {
Console.WriteLine(error);
}
return String.Join("\n", allErrors[fieldName]).Trim();
} else {
return null;
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
throw new NotImplementedException();
}
}
和
class ErrorPresent : IValueConverter {
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
var allErrors = (Dictionary<string, List<string>>)value;
var fieldName = (string)parameter;
if (allErrors.ContainsKey(fieldName) && allErrors[fieldName].Count > 0) {
return "Visible";
} else {
return "Collapsed";
};
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
throw new NotImplementedException();
}
}
感觉就像任何需要显示验证错误的人也必须写这些一样,所以,我是否错过了某些内容?我注意到一些使用BooleanToVisibilityConverter
的表达式,但是我无法按照它们存储错误的方式来使用它们?
感觉像是闻起来的是,这些字段被自动标记为红色边框,我认为这是由于INotifyDataErrorInfo
造成的,但是后来我找不到进一步的自动行为来正确接线。我错过了什么大事吗?