2012-05-23 11 views
5

C#/ WPF/Entity Framework 4.0アプリケーションでエレガントなフォーム検証を実装しようとしています。私は古典的なフォーム(テキストボックスとチェックボックス)と保存ボタンを持っています。
ユーザーが保存を押したときにのみソース(およびデータベース)を更新したいと思います。また、ユーザーが保存を押したときにフォームの検証を実行したいだけです。IDataErrorInfoを使用したソース更新前のWPFフォームの検証

すべてのバインディングをパラメータUpdateSourceTrigger=Explicitで設定しました。私もすべてのバインディングをBindingGroupに入れました。

ユーザーが保存を押すと、バインディンググループのUpdateSourcesメソッドがトリガーされます。これにより、各バインディングでUpdateSourceがトリガーされます。この時点で(が完了する前に)、フォームの検証が行われ、GUIでエラーが強調表示されるようにします。入力エラーがない場合、更新は自由に通過する必要があります。

フィールドがバインドされているIDataErrorInfoEntityObjectに実装し、すべてのバインドでパラメータValidatesOnDataErrors=Trueを設定することで、これを実現できると思いました。見出し「検証プロセス」

5)バインディングエンジンはソースプロパティを設定し、下 MSDN Databinding Overview - Data Validation :としてここで説明するので

は、残念ながら、これはない作業を行います。

6)......これは、ValidatesOnDataErrorsがtrueに設定されているバインディングがチェックされる時点です。

これは本当に私にとっては馬鹿げているようです。なぜ、データがオブジェクトに「コミット」された後に検証する必要があるのでしょうか?私は、私が望む行動を得るための時間を探してきました...誰かが前にこのような何かをしましたか?

だから、主な問題は、検証が失敗した場合にソースが更新され、更新がキャンセルされる前に、私は入力を検証するにはどうすればよい

答えて

1

IDataErrorInfoはpropertyNameのみを使用して特定のプロパティのエラーを取得するため、値をオブジェクトにコミットする必要があります。提案された値(検証する必要がある)を渡す方法はないので、コミットされたプロパティ値のみを使用できます。

プロパティが無効な値であり、無効な値の状態がビューモデルで保持されていても、ビューモデルとビューが常に同期されるため、その情報に基づいた追加のロジックをビューに含めることができるモデルであり、表示されません。

提案された値の検証をビューモデルに伝播したい場合は、独自のカスタムインターフェイスと検証ルールで行う必要があります。ここで

は、私はそれを達成する方法である:

IProposedValueErrorInfo.cs

using System.Globalization; 

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

ProposedValueErrorValidationRule。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; 
     } 
    } 
} 

ProposedValueValidationBindingExtension.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); 
     } 
    } 
} 

Person.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 
    } 
} 

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> 
関連する問題