【问题标题】:WPF bind to ancestor from UserControl that is added from code behindWPF 从后面的代码添加的 UserControl 绑定到祖先
【发布时间】:2021-11-05 03:49:34
【问题描述】:

我有一个用户控件,用于从后面的代码动态填充 ListBox。我希望在选择父 ListBoxItem 时反转图标的颜色。

但是数据触发器不起作用。我收到以下错误消息:“找不到源:RelativeSource FindAncestor, AncestorType='System.Windows.Controls.ListBoxItem', AncestorLevel='1'”

但是,我遇到了 2 个 DataTrigger(如下所示)开始工作的案例。

  1. 如果我在我的 XAML 中对用户控件进行硬编码。这不是一个选择。
  2. 如果我更改样式的某些内容(例如,将默认值从 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} 应该可以工作。

标签: c# wpf binding datatrigger


【解决方案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>
    

    【讨论】:

      猜你喜欢
      • 2015-04-14
      • 2011-03-25
      • 1970-01-01
      • 2018-11-24
      • 2020-02-19
      • 2012-07-31
      • 1970-01-01
      • 1970-01-01
      • 2011-01-22
      相关资源
      最近更新 更多