English 中文(简体)
WPF 在使用 IDataErrorInfo 更新源代码前先以 WPF 形式进行验证
原标题:WPF form validation before source update using IDataErrorInfo

I m trying to implement elegant form validation in a C# / WPF / Entity Framework 4.0 application. I have a classic form (some textboxes and checkboxes) and a save button.
I only want to update the source (and database) when the user presses save, and I also only want to run the form validation when the user presses save.

我用参数 UpdateSourceTrigger=Explicate 设置了所有绑定。我还将所有绑定都放置在 Binding Group 中。

当用户按下保存我时, 当用户触发约束组的 < code> update sources 方法 - 这触发了每个约束组的 < code> update source 。 此时点( 在源更新完成 < / strong > 之前的 < enger > ) 我要进行窗体验证, 并在 GUI 中突出错误 。 如果没有输入错误, 更新应该可以自由进行 。

我想我可以通过在 EntityObject 上执行 IDataErrorInfo 实现这一点,并在我所有的连接上设定参数 ValidatesOnDataErrors=True

Unfortunately this does not work because as explained here: MSDN Databinding Overview - Data Validation under the heading "Validation Process"

(5) 装订引擎设定源财产。

6)6)...

这对我来说似乎很愚蠢--为什么你想要验证数据 在它已经被“承诺”到这个对象之后?我一直在寻找一个好几个小时的方法来得到我想要的行为。

So the main question is:
How can I validate the input BEFORE the source is updated and cancel the update if the validation fails?

最佳回答

必须对对象承诺值, 因为 IDataErrorInfo 仅使用属性来检索特定属性的错误Name 。 无法将拟议值( 应该验证的值)传递到它, 因此只能使用已承诺的属性值 。

我认为这是一个好办法,因为观点模型和观点总是同步的,即使属性有无效价值,无效价值状态也保留在观点模型中,因此基于这一信息的额外逻辑可以包含在观点模型中,而不包含在观点模型中。

如果您想要传播拟议值验证以查看模型, 您将不得不使用您自己的自定义界面和验证规则来进行此操作 。

这就是我是如何完成的:

< 强度 > i 拟议数值ErrorInfo.cs

using System.Globalization;

namespace WpfApplication
{
    public interface IProposedValueErrorInfo
    {
        object GetError(string propertyName, object value, CultureInfo cultureInfo);
    }
}

<强度 > 提议值错误估价规则.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;
        }
    }
}

<强度>拟议估值导值Extension.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);
        }
    }
}

<强度 > 人/人.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
    }
}

< pronger > mainWindow.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>
问题回答

暂无回答




相关问题
Anyone feel like passing it forward?

I m the only developer in my company, and am getting along well as an autodidact, but I know I m missing out on the education one gets from working with and having code reviewed by more senior devs. ...

NSArray s, Primitive types and Boxing Oh My!

I m pretty new to the Objective-C world and I have a long history with .net/C# so naturally I m inclined to use my C# wits. Now here s the question: I feel really inclined to create some type of ...

C# Marshal / Pinvoke CBitmap?

I cannot figure out how to marshal a C++ CBitmap to a C# Bitmap or Image class. My import looks like this: [DllImport(@"test.dll", CharSet = CharSet.Unicode)] public static extern IntPtr ...

How to Use Ghostscript DLL to convert PDF to PDF/A

How to user GhostScript DLL to convert PDF to PDF/A. I know I kind of have to call the exported function of gsdll32.dll whose name is gsapi_init_with_args, but how do i pass the right arguments? BTW, ...

Linqy no matchy

Maybe it s something I m doing wrong. I m just learning Linq because I m bored. And so far so good. I made a little program and it basically just outputs all matches (foreach) into a label control. ...

热门标签