flo*_*lim 6 c# data-binding validation wpf entity-framework-4
我正在尝试在C#/ WPF/Entity Framework 4.0应用程序中实现优雅的表单验证.我有一个经典的表单(一些文本框和复选框)和一个保存按钮.
我只想在用户按下save时更新源(和数据库),我也只想在用户按下save时运行表单验证.
我用参数设置了所有绑定UpdateSourceTrigger=Explicit.我还将所有绑定放在一个BindingGroup.
当用户按下save时,触发UpdateSources绑定组的方法 - 这会触发UpdateSource每个绑定.此时(源更新完成之前)我希望进行表单验证,并在GUI中突出显示错误.如果没有输入错误,则更新应该可以自由进行.
我以为我可以通过执行实现这一目标IDataErrorInfo的EntityObject绑定到田间地头,并设置参数ValidatesOnDataErrors=True在我的所有绑定.
不幸的是这并没有工作,因为这里解释: MSDN数据绑定概述-数据验证 标题为"验证流程"下
5)绑定引擎设置source属性.
6)......这是检查将ValidatesOnDataErrors设置为true的绑定的点.
这对我来说似乎真的很愚蠢 - 为什么要在数据已经"提交"到对象之后验证数据?我一直在寻找一种方法来获得我想要的行为......有人曾经做过这样的事吗?
所以主要的问题是:
如何在更新源之前验证输入并在验证失败时取消更新?
必须将值提交给对象,因为 IDataErrorInfo 仅使用 propertyName 来检索特定属性的错误。无法将建议值(应验证的值)传递给它,因此只能使用提交的属性值。
我认为这是一个很好的方法,因为视图模型和视图总是同步的,即使属性具有无效值并且视图模型中保留了无效值状态,因此基于该信息的附加逻辑可以包含在视图模型中而不是视图中。
如果您想将建议的值验证传播到视图模型,您将必须使用自己的自定义界面和验证规则来完成此操作。
这是我完成它的方法:
IProposeValueErrorInfo.cs
using System.Globalization;
namespace WpfApplication
{
public interface IProposedValueErrorInfo
{
object GetError(string propertyName, object value, CultureInfo cultureInfo);
}
}
Run Code Online (Sandbox Code Playgroud)
ProposeValueErrorValidationRule.cs
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
namespace WpfApplication
{
internal sealed class ProposedValueErrorValidationRule : ValidationRule
{
private readonly DependencyObject targetObject;
private readonly DependencyProperty targetProperty;
public ProposedValueErrorValidationRule(DependencyObject targetObject, DependencyProperty targetProperty)
: base(ValidationStep.RawProposedValue, true)
{
if (targetObject == null)
throw new ArgumentNullException("targetObject");
if (targetProperty == null)
throw new ArgumentNullException("targetProperty");
this.targetObject = targetObject;
this.targetProperty = targetProperty;
}
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
var expression = BindingOperations.GetBindingExpression(this.targetObject, this.targetProperty);
if (expression != null)
{
var sourceItem = expression.DataItem as IProposedValueErrorInfo;
if (sourceItem != null)
{
var propertyName = expression.ParentBinding.Path != null ? expression.ParentBinding.Path.Path : null;
if (propertyName != null)
{
var error = sourceItem.GetError(propertyName, value, cultureInfo);
if (error != null)
return new ValidationResult(false, error);
}
}
}
return ValidationResult.ValidResult;
}
}
}
Run Code Online (Sandbox Code Playgroud)
ProposeValueValidationBindingExtension.cs
using System;
using System.Windows;
using System.Windows.Data;
using System.Windows.Markup;
namespace WpfApplication
{
public sealed class ProposedValueValidationBindingExtension : MarkupExtension
{
private readonly Binding binding;
public ProposedValueValidationBindingExtension(Binding binding)
{
if (binding == null)
throw new ArgumentNullException("binding");
this.binding = binding;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
var provideValueTarget = serviceProvider != null ? serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget : null;
if (provideValueTarget != null)
this.binding.ValidationRules.Add(new ProposedValueErrorValidationRule(provideValueTarget.TargetObject as DependencyObject, provideValueTarget.TargetProperty as DependencyProperty));
return this.binding.ProvideValue(serviceProvider);
}
}
}
Run Code Online (Sandbox Code Playgroud)
人物.cs
using System.Globalization;
namespace WpfApplication
{
public class Person : IProposedValueErrorInfo
{
public int Age { get; set; }
public string Surname { get; set; }
#region IProposedValueErrorInfo Members
object IProposedValueErrorInfo.GetError(string propertyName, object value, CultureInfo cultureInfo)
{
switch (propertyName)
{
case "Age":
int dummy;
return value is int || int.TryParse(value as string, NumberStyles.Integer, cultureInfo, out dummy) ? null : "Age must be a number.";
}
return null;
}
#endregion
}
}
Run Code Online (Sandbox Code Playgroud)
主窗口.xaml
<Window x:Class="WpfApplication.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:Person Age="16"/>
</Window.DataContext>
<StackPanel>
<TextBox Text="{local:ProposedValueValidationBinding {Binding Age}}" ToolTip="{Binding Path='(Validation.Errors)/ErrorContent', RelativeSource={RelativeSource Self}}"/>
<TextBox Text="{local:ProposedValueValidationBinding {Binding Age}}" ToolTip="{Binding Path='(Validation.Errors)/ErrorContent', RelativeSource={RelativeSource Self}}"/>
</StackPanel>
</Window>
Run Code Online (Sandbox Code Playgroud)