首页 > 解决方案 > 特殊附加属性绑定有效,但不适用于 DataTemplate

问题描述

目标

我的目标是能够通过绑定为我的代码提供对 UI 元素的引用(而不是为元素提供 aName或必须手动遍历可视化树才能找到它)。

为此,我创建了一个特殊的附加依赖属性,名为Self. 它基于此答案中的代码。它旨在以两种方式变得特别:

  1. 的值Self应始终是对其设置的元素的引用。因此,如果Self在 a 上使用Button,其值Self应始终返回对 said 的引用Button
  2. 绑定到该Self属性时,应将其值推送到绑定源。

基本上,你应该能够做到这一点:

<Button Name="A" local:BindingHelper.Self="{Binding Foo.Button}"/>

然后Foo.ButtonButton A对象作为它的值。

主要代码

为了做到这一点,我从前面提到的答案中调整了代码并创建了这个:

Public Class BindingHelper
    Public Shared ReadOnly SelfProperty As DependencyProperty =
            DependencyProperty.RegisterAttached("Self", GetType(DependencyObject), GetType(BindingHelper),
                                                New FrameworkPropertyMetadata(Nothing, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
                                                                              AddressOf Self_PropertyChanged, AddressOf Self_CoerceValue))

    Public Shared Function GetSelf(element As DependencyObject) As DependencyObject
        Return element.GetValue(SelfProperty)
    End Function
    Public Shared Sub SetSelf(element As DependencyObject, value As DependencyObject)
        element.SetValue(SelfProperty, value)
    End Sub

    Private Shared Sub Self_PropertyChanged(d As DependencyObject, e As DependencyPropertyChangedEventArgs)
        If e.NewValue IsNot d Then UpdateSelfValue(d)
    End Sub

    Private Shared SelfCoercionInProgress As New HashSet(Of DependencyObject)

    Private Shared Function Self_CoerceValue(d As DependencyObject, baseValue As Object) As Object
        If baseValue IsNot d AndAlso Not SelfCoercionInProgress.Contains(d) Then
            SelfCoercionInProgress.Add(d)
            UpdateSelfValue(d)
            SelfCoercionInProgress.Remove(d)
        End If

        Return d
    End Function

    Private Shared Sub UpdateSelfValue(d As DependencyObject)
        Dim B = BindingOperations.GetBindingExpression(d, SelfProperty)

        If B IsNot Nothing AndAlso B.Status <> BindingStatus.Detached Then
            B.UpdateTarget()
            SetSelf(d, d)
            B.UpdateSource()
        Else
            SetSelf(d, d)
        End If
    End Sub
End Class

用于测试和错误重现的代码

一个简单的MainWindow.xaml.vb

Class MainWindow
    Public Property Foo As New Foo

    Private Sub Button_Click(sender As Object, e As RoutedEventArgs)
        'You can put a breakpoint here
    End Sub
End Class

Public Class Foo
    Public Property Button As Button
End Class

并且MainWindow.xaml

<Window x:Class="MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:VBTest"
        DataContext="{Binding RelativeSource={RelativeSource Self}}">

    <StackPanel>
        <Button Name="A" local:BindingHelper.Self="{Binding Foo.Button}" Click="Button_Click">A</Button>

        <ContentControl Content="{Binding Foo}">
            <ContentControl.ContentTemplate>
                <DataTemplate>
                    <!--<Button Name="B" local:BindingHelper.Self="{Binding Button}" Click="Button_Click">B</Button>-->
                </DataTemplate>
            </ContentControl.ContentTemplate>
        </ContentControl>
    </StackPanel>
</Window>

请注意,中的B行已DataTemplate被注释掉。如果您运行上面的代码,它会按预期工作。Foo.Button给出了参考Button A

现在,注释掉该A行并取消注释该B行。从理论上讲,它应该完全一样,我所做的只是将 移动Button到 aDataTemplate中,但由于某种原因Foo.Button从未给出对 . 的引用Button B。这是我需要帮助弄清楚的部分。如果无法在 aDataTemplate中使用它,我将永远无法在ItemsControl.

我到目前为止的进展

这个问题似乎与以下有关:

Dim B = BindingOperations.GetBindingExpression(d, SelfProperty)

这出乎意料地返回Nothingfor Button B,因此UpdateSource永远不会被调用。初始化/加载完成后,如果我尝试GetBindingExpression从断点调用,它会返回预期值,但无论出于何种原因,当目标在DataTemplate.

标签: wpfvb.netdata-bindingdependency-properties

解决方案


您应该能够通过添加以下内容来告诉ContentControl在哪里可以找到该Foo属性来使其工作:

RelativeSource={RelativeSource 模式=FindAncestor, AncestorType=Window}

<ContentControl Content="{Binding Foo, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}">

其他一切都与您上面的示例完全相同。


推荐阅读