wpf - 特殊附加属性绑定有效,但不适用于 DataTemplate
问题描述
目标
我的目标是能够通过绑定为我的代码提供对 UI 元素的引用(而不是为元素提供 aName
或必须手动遍历可视化树才能找到它)。
为此,我创建了一个特殊的附加依赖属性,名为Self
. 它基于此答案中的代码。它旨在以两种方式变得特别:
- 的值
Self
应始终是对其设置的元素的引用。因此,如果Self
在 a 上使用Button
,其值Self
应始终返回对 said 的引用Button
。 - 绑定到该
Self
属性时,应将其值推送到绑定源。
基本上,你应该能够做到这一点:
<Button Name="A" local:BindingHelper.Self="{Binding Foo.Button}"/>
然后Foo.Button
将Button
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)
这出乎意料地返回Nothing
for Button
B
,因此UpdateSource
永远不会被调用。初始化/加载完成后,如果我尝试GetBindingExpression
从断点调用,它会返回预期值,但无论出于何种原因,当目标在DataTemplate
.
解决方案
您应该能够通过添加以下内容来告诉ContentControl
在哪里可以找到该Foo
属性来使其工作:
RelativeSource={RelativeSource 模式=FindAncestor, AncestorType=Window}
<ContentControl Content="{Binding Foo, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}">
其他一切都与您上面的示例完全相同。
推荐阅读
- odoo - 如何使用控制器 odoo 13 在网站中显示二进制字段数据
- php - 我没有收到来自 PHP mail() 函数的邮件
- r - R: dplyr::filter 中的 IF 语句需要 ELSE 否则会失败?
- reactjs - 如何将 typescript 应用于样式化组件,以便在组件上看到推荐的道具?
- python - 使用 django admin 创建学生帐户并使用它们进行登录
- libgdx - 将颜色转换应用于纹理的非常像素 (Libgdx)
- android - Firebase Android 自定义列表视图
- kubernetes - 有没有办法控制每个 pod 的读/写速度或 IOPS 限制?
- calendar - 谷歌日历 API 从谷歌日历设置中获取工作时间数据
- leaflet - Leaflet LatLng 坐标到 xy 地图