首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >INotifyDataErrorInfo和绑定异常

INotifyDataErrorInfo和绑定异常
EN

Stack Overflow用户
提问于 2015-05-04 08:30:56
回答 3查看 2K关注 0票数 2

我正在使用INotifyDataErrorInfo接口来实现通用的MVVM验证机制。我通过调用OnValidate而不是OnPropertyChanged来实现接口:

代码语言:javascript
复制
public void OnValidate(dynamic value, [CallerMemberName] string propertyName = null)
{
        if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        Validate(propertyName, value);
}

在验证方法中,我生成验证错误,将它们添加到字典中,如果发现或清除验证错误,则引发ErrorsChanged事件:

代码语言:javascript
复制
if (entry.Validate(strValue, out errorNumber, out errorString) == false)
{
      _validationErrors[propertyName] = new List<string> {errorString};
      RaiseErrorsChanged(propertyName);
}
else if (_validationErrors.ContainsKey(propertyName))
{
      _validationErrors.Remove(propertyName);
      RaiseErrorsChanged(propertyName);
}

HasErrors属性是通过查看错误字典实现的:

代码语言:javascript
复制
    public bool HasErrors
    {
         get { return _validationErrors.Any(kv => kv.Value != null 
                   && kv.Value.Count > 0); }
    }

为了防止在验证错误时启用保存按钮,保存命令canExecuteMethod查看HasErrors属性:

代码语言:javascript
复制
private bool IsSaveEnabled()
{
    return HasErrors == false;
}

除了我有绑定错误的情况(例如,绑定值是一个整数,输入一个非整数)之外,所有操作都很好--文本框的ErrorContent被更新为一个错误字符串:“值‘某事’不能转换为”。但是INotifyDataErrorInfo机制没有对此进行更新。尽管视图中有错误,但HasErrors仍然是假的,并且已启用保存。我希望找到一种将绑定异常传播到INotifyDataErrorInfo机制的方法,以便能够:

  1. 禁用“保存”按钮(必须)。
  2. 将验证错误消息更改为更有意义的错误字符串(很好)。

我希望找到一个通用的MVVM解决方案,而不需要在视图后面添加代码。

谢谢你的帮助

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2015-05-06 07:53:01

这是我找到的解决办法。它使INotifyDataErrorInfo在viewModel中行为正确(当存在任何验证错误时-- HasError为真),并允许从ViewModel中添加验证错误。除此之外,它不需要更改视图、绑定或转换器中的更改。

这一解决办法包括:

  • 添加自定义验证规则。
  • 添加基本用户控件(所有视图都必须从该控件派生)。
  • 在ViewModel基中添加一些代码。

添加自定义验证规则-验证实体,该实体执行实际验证并在验证更改时引发事件:

代码语言:javascript
复制
class ValidationEntity : ValidationRule
{
   public string Key { get; set; }

   public string BaseName = "Base";

   public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
   {
       var fullPropertyName = BaseName + "." + Key;
       ValidationEntry entry;

       var validationResult = new ValidationResult(true, null);

       if ((entry = ValidationManager.Instance.FindValidation(fullPropertyName)) != null)
       {
           int errorNumber;
           string errorString;

           var strValue = (value != null) ? value.ToString() : string.Empty;

           if (entry.Validate(strValue, out errorNumber, out errorString) == false)
           {
               validationResult = new ValidationResult(false, errorString);
           }
       }

       if (OnValidationChanged != null)
       {
           OnValidationChanged(Key, validationResult);
       }
       return validationResult;
   }

   public event Action<string, ValidationResult> OnValidationChanged;
}

添加一个基本用户控件,该控件保存活动textbox的列表,并将验证规则添加到每个textbox绑定中:这是用户控件基上的代码:

代码语言:javascript
复制
private void OnLoaded(object sender, RoutedEventArgs routedEventArgs)
{
    _textBoxes = FindAllTextBoxs(this);

    var vm = DataContext as ViewModelBase;
    if (vm != null) vm.UpdateAllValidationsEvent += OnUpdateAllValidationsEvent;

    foreach (var textbox in _textBoxes)
    {
        var binding = BindingOperations.GetBinding(textbox, TextBox.TextProperty);

        if (binding != null)
        {
            var property = binding.Path.Path;
            var validationEntity = new ValidationEntity {Key = property};
            binding.ValidationRules.Add(validationEntity);
            validationEntity.ValidationChanged += OnValidationChanged;
        }
    }
}
private List<TextBox> FindAllTextBoxs(DependencyObject fe)
{
    return FindChildren<TextBox>(fe);
}

private List<T> FindChildren<T>(DependencyObject dependencyObject)
            where T : DependencyObject
{
    var items = new List<T>();

    if (dependencyObject is T)
    {
        items.Add(dependencyObject as T);
        return items;
    }

    var count = VisualTreeHelper.GetChildrenCount(dependencyObject);
    for (var i = 0; i < count; i++)
    {
        var child = VisualTreeHelper.GetChild(dependencyObject, i);
        var children = FindChildren<T>(child);
        items.AddRange(children);
    }
    return items;
}

当发生ValidationChange事件时,将调用视图以通知验证错误:

代码语言:javascript
复制
private void OnValidationChanged(string propertyName, ValidationResult validationResult)
{
    var vm = DataContext as ViewModelBase;

    if (vm != null)
    {
        if (validationResult.IsValid)
        {
            vm.ClearValidationErrorFromView(propertyName);
        }
        else
        {
            vm.AddValidationErrorFromView(propertyName, validationResult.ErrorContent as string);
        }
    }
}

ViewModel库保存两个列表:

  • _notifyvalidationErrors,INotifyDataErrorInfo接口用于显示验证错误。
  • _privateValidationErrors,用于向用户显示从验证规则生成的错误。

当从视图添加验证错误时-- _notifyvalidationErrors被更新为空值(只是表示存在验证错误)--错误字符串没有添加到_notifyvalidationErrors中。如果我们将它添加到其中,我们将在textbox ErrorContent中获得两次验证错误字符串。验证错误字符串也被添加到_privateValidationErrors中(因为我们希望能够将它保存在视图模型中)--这是ViewModel基础上的代码:

代码语言:javascript
复制
private readonly Dictionary<string, List<string>> _notifyvalidationErrors =
        new Dictionary<string, List<string>>();
private readonly Dictionary<string, List<string>> _privateValidationErrors =
            new Dictionary<string, List<string>>();

public void AddValidationErrorFromView(string propertyName, string errorString)
{
   _notifyvalidationErrors[propertyName] = new List<string>();
   // Add the error to the private dictionary
   _privateValidationErrors[propertyName] = new List<string> {errorString};
   RaiseErrorsChanged(propertyName);
}

 public void ClearValidationErrorFromView(string propertyName)
 {
     if (_notifyvalidationErrors.ContainsKey(propertyName))
     {
         _notifyvalidationErrors.Remove(propertyName);
     }
     if (_privateValidationErrors.ContainsKey(propertyName))
     {
         _privateValidationErrors.Remove(propertyName);
     }
     RaiseErrorsChanged(propertyName);

 }

视图中的INotifyDataErrorInfo实现:

代码语言:javascript
复制
public bool HasErrors
{
    get { return _notifyvalidationErrors.Any(kv => kv.Value != null); }
}
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;

public void RaiseErrorsChanged(string propertyName)
{
       var handler = ErrorsChanged;
       if (handler != null)
           handler(this, new DataErrorsChangedEventArgs(propertyName));
}
public IEnumerable GetErrors(string propertyName)
{
    List<string> errorsForProperty;
    _notifyvalidationErrors.TryGetValue(propertyName, out errorsForProperty);

    return errorsForProperty;
}

用户可以通过调用ViewModelBase、AddValidationError和ClearValidationError方法从视图中添加验证错误。

代码语言:javascript
复制
public void AddValidationError(string errorString, [CallerMemberName] string propertyName = null)
{
    _notifyvalidationErrors[propertyName] = new List<string>{ errorString };
    RaiseErrorsChanged(propertyName);
}

public void ClearValidationError([CallerMemberName] string propertyName = null)
{
    if (_notifyvalidationErrors.ContainsKey(propertyName))
    {
        _notifyvalidationErrors.Remove(propertyName);
        RaiseErrorsChanged(propertyName);
    }
}

视图可以通过调用ViewModel和GetValidationErrorsString方法从GetValidationErrors基获取当前所有验证错误的列表。

代码语言:javascript
复制
public List<string> GetValidationErrors()
{
    var errors = new List<string>();
    foreach (var key in _notifyvalidationErrors.Keys)
    {
        errors.AddRange(_notifyvalidationErrors[key]);

        if (_privateValidationErrors.ContainsKey(key))
        {
            errors.AddRange(_privateValidationErrors[key]);
        }
    }
    return errors;
}

public string GetValidationErrorsString()
{
    var errors = GetValidationErrors();
    var sb = new StringBuilder();
    foreach (var error in errors)
    {
        sb.Append("● ");
        sb.AppendLine(error);
    }
    return sb.ToString();
}
票数 1
EN

Stack Overflow用户

发布于 2015-05-04 12:49:23

string int情况不适用于MVVM,因为您的视图模型由于绑定异常而得不到任何信息。

我看到了获得所需验证的两种方法:

  1. 只需在视图模型中使用string属性,当必须转到模型时,只需将字符串转换为模型类型即可。
  2. 创建行为或“特殊”控件,以便您的视图中的输入始终“可转换”到您的视图模型类型。

顺便说一下,我使用第二种方法,因为我必须:),但是第一种方法总是有效的,对我来说似乎更容易。

票数 3
EN

Stack Overflow用户

发布于 2015-05-04 08:35:43

设置

ValidatesOnExceptions=“真”

在你的绑定表达式中。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/30025757

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档