WPF 从后面的代码添加的 UserControl 绑定到祖先
Posted
技术标签:
【中文标题】WPF 从后面的代码添加的 UserControl 绑定到祖先【英文标题】:WPF bind to ancestor from UserControl that is added from code behind 【发布时间】:2021-11-05 03:49:34 【问题描述】:我有一个用户控件,用于从后面的代码动态填充 ListBox。我希望在选择父 ListBoxItem 时反转图标的颜色。
但是数据触发器不起作用。我收到以下错误消息:“找不到源:RelativeSource FindAncestor, AncestorType='System.Windows.Controls.ListBoxItem', AncestorLevel='1'”
但是,我遇到了 2 个 DataTrigger(如下所示)开始工作的案例。
-
如果我在我的 XAML 中对用户控件进行硬编码。这不是一个选择。
如果我更改样式的某些内容(例如,将默认值从 true 变回 false)。所以基本上如果我强制重新评估样式。
所以我想我知道发生了什么,但我不知道该怎么做:我在后面的代码中创建了一个 UserControl 的新实例,并且 Style 和 DataTrigger 立即被评估并抛出一个错误(这使得感觉,因为它还没有添加到 VisualTree,因此没有找到祖先)。
这是我的用户控件的内容:
<UserControl.Resources>
<Style x:Key="FontAwesomeIconInvertedColorOnSelection" TargetType="fonts:FontAwesomeIcon">
<Setter Property="ReverseColors" Value="False" />
<Style.Triggers>
<DataTrigger Binding="Binding
RelativeSource=RelativeSource
Mode=FindAncestor,
AncestorType=x:Type ListBoxItem,
Path=IsSelected"
Value="True">
<Setter Property="ReverseColors" Value="True" />
</DataTrigger>
</Style.Triggers>
</Style>
</UserControl.Resources>
<Grid>
<fonts:FontAwesomeIcon
Style="StaticResource FontAwesomeIconInvertedColorOnSelection" />
</Grid>
我能否以某种方式强制在 UserControl.Loaded 上重新评估样式?或者您对如何获得我想要的行为有其他建议?
【问题讨论】:
UserControl 根本不应该声明 DataTrigger 绑定,因为它会创建不必要的依赖项。为什么控件总是只能在 ListBoxItem 中使用?相反,让您的 UserControl 公开其自己的IsSelected
属性,您将 DataTrigger(或常规触发器)绑定到该属性,并在您使用该控件的 ListBox 的 ItemTemplate 中绑定。
从您所展示的内容来看,人们甚至可能会争论为什么有一个 UserControl,而不是一个简单的 DataTemplate 资源。
我省略了一些代码以减少混乱。它比上面显示的要多一点。谢谢您的建议。这很有意义!但是,如果我在 ItemTemplate 中使用我的 UserControl,那么我的 UserControl 中的 DataContext 将自动成为基础数据对象。然后如何绑定到我的 UserControl 中的 IsSelected 属性?
我想RelativeSource=RelativeSource Mode=FindAncestor, AncestorType=local:MyUserControl 应该可以工作。
【参考方案1】:
这个答案不是问题的解决方案(特别是考虑到@Clemens 的答案)。 将此视为扩展评论。
您提出的问题与 DataTrigger 中的样式和绑定无关。
这是一个简单的示例,展示了在不使用自定义 FontAwesomeIcon 的情况下应用您的样式:
<UserControl x:Class="StyleInUserControl.MyUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:StyleInUserControl"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
<Style x:Key="FontAwesomeIconInvertedColorOnSelection" TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="Binding
RelativeSource=RelativeSource
Mode=FindAncestor,
AncestorType=x:Type ListBoxItem,
Path=IsSelected"
Value="True">
<Setter Property="Foreground" Value="LightGreen" />
</DataTrigger>
</Style.Triggers>
</Style>
</UserControl.Resources>
<Grid>
<TextBlock Text="Binding"
Style="DynamicResource FontAwesomeIconInvertedColorOnSelection"/>
</Grid>
</UserControl>
<Window x:Class="StyleInUserControl.MyUCExamleWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="MyUCExamleWindow" Height="450" Width="800">
<Grid>
<ListBox x:Name="listBox"/>
</Grid>
</Window>
public partial class MyUCExamleWindow : Window
public MyUCExamleWindow()
InitializeComponent();
listBox.Items.Add(new MyUserControl() DataContext = "First" );
listBox.Items.Add(new MyUserControl() DataContext = "Second" );
listBox.Items.Add(new MyUserControl() DataContext = "Third" );
listBox.SelectedIndex = 1;
此示例运行良好,没有任何绑定错误。
由此我们可以得出结论,您的问题的原因不在于您显示的代码。 它可以在“字体:FontAwesomeIcon”的实现代码中找到,也可以在 Window 中 ListBox 的初始化代码中找到。 但是您没有显示这些代码。
至于UserControl本身的实现,它只有一个UI元素和一个样式,完全不清楚为什么要声明这样一个UserControl。
我也完全同意克莱门特的观点。 UserControl“不知道”它将在哪个容器中使用。 而且Style中的“FindAncestor”绑定看起来很歪歪扭扭。 如果你需要一些外部数据,只能通过UserControl的应用位置来确定,那么最好在UserControl中为它们声明一个属性,然后给这个属性设置一个值或者绑定。
【讨论】:
【参考方案2】:您的 UserControl 应公开适当的属性,例如IsSelected
您将绑定到 DataTemplate 中 ListBoxItem 的 IsSelected 属性。
<ListBox>
<ListBox.ItemTemplate>
<DataTemplate>
<local:MyIconControl
IsSelected="Binding IsSelected,
RelativeSource=RelativeSource AncestorType=ListBoxItem"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
在 UserControl 的 XAML 中,该属性将被另一个 RelativeSource Binding 绑定:
<Grid>
<fonts:FontAwesomeIcon
ReverseColors="Binding IsSelected,
RelativeSource=RelativeSource AncestorType=UserControl" />
</Grid>
【讨论】:
以上是关于WPF 从后面的代码添加的 UserControl 绑定到祖先的主要内容,如果未能解决你的问题,请参考以下文章
ViewModel应该继承WPF中的DependencyObject吗?