更改基于/继承样式中的触发器顺序
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
被覆盖,IsMouseOver
或IsSelected
不再触发。
解决方案 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
中的触发顺序?
【问题讨论】:
【参考方案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 钩子触发器吗?