更改基于/继承样式中的触发器顺序

Posted

技术标签:

【中文标题】更改基于/继承样式中的触发器顺序【英文标题】:Change Trigger order in BasedOn / inherited Style 【发布时间】:2018-07-31 07:45:37 【问题描述】:

我有一个基地Style - DataGridRowSelectionStyle。在某些DataGrids 上,我需要扩展此Style 以在background 上添加墨水。

DataGridRowSelectionStyle

<Style TargetType="DataGridRow" x:Key="DataGridRowSelectionStyle">
    <Style.Triggers>
        <Trigger Property="IsMouseOver" Value="True">
            <Setter Property="Background" Value="extensions:Theme Key=DataGrid_Row_IsMouseOver"/>
        </Trigger>
        <Trigger Property="IsSelected" Value="True">
            <Setter Property="Background" Value="extensions:Theme Key=DataGrid_Row_IsSelected"/>
            <Setter Property="FontWeight" Value="Bold"/>
        </Trigger>
    </Style.Triggers>
</Style>

行样式

<DataGrid.RowStyle>
    <Style TargetType="DataGridRow" BasedOn="StaticResource DataGridRowSelectionStyle">                                        
        <Style.Triggers>
            <DataTrigger Binding="Binding CurrentStatus" Value="x:Static production1:ProcessDataEval.OK">
                <Setter Property="Background" Value="extensions:Theme Key=DGLB_Green"/>                                                
            </DataTrigger>
            <DataTrigger Binding="Binding CurrentStatus" Value="x:Static production1:ProcessDataEval.NG">
                <Setter Property="Background" Value="extensions:Theme Key=DGLB_Red"/>                                                
            </DataTrigger>
        </Style.Triggers>
    </Style>
</DataGrid.RowStyle>

由于Trigger 顺序,两个基本Triggers 被覆盖,IsMouseOverIsSelected 不再触发。


解决方案 1:扩展 RowStyle。非常糟糕的解决方案,因为我不再需要我的基地Style..

<DataGrid.RowStyle>
    <Style TargetType="DataGridRow" BasedOn="StaticResource DataGridRowSelectionStyle">                                        
        <Style.Triggers>
            <DataTrigger Binding="Binding CurrentStatus" Value="x:Static production1:ProcessDataEval.OK">
                <Setter Property="Background" Value="extensions:Theme Key=DGLB_Green"/>                                                
            </DataTrigger>
            <DataTrigger Binding="Binding CurrentStatus" Value="x:Static production1:ProcessDataEval.NG">
                <Setter Property="Background" Value="extensions:Theme Key=DGLB_Red"/>                                                
            </DataTrigger>
            <Trigger Property="IsMouseOver" Value="True">
                <Setter Property="Background" Value="extensions:Theme Key=DataGrid_Row_IsMouseOver"/>
            </Trigger>
            <Trigger Property="IsSelected" Value="True">
                <Setter Property="Background" Value="extensions:Theme Key=DataGrid_Row_IsSelected"/>
                <Setter Property="FontWeight" Value="Bold"/>
            </Trigger>
        </Style.Triggers>
    </Style>
</DataGrid.RowStyle>

解决方案 2:创建一个 behavior 并将其添加到基础 Style,这将对最终的 Style 重新排序。 问题:Behavior&lt;TriggerCollection&gt;Behavior&lt;Style&gt; 不起作用!

类型“System.Windows.Style”必须可转换为“System.Windows.DependencyObject”才能在通用类“System.Windows.Interactivity.Behavior”中用作参数“T”


有人解决了如何在样式中使用behavior 或如何更改继承的Style 中的触发顺序?

【问题讨论】:

【参考方案1】:

我使用AttachedProperty 得到了解决方案。

我缓存每个触发器,并为最终的TriggerCollection 提供索引。在DataGridRow 被渲染后,else if (d is FrameworkElement frameworkElement)true 并且Style 被克隆为Triggers 的新顺序。

public static class TriggerAttachedBehavior

    private static readonly Dictionary<Trigger, int> _Triggers = new Dictionary<Trigger, int>();

    /// <summary>
    /// Gets property value.
    /// </summary>
    /// <param name="attachedObj"></param>
    /// <returns></returns>
    public static int GetOderIndex(Trigger attachedObj)
    
        return (int)attachedObj.GetValue(OderIndexProperty);
    

    /// <summary>
    /// Sets property value.
    /// </summary>
    /// <param name="attachedObj"></param>
    /// <param name="value"></param>
    public static void SetOderIndex(Trigger attachedObj, int value)
    
        attachedObj.SetValue(OderIndexProperty, value);
    

    /// <summary>
    /// The <see cref="OderIndexProperty"/> DependencyProperty.
    /// </summary>
    public static readonly DependencyProperty OderIndexProperty = DependencyProperty.RegisterAttached("OderIndex", typeof(int), typeof(TriggerAttachedBehavior), new UIPropertyMetadata(-1, OderIndexChangedCallback));

    /// <summary>
    /// Occurs when OderIndexProperty has changed.
    /// </summary>
    /// <param name="d">Dependency object.</param>
    /// <param name="args">Event arguments.</param>
    private static void OderIndexChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs args)
    
        if (d is Trigger attachedObj)
        
            _Triggers.Add(attachedObj, (int)args.NewValue);
        
        else if (d is FrameworkElement frameworkElement)
        
            // clone style with trigger lock
            var newStyle = new Style(frameworkElement.Style.TargetType, frameworkElement.Style);
            newStyle.Triggers.Clear();

            // add all triggers except the base
            foreach (TriggerBase triggerBase in frameworkElement.Style.Triggers)
            
                if(_Triggers.Any(t => _Equals(t.Key, triggerBase)))
                    continue;    
                newStyle.Triggers.Add(triggerBase);
            

            // add the base class triggers
            foreach (int i in _Triggers.Values.OrderBy(t => t))
            
                newStyle.Triggers.Add(_Triggers.First(t => t.Value == i).Key);
            

            // apply new style
            frameworkElement.Style = newStyle;
        
    

    private static bool _Equals(TriggerBase x, TriggerBase y)
    
        if (x.GetType() != y.GetType())
            return false;

        switch (x)
        
            case DataTrigger dataTrigger:
                return false;
            case EventTrigger eventTrigger:
                return false;
            case MultiDataTrigger multiDataTrigger:
                return false;
            case MultiTrigger multiTrigger:
                return false;
            case Trigger trigger:
                return trigger.Property.Name.Equals((y as Trigger).Property.Name);
        

        return false;
    


<Style TargetType="DataGridRow" x:Key="DataGridRowSelectionStyle" BasedOn="StaticResource DataGridRowDefaultStyle">
    <Setter Property="behaviors:TriggerAttachedBehavior.OderIndex" Value="0"></Setter>
    <Style.Triggers>
        <Trigger Property="IsMouseOver" Value="True" behaviors:TriggerAttachedBehavior.OderIndex="998">
            <Setter Property="Background" Value="extensions:Theme Key=DataGrid_Row_IsMouseOver"/>
        </Trigger>
        <Trigger Property="IsSelected" Value="True" behaviors:TriggerAttachedBehavior.OderIndex="999">
            <Setter Property="Background" Value="extensions:Theme Key=DataGrid_Row_IsSelected"/>
            <Setter Property="FontWeight" Value="Bold"/>
        </Trigger>
    </Style.Triggers>
</Style>

【讨论】:

【参考方案2】:

MultiDataTrigger 可用于根据行的状态值绘制行仅当 IsMouseOver 为假且 IsSelected 为假时。附加条件(当它们满足时)阻止 MultiDataTriggers 覆盖基本触发器:

<DataGrid.RowStyle>
    <Style TargetType="DataGridRow" BasedOn="StaticResource DataGridRowSelectionStyle">
        <Style.Triggers>
            <MultiDataTrigger>
                <MultiDataTrigger.Conditions>
                    <Condition Binding="Binding CurrentStatus" Value="x:Static production1:ProcessDataEval.OK"/>
                    <Condition Binding="Binding IsMouseOver, RelativeSource=RelativeSource Self" Value="False"/>
                    <Condition Binding="Binding IsSelected, RelativeSource=RelativeSource Self" Value="False"/>
                </MultiDataTrigger.Conditions>

                <Setter Property="Background" Value="extensions:Theme Key=DGLB_Green"/>
            </MultiDataTrigger>

            <MultiDataTrigger >
                <MultiDataTrigger.Conditions>
                    <Condition Binding="Binding CurrentStatus" Value="x:Static production1:ProcessDataEval.NG"/>
                    <Condition Binding="Binding IsMouseOver, RelativeSource=RelativeSource Self" Value="False"/>
                    <Condition Binding="Binding IsSelected, RelativeSource=RelativeSource Self" Value="False"/>
                </MultiDataTrigger.Conditions>

                <Setter Property="Background" Value="extensions:Theme Key=DGLB_Red"/>
            </MultiDataTrigger>
        </Style.Triggers>
    </Style>
</DataGrid.RowStyle>

【讨论】:

这也是您个人的解决方案,您将如何解决这个问题?如果您在 6 个月后更改基本样式,则可能必须修复所有继承的样式,包括客户项目。 (样式是框架库的一部分,每个客户都有一个个人应用程序)

以上是关于更改基于/继承样式中的触发器顺序的主要内容,如果未能解决你的问题,请参考以下文章

在 DOM 元素的样式对象更改后,您可以使用 javascript 钩子触发器吗?

导航控制器中的更改顺序

如何在底层对象状态更改时触发 ListBoxItem 的样式更改?

从子组件触发应用级别样式更改

WPF 使用混合更改样式

在 chrome 开发工具中更改样式时,有啥方法可以触发事件?