将命令 canexecute 绑定到 IDataErrorInfo
Binding a commands canexecute to IDataErrorInfo
编辑
我的事情可能有点复杂,所以让我们保持简单。如果将整数绑定到文本框,如果在文本框中键入非法字符,则会出现验证异常。如何根据 属性 是否有验证异常来禁用按钮。
原题#
我正在使用 MVVM 方法在 WPF 中创建一个应用程序,但没有任何框架。
我的模型 类 实现了 IDataErrorInfo,如果发生错误,所有模型都有 HasError 属性 和 < 属性Name, ErrorMessage > 的字典。如果 HasError 属性 将我的命令上的 RaiseCanExecuteChanged 更改为 re-evaluate 如果我们现在可以保存。
这很好用,但只适用于像
这样的显式数据注释
[MaxLength(3,ErrorMessage = "The text can't be longer than 3")]
[CustomRequiredAttribute]
public string CountryCode
{
get { return m_CountryCode; }
set { SetProperty(ref m_CountryCode, value); }
}
public string m_CountryCode;
另一方面,如果我将整数绑定到文本框并输入非法字符(如字母),则不会触发 OnValidate 函数,因此不会将错误添加到我的 collection。
如何捕获所有验证错误并添加到我的 collection?
string IDataErrorInfo.this[string propertyName]
{
get
{
var error = OnValidate(propertyName);
return error;
}
}
这是我的 CanExecute
protected override bool CanHandleSaveCommand()
{
return !Feeds.Any(e => e.HasErrors) && IsEdited;
}
OnValidate 方法运行良好,只是为了让您了解完整情况
protected virtual string OnValidate(string propertyName)
{
if (string.IsNullOrEmpty(propertyName))
{
throw new ArgumentException("Invalid property name", propertyName);
}
var error_summary = new StringBuilder();
PropertyInfo property_info = GetType().GetProperty(propertyName);
var value = property_info.GetValue(this);
var validation_errors = new List<ValidationResult>();
var is_valid = Validator.TryValidateProperty(
value,
new ValidationContext(this, null, null)
{
MemberName = propertyName
},
validation_errors);
if (is_valid)
{
if (Errors.ContainsKey(propertyName))
{
Errors.Remove(propertyName);
PropertyChanged(this, new PropertyChangedEventArgs("HasErrors"));
HasErrorsChanged(this, EventArgs.Empty);
}
}
else
{
validation_errors.ForEach(e => error_summary.Append(e.ErrorMessage));
if (Errors.ContainsKey(propertyName))
Errors[propertyName] = error_summary.ToString();
else
Errors.Add(propertyName, error_summary.ToString());
PropertyChanged(this, new PropertyChangedEventArgs("HasErrors"));
HasErrorsChanged(this, EventArgs.Empty);
}
return error_summary.ToString();
}
您的代码没有被调用,因为 UI 不知道是否有错误。我通常做的是在我的 VM 中实现 INotifyDataErrorInfo
。当 属性 被更改时,我在 属性 setter 中验证 value
。然后,如果需要,我会触发 INotifyDataErrorInfo.ErrorsChangedEvent
。因此 UI 知道错误并做出相应的反应
当然你仍然可以在幕后使用IDataErrorInfo
。希望这有帮助
编辑
我明白你的意思了。我现在无法为您提供解决方案,但可以为您指明方向。我建议查看 Overriding metadata technique and Dependency propery callbacks。因此,您覆盖 TextBox.TextProperty
元数据并使用自定义回调来设置错误。
等我有空再回来讨论这个问题
我终于找到了我要找的答案。正如我之前所说,如果您输入非法字符,您的 属性 将永远不会被设置。 WPF 框架抛出转换错误异常并将其添加到 Validation.Errors 集合,但不会调用任何自定义验证代码。您必须改用 UpdateSourceExceptionFilter Property。
<TextBox>
<TextBox.Text>
<Binding
UpdateSourceExceptionFilter="ReturnExceptionHandler"
Path="CurrencyPotens"
UpdateSourceTrigger="PropertyChanged"
ValidatesOnDataErrors="True"></Binding>
</TextBox.Text>
</TextBox>
在代码隐藏中,我可以将我的绑定对象转换为我的 NotifyPropertyChanged class 并添加错误,这反过来会导致我的 ICommands CanExecute 重新计算。
public object ReturnExceptionHandler(object bindingExpression, Exception exception)
{
BindingExpression be = bindingExpression as BindingExpression;
var boundItem = be.DataItem;
((Wrapper.NotifyPropertyChanged)boundItem).Errors.Add(be.ResolvedSourcePropertyName, exception.Message);
return exception.Message;
}
现在我无法将它与我的 MVVM 方法一起使用,所以我在代码隐藏中遇到了一些代码。
编辑
我的事情可能有点复杂,所以让我们保持简单。如果将整数绑定到文本框,如果在文本框中键入非法字符,则会出现验证异常。如何根据 属性 是否有验证异常来禁用按钮。
原题#
我正在使用 MVVM 方法在 WPF 中创建一个应用程序,但没有任何框架。
我的模型 类 实现了 IDataErrorInfo,如果发生错误,所有模型都有 HasError 属性 和 < 属性Name, ErrorMessage > 的字典。如果 HasError 属性 将我的命令上的 RaiseCanExecuteChanged 更改为 re-evaluate 如果我们现在可以保存。
这很好用,但只适用于像
这样的显式数据注释 [MaxLength(3,ErrorMessage = "The text can't be longer than 3")]
[CustomRequiredAttribute]
public string CountryCode
{
get { return m_CountryCode; }
set { SetProperty(ref m_CountryCode, value); }
}
public string m_CountryCode;
另一方面,如果我将整数绑定到文本框并输入非法字符(如字母),则不会触发 OnValidate 函数,因此不会将错误添加到我的 collection。
如何捕获所有验证错误并添加到我的 collection?
string IDataErrorInfo.this[string propertyName]
{
get
{
var error = OnValidate(propertyName);
return error;
}
}
这是我的 CanExecute
protected override bool CanHandleSaveCommand()
{
return !Feeds.Any(e => e.HasErrors) && IsEdited;
}
OnValidate 方法运行良好,只是为了让您了解完整情况
protected virtual string OnValidate(string propertyName)
{
if (string.IsNullOrEmpty(propertyName))
{
throw new ArgumentException("Invalid property name", propertyName);
}
var error_summary = new StringBuilder();
PropertyInfo property_info = GetType().GetProperty(propertyName);
var value = property_info.GetValue(this);
var validation_errors = new List<ValidationResult>();
var is_valid = Validator.TryValidateProperty(
value,
new ValidationContext(this, null, null)
{
MemberName = propertyName
},
validation_errors);
if (is_valid)
{
if (Errors.ContainsKey(propertyName))
{
Errors.Remove(propertyName);
PropertyChanged(this, new PropertyChangedEventArgs("HasErrors"));
HasErrorsChanged(this, EventArgs.Empty);
}
}
else
{
validation_errors.ForEach(e => error_summary.Append(e.ErrorMessage));
if (Errors.ContainsKey(propertyName))
Errors[propertyName] = error_summary.ToString();
else
Errors.Add(propertyName, error_summary.ToString());
PropertyChanged(this, new PropertyChangedEventArgs("HasErrors"));
HasErrorsChanged(this, EventArgs.Empty);
}
return error_summary.ToString();
}
您的代码没有被调用,因为 UI 不知道是否有错误。我通常做的是在我的 VM 中实现 INotifyDataErrorInfo
。当 属性 被更改时,我在 属性 setter 中验证 value
。然后,如果需要,我会触发 INotifyDataErrorInfo.ErrorsChangedEvent
。因此 UI 知道错误并做出相应的反应
当然你仍然可以在幕后使用IDataErrorInfo
。希望这有帮助
编辑
我明白你的意思了。我现在无法为您提供解决方案,但可以为您指明方向。我建议查看 Overriding metadata technique and Dependency propery callbacks。因此,您覆盖 TextBox.TextProperty
元数据并使用自定义回调来设置错误。
等我有空再回来讨论这个问题
我终于找到了我要找的答案。正如我之前所说,如果您输入非法字符,您的 属性 将永远不会被设置。 WPF 框架抛出转换错误异常并将其添加到 Validation.Errors 集合,但不会调用任何自定义验证代码。您必须改用 UpdateSourceExceptionFilter Property。
<TextBox>
<TextBox.Text>
<Binding
UpdateSourceExceptionFilter="ReturnExceptionHandler"
Path="CurrencyPotens"
UpdateSourceTrigger="PropertyChanged"
ValidatesOnDataErrors="True"></Binding>
</TextBox.Text>
</TextBox>
在代码隐藏中,我可以将我的绑定对象转换为我的 NotifyPropertyChanged class 并添加错误,这反过来会导致我的 ICommands CanExecute 重新计算。
public object ReturnExceptionHandler(object bindingExpression, Exception exception)
{
BindingExpression be = bindingExpression as BindingExpression;
var boundItem = be.DataItem;
((Wrapper.NotifyPropertyChanged)boundItem).Errors.Add(be.ResolvedSourcePropertyName, exception.Message);
return exception.Message;
}
现在我无法将它与我的 MVVM 方法一起使用,所以我在代码隐藏中遇到了一些代码。