1

Decorator を継承する ErrorProvider コントロールを開発しています。何かにバインドされているコントロール内の要素を検証します。これは、FrameworkElement 内のすべてのバインディングをループし、バインディングの ValidationRules に ExceptionValidationRule と DataErrorValidation を追加します。

これは、作業を行うメソッドです。

Private Sub ApplyValidationRulesToBindings()
    Dim bindings As Dictionary(Of FrameworkElement, List(Of Binding)) = GetBindings()

    For Each felement In bindings.Keys
        Dim knownBindings As List(Of Binding) = bindings(felement)

        For Each knownBinding As Binding In knownBindings
            'Applying Exception and Data Error validation rules'
            knownBinding.ValidationRules.Add(New ExceptionValidationRule())
            knownBinding.ValidationRules.Add(New DataErrorValidationRule())
        Next

    Next
End Sub

どうやら DataErrorValidationRule はバインドに適用されますが、ExceptionValidationRule は適用されません。

なぜこれが当てはまるのか誰にもわかりますか?

編集: わかりました。問題についてもう少し詳しく説明します。

Validation と Binding クラスに関する大量の MSDN ドキュメントを読んできました。Binding.UpdateSourceExceptionFilterプロパティを使用すると、バインディングにExceptionValidationRuleが関連付けられている場合に、バインディングで発生するすべての例外を処理する関数を指定できます。

UpdateSourceExceptionFilter プロパティのメソッドを追加しました。実行されました。しかし!!私は例外を返しましたが、MSDN のドキュメントでは、ValidationError オブジェクトはバインドされた要素の Validation.Errors コレクションに追加されませんでした...

ExceptionValidationRule を動的に追加するコードをコメントアウトし、次のように XAML の Binding に手動で追加しました。

<TextBox HorizontalAlignment="Left" Name="TextBox1" VerticalAlignment="Top" 
                                   Width="200">
  <TextBox.Text>
    <Binding Path="Name">
      <Binding.ValidationRules>
        <ExceptionValidationRule />
      </Binding.ValidationRules>
    </Binding>
  </TextBox.Text> 
</TextBox>

UpdateSourceException メソッドが実行され (私は変更しませんでした)、MSDN に記載されているように Validation.Errors にエラーが追加されました。

この全体で興味深いのは、VB.NET コードを介して行われると、実際には ExceptionValidationRule がバインディングに追加されるという事実です (または、UpdateSourceException が実行されることはありません)。ただし、Validate.Errors はエラーで更新されません。

前に述べたように、DataErrorValidationRule がバインディングに追加され、適切に機能します...ExceptionValidationRule に問題があるだけです。

私の解決策:

検証ルールをバインディングに適用するには、バインディングとプロパティに対してBindingOperations.SetBinding メソッドを呼び出す必要があることがわかりました。ApplyValidationRulesToBindingsメソッドの要素/バインディングの DependencyProperty を取得する方法がなかったため、ルールを適用するコードを、すべてのバインディングを再帰的に取得するメソッドを提供するコールバック メソッドに移動しました。

これが私の解決策です:

''' <summary>'
''' Gets and returns a list of all bindings. '
''' </summary>'
''' <returns>A list of all known bindings.</returns>'
Private Function GetBindings() As Dictionary(Of FrameworkElement, List(Of Binding))
    If _bindings Is Nothing OrElse _bindings.Count = 0 Then
        _bindings = New Dictionary(Of FrameworkElement, List(Of Binding))
        FindBindingsRecursively(Me.Parent, AddressOf RetrieveBindings)
    End If
    Return _bindings
End Function


''' <summary>'
''' Recursively goes through the control tree, looking for bindings on the current data context.'
''' </summary>'
''' <param name="element">The root element to start searching at.</param>'
''' <param name="callbackDelegate">A delegate called when a binding if found.</param>'
Private Sub FindBindingsRecursively(ByVal element As DependencyObject, ByVal callbackDelegate As FoundBindingCallbackDelegate)

    ' See if we should display the errors on this element'
    Dim members As MemberInfo() = element.[GetType]().GetMembers(BindingFlags.[Static] Or BindingFlags.[Public] Or BindingFlags.FlattenHierarchy)

    For Each member As MemberInfo In members
        Dim dp As DependencyProperty = Nothing
        ' Check to see if the field or property we were given is a dependency property'
        If member.MemberType = MemberTypes.Field Then
            Dim field As FieldInfo = DirectCast(member, FieldInfo)
            If GetType(DependencyProperty).IsAssignableFrom(field.FieldType) Then
                dp = DirectCast(field.GetValue(element), DependencyProperty)
            End If
        ElseIf member.MemberType = MemberTypes.[Property] Then
            Dim prop As PropertyInfo = DirectCast(member, PropertyInfo)
            If GetType(DependencyProperty).IsAssignableFrom(prop.PropertyType) Then
                dp = DirectCast(prop.GetValue(element, Nothing), DependencyProperty)
            End If

        End If
        If dp IsNot Nothing Then
            ' we have a dependency property. '
            'Checking if it has a binding and if so, checking if it is bound to the property we are interested in'
            Dim bb As Binding = BindingOperations.GetBinding(element, dp)
            If bb IsNot Nothing Then
                ' This element has a DependencyProperty that we know of that is bound to the property we are interested in. '
                ' Passing the information to the call back method so that the caller can handle it.'
                If TypeOf element Is FrameworkElement Then
                    If Me.DataContext IsNot Nothing AndAlso DirectCast(element, FrameworkElement).DataContext IsNot Nothing Then
                        callbackDelegate(DirectCast(element, FrameworkElement), bb, dp)
                    End If
                End If
            End If
        End If
    Next

    'Recursing through any child elements'
    If TypeOf element Is FrameworkElement OrElse TypeOf element Is FrameworkContentElement Then
        For Each childElement As Object In LogicalTreeHelper.GetChildren(element)
            If TypeOf childElement Is DependencyObject Then
                FindBindingsRecursively(DirectCast(childElement, DependencyObject), callbackDelegate)
            End If
        Next
    End If
End Sub

''' <summary>'
''' Called when recursively populating the Bindings dictionary with FrameworkElements(key) and their corresponding list of Bindings(value)'
''' </summary>'
''' <param name="element">The element the binding belongs to</param>'
''' <param name="binding">The Binding that belongs to the element</param>'
''' <param name="dp">The DependencyProperty that the binding is bound to</param>'
''' <remarks></remarks>'
Sub RetrieveBindings(ByVal element As FrameworkElement, ByVal binding As Binding, ByVal dp As DependencyProperty)
    'Applying an exception validation and data error validation rules to the binding' 
    'to ensure that validation occurs for the element'
    If binding.ValidationRules.ToList.Find(Function(x) GetType(ExceptionValidationRule) Is (x.GetType)) Is Nothing Then
        binding.ValidationRules.Add(New ExceptionValidationRule())
        binding.ValidationRules.Add(New DataErrorValidationRule())
        binding.UpdateSourceExceptionFilter = New UpdateSourceExceptionFilterCallback(AddressOf ReturnExceptionHandler)
        'Resetting the binding to include the validation rules just added'
        BindingOperations.SetBinding(element, dp, binding)
    End If

    ' Remember this bound element. This is used to display error messages for each property.'
    If _bindings.ContainsKey(element) Then
        DirectCast(_bindings(element), List(Of Binding)).Add(binding)
    Else
        _bindings.Add(element, New List(Of Binding)({binding}))
    End If
End Sub

ありがとう!

-フリニー

4

1 に答える 1

2

私はあなたのコードがどのように機能するかを実際にフォローしていませんが、使用後にバインディングを変更することはできないためValidationRule、既存のバインディングに s を追加することはできません。ValidationRuleBinding、Property の Property をコピーしてからs を追加し、新しいコピーされた Binding を で設定する必要があると思いますBindingOperations.SetBinding(...)

ValidationRule別のアプローチは、 s を直接追加する独自のサブクラス化された Binding を作成することです。

public class ExBinding : Binding
{
    public ExBinding()
    {
        NotifyOnValidationError = true;
        ValidationRules.Add(new ExceptionValidationRule());
        ValidationRules.Add(new DataErrorValidationRule());
    }
}

のように使える

<TextBox Text="{local:ExBinding Path=MyProperty}"/>

アップデート

あなたは私の答えを理解していないと思います。一度使用するとバインディングを変更できないため、実行しようとしていることが機能しません。これを示す C# サンプル アプリを次に示します。TextBox3 つの s が含まれています。

  • 最初TextBoxに、Xaml で ExceptionValidationRule を使用してバインディングを追加します
  • 次にTextBox、Xaml に Binding を追加し、Loaded イベントに ExceptionValidationRule を追加します。
  • 3 番目TextBoxに、Loaded イベントに ExceptionValidationRule を使用して Binding を追加します。

ExceptionValidationRule はTextBox1 と 3 では機能しますが、2 では機能しません。ここにサンプルをアップロードしました: http://www.mediafire.com/?venm09dy66q4rmq

Update 2
次のような検証ルールを追加した後にバインディングを再度設定すると、これが機能する可能性があります

BindingExpression bindingExpression = textBox.GetBindingExpression(TextBox.TextProperty);
Binding textBinding = bindingExpression.ParentBinding;
textBinding.ValidationRules.Add(new ExceptionValidationRule());
// Set the Binding again after the `ExceptionValidationRule` has been added
BindingOperations.SetBinding(textBox, TextBox.TextProperty, textBinding);

メソッドがどのように見えるかはわかりませんが、Bindings を再度設定するメソッドを追加し、 sを追加した後にそのメソッドを呼び出すことGetBindingsができるかもしれませんSetBindingsExceptionValidationRule

于 2011-02-18T20:49:44.747 に答える