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