Является ли это правильным способом отображения и проверки на WPF, Prism, MVVM? - PullRequest
0 голосов
/ 30 сентября 2018

Я новичок в 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 s:

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, но тогда я не смог найти дальнейшее автоматическое поведение для правильной связи.Я что-то упустил?

...