这是在WPF,Prism,MVVM上进行和显示验证的正确方法吗?

时间:2018-09-30 10:53:11

标签: c# wpf mvvm prism prism-6

我是WPF,Prism和MVVM的新手,但是我发现的信息很少。当涉及验证和显示验证错误时,我阅读了很多博客文章和文章,但似乎都不再起作用。

我要实现的目标是:

enter image description here

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造成的,但是后来我找不到进一步的自动行为来正确接线。我错过了什么大事吗?

0 个答案:

没有答案