WPF 绑定到从后面的代码添加的 UserControl 的祖先
WPF bind to ancestor from UserControl that is added from code behind
我有一个用户控件,用于从代码隐藏动态填充列表框。我希望在选择父 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 应该公开一个适当的 属性,例如IsSelected
您将绑定到 DataTemplate 中的 ListBoxItem 的 IsSelected 属性。
<ListBox>
<ListBox.ItemTemplate>
<DataTemplate>
<local:MyIconControl
IsSelected="{Binding IsSelected,
RelativeSource={RelativeSource AncestorType=ListBoxItem}}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
在 UserControl 的 XAML 中,属性 将被另一个 RelativeSource 绑定绑定:
<Grid>
<fonts:FontAwesomeIcon
ReverseColors="{Binding IsSelected,
RelativeSource={RelativeSource AncestorType=UserControl}}" />
</Grid>
这个答案不是问题的解决方案(特别是考虑到@Clemens 的回答)。
将此视为扩展评论。
您提出的问题与DataTrigger中的Style和Binding无关。
这是一个简单的示例,展示了在不使用自定义 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;
}
}
这个例子很好用,没有任何绑定错误。
由此我们可以得出结论,问题的原因不在于您显示的代码。
它可以在“fonts: FontAwesomeIcon”的实现代码中找到,也可以在 Window 中的 ListBox 的初始化代码中找到。
但是你没有显示这些代码。
至于UserControl本身的实现,它只有一个UI元素和它的样式,完全不清楚为什么要声明这样一个UserControl。
我也完全同意克莱门特的观点。
UserControl“不知道”它将在哪个容器中的何处使用。
而且Style中的“FindAncestor”绑定看起来非常歪歪扭扭。
如果你需要一些只能通过UserControl的应用位置来确定的外部数据,那么最好在UserControl中为它们声明一个属性,然后设置一个值或绑定到这个属性.
我有一个用户控件,用于从代码隐藏动态填充列表框。我希望在选择父 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 应该公开一个适当的 属性,例如IsSelected
您将绑定到 DataTemplate 中的 ListBoxItem 的 IsSelected 属性。
<ListBox>
<ListBox.ItemTemplate>
<DataTemplate>
<local:MyIconControl
IsSelected="{Binding IsSelected,
RelativeSource={RelativeSource AncestorType=ListBoxItem}}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
在 UserControl 的 XAML 中,属性 将被另一个 RelativeSource 绑定绑定:
<Grid>
<fonts:FontAwesomeIcon
ReverseColors="{Binding IsSelected,
RelativeSource={RelativeSource AncestorType=UserControl}}" />
</Grid>
这个答案不是问题的解决方案(特别是考虑到@Clemens 的回答)。 将此视为扩展评论。
您提出的问题与DataTrigger中的Style和Binding无关。
这是一个简单的示例,展示了在不使用自定义 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;
}
}
这个例子很好用,没有任何绑定错误。
由此我们可以得出结论,问题的原因不在于您显示的代码。 它可以在“fonts: FontAwesomeIcon”的实现代码中找到,也可以在 Window 中的 ListBox 的初始化代码中找到。 但是你没有显示这些代码。
至于UserControl本身的实现,它只有一个UI元素和它的样式,完全不清楚为什么要声明这样一个UserControl。
我也完全同意克莱门特的观点。
UserControl“不知道”它将在哪个容器中的何处使用。
而且Style中的“FindAncestor”绑定看起来非常歪歪扭扭。
如果你需要一些只能通过UserControl的应用位置来确定的外部数据,那么最好在UserControl中为它们声明一个属性,然后设置一个值或绑定到这个属性.