以BasedOn /继承样式

时间:2018-07-31 07:45:37

标签: wpf xaml triggers blend

我有一个基本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>

RowStyle

<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>无法正常工作!

  

类型“ System.Windows.Style”必须可转换为“ System.Windows.DependencyObject”,以便在通用类“ System.Windows.Interactivity.Behavior”中将其用作参数“ T”


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

2 个答案:

答案 0 :(得分: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>

答案 1 :(得分:0)

MultiDataTrigger仅在IsemuseOver为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>