在 BasedOn / inherited Style 中更改触发顺序

Change Trigger order in BasedOn / inherited Style

我有一个基地 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<TriggerCollection>Behavior<Style> 不工作!

The type 'System.Windows.Style' must be convertible to 'System.Windows.DependencyObject' in order to use it as paramter 'T' in the generic class 'System.Windows.Interactivity.Behavior'


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

我通过 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>

MultiDataTrigger 仅当 IsMouseOver 为 false 且 IsSelected 为 false 时, 才可用于根据状态值绘制行。附加条件(满足时)阻止 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>