我陷入了一个看似普遍的要求.我有一个WPF Prism(用于MVVM)应用程序.我的模型实现了IDataErrorInfo以进行验证.该IDataErrorInfo的非数值属性的伟大工程.但是,对于数字属性,如果用户输入无效字符(非数字),则数据甚至不会到达模型,因为wpf无法将其转换为数字类型.
所以,我不得不使用WPF ValidationRule为用户提供一些有意义的无效数字条目消息.视图中的所有按钮都绑定到棱镜的DelegateCommand(在视图模型中),按钮的启用/禁用在View Model本身中完成.
现在,如果某个TextBox的wpf ValidationRule失败,如何将此信息传递给View Model,以便它可以适当地禁用视图中的按钮?
Pau*_*ler 10
对于MVVM,我更喜欢将附加属性用于此类事物,因为它们是可重用的并且它使视图模型保持干净.
为了将Validation.HasError属性绑定到视图模型,您必须创建一个附加属性,该属性具有CoerceValueCallback,该属性将附加属性的值与您正在验证用户输入的控件上的Validation.HasError属性同步.
此文章解释了如何使用这种技术来解决,通知WPF有效性规则的错误的视图模型的问题.代码在VB中,所以如果你不是VB的话,我把它移植到C#.
附属物
public static class ValidationBehavior
{
#region Attached Properties
public static readonly DependencyProperty HasErrorProperty = DependencyProperty.RegisterAttached(
"HasError",
typeof(bool),
typeof(ValidationBehavior),
new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, null, CoerceHasError));
private static readonly DependencyProperty HasErrorDescriptorProperty = DependencyProperty.RegisterAttached(
"HasErrorDescriptor",
typeof(DependencyPropertyDescriptor),
typeof(ValidationBehavior));
#endregion
private static DependencyPropertyDescriptor GetHasErrorDescriptor(DependencyObject d)
{
return (DependencyPropertyDescriptor)d.GetValue(HasErrorDescriptorProperty);
}
private static void SetHasErrorDescriptor(DependencyObject d, DependencyPropertyDescriptor value)
{
d.SetValue(HasErrorDescriptorProperty, value);
}
#region Attached Property Getters and setters
public static bool GetHasError(DependencyObject d)
{
return (bool)d.GetValue(HasErrorProperty);
}
public static void SetHasError(DependencyObject d, bool value)
{
d.SetValue(HasErrorProperty, value);
}
#endregion
#region CallBacks
private static object CoerceHasError(DependencyObject d, object baseValue)
{
var result = (bool)baseValue;
if (BindingOperations.IsDataBound(d, HasErrorProperty))
{
if (GetHasErrorDescriptor(d) == null)
{
var desc = DependencyPropertyDescriptor.FromProperty(System.Windows.Controls.Validation.HasErrorProperty, d.GetType());
desc.AddValueChanged(d, OnHasErrorChanged);
SetHasErrorDescriptor(d, desc);
result = System.Windows.Controls.Validation.GetHasError(d);
}
}
else
{
if (GetHasErrorDescriptor(d) != null)
{
var desc = GetHasErrorDescriptor(d);
desc.RemoveValueChanged(d, OnHasErrorChanged);
SetHasErrorDescriptor(d, null);
}
}
return result;
}
private static void OnHasErrorChanged(object sender, EventArgs e)
{
var d = sender as DependencyObject;
if (d != null)
{
d.SetValue(HasErrorProperty, d.GetValue(System.Windows.Controls.Validation.HasErrorProperty));
}
}
#endregion
}
Run Code Online (Sandbox Code Playgroud)
在XAML中使用附加属性
<Window
x:Class="MySolution.MyProject.MainWindow"
xmlns:v="clr-namespace:MyNamespace;assembly=MyAssembly">
<TextBox
v:ValidationBehavior.HasError="{Binding MyPropertyOnMyViewModel}">
<TextBox.Text>
<Binding
Path="ValidationText"
UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<v:SomeValidationRuleInMyNamespace/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
</ Window >
Run Code Online (Sandbox Code Playgroud)
现在,视图模型上的属性将与文本框上的Validation.HasError同步.
从 .NET 4.5 开始,ValidationRule 重载了 Validate 方法:
public ValidationResult Validate(object value, CultureInfo cultureInfo,
BindingExpressionBase owner)
Run Code Online (Sandbox Code Playgroud)
您可以覆盖它并通过以下方式获取视图模型:
public override ValidationResult Validate(object value,
CultureInfo cultureInfo, BindingExpressionBase owner)
{
ValidationResult result = base.Validate(value, cultureInfo, owner);
var vm = (YourViewModel)((BindingExpression)owner).DataItem;
// ...
return result;
}
Run Code Online (Sandbox Code Playgroud)
小智 6
NIRVAN
解决此特定问题的最简单方法是使用数字文本框,以防止用户输入无效值(您可以通过第三方供应商执行此操作,或查找开源解决方案,例如从Textbox派生的类这会抑制非数字输入).
在没有执行上述操作的情况下在MVVM中处理此问题的第二种方法是在ViewModel中定义另一个字段,该字段是字符串,并将该字段绑定到文本框.然后,在字符串字段的setter中,您可以设置Integer,并为数字字段赋值:
这是一个粗略的例子:(注意我没有测试它,但它应该给你的想法)
// original field
private int _age;
int Age
{
get { return _age; }
set {
_age = value;
RaisePropertyChanged("Age");
}
}
private string _ageStr;
string AgeStr
{
get { return _ageStr; }
set {
_ageStr = value;
RaisePropertyChanged("AgeStr");
if (!String.IsNullOrEmpty(AgeStr) && IsNumeric(AgeStr) )
Age = intVal;
}
}
private bool IsNumeric(string numStr)
{
int intVal;
return int.TryParse(AgeStr, out intVal);
}
#region IDataErrorInfo Members
public string this[string columnName]
{
get
{
if (columnName == "AgeStr" && !IsNumeric(AgeStr)
return "Age must be numeric";
}
}
#endregion
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
11825 次 |
最近记录: |