【问题标题】:Hide DataTrigger if RelativeSource doesn't exist如果 RelativeSource 不存在,则隐藏 DataTrigger
【发布时间】:2011-09-11 01:39:18
【问题描述】:

我想将一个 DataTrigger 添加到我的基本 TextBox 样式中,以便它将前景色设置为不同的值(如果它位于选定的 DataGridCell 内)。这是我的触发器的样子:

<Style.Triggers>
    <DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGridCell}}, Path=IsSelected}"
                 Value="True">
        <Setter Property="Foreground"
                Value="White" />
    </DataTrigger>
</Style.Triggers>

这很好用,除了当我的 TextBox 不在 DataGrid 中时,绑定失败并将异常写入输出窗口。我该如何防止这种情况。

我基本上想说如果 Parent 是 DataGridCell 然后应用这个触发器,否则忽略它。

【问题讨论】:

    标签: wpf binding


    【解决方案1】:

    一般只在适用的地方应用样式。如果您想要隐式应用程序使用嵌套样式:

    <Style TargetType="{x:Type DataGrid}">
        <Style.Resources>
            <Style TargetType="{x:Type TextBox}">
                <Style.Triggers>
                    <DataTrigger
                            Binding="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGridCell}}, Path=IsSelected}"
                            Value="True">
                        <Setter Property="Foreground" Value="White" />
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </Style.Resources>
    </Style>
    

    如果您想要应用于所有文本框的其他部分,请以锯齿状样式取出这些部分,并以适用于 DataGrid 内的文本框的样式使用 BasedOn


    编辑:如果不满足条件,MultiDataTrigger 似乎会立即返回,因此您可以避免绑定错误:

    <Style TargetType="{x:Type TextBox}">
        <Style.Resources>
            <vc:HasAncestorOfTypeConverter x:Key="HasAncestorOfTypeConverter" AncestorType="{x:Type DataGridCell}" />
        </Style.Resources>
        <Style.Triggers>
            <MultiDataTrigger>
                <MultiDataTrigger.Conditions>
                    <Condition
                            Binding="{Binding RelativeSource={RelativeSource Self}, Converter={StaticResource HasAncestorOfTypeConverter}}"
                            Value="True" />
                    <Condition
                            Binding="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGridCell}}, Path=IsSelected}"
                            Value="True" />
                </MultiDataTrigger.Conditions>
                <Setter Property="Foreground" Value="Red" />
            </MultiDataTrigger>
        </Style.Triggers>
    </Style>
    
    public class HasAncestorOfTypeConverter : IValueConverter
    {
        public Type AncestorType { get; set; }
    
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            if (value == null) return false;
            DependencyObject current = value as DependencyObject;
            while (true)
            {
                current = VisualTreeHelper.GetParent(current);
                if (current == null)
                {
                    return false;
                }
                if (current.GetType() == AncestorType)
                {
                    return true;
                }
            }
        }
    
        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotSupportedException();
        }
    }
    

    这当然会导致相当大的开销,因此它可能不是一个很好的解决方案,如果RelativeSource绑定失败,它也必须先上树。

    【讨论】:

    • 谢谢 H.B.但这是在我们自定义皮肤中的文件中设置的。由于会有多个皮肤覆盖它,我希望将所有 TextBox 样式触发器放在同一个文件中,而不是将它们分开 - 你知道有什么方法可以解决我特别问的问题吗(也许通过使用 MultiTrigger 和写我自己的 DataTrigger 类什么的)?
    • @MattWest:MultiDataTrigger 可能会起作用,但前提是如果第一个条件为负,则不评估以下条件,不知道是否是这种情况。 (是输出消息的问题还是只是绑定失败?消息可能被敲掉)
    • 输出消息是主要问题 - 我该如何解决?
    • @MattWest:你应该可以通过an attached property控制绑定输出的细节。不过到目前为止我还没有使用过。
    • @MattWest:用 MultiDataTrigger 方法更新了我的答案。
    猜你喜欢
    • 2020-08-03
    • 2020-11-06
    • 1970-01-01
    • 2016-02-09
    • 2016-04-16
    • 1970-01-01
    • 2018-02-15
    • 1970-01-01
    • 2023-03-23
    相关资源
    最近更新 更多