我有同样的问题,我想出了一个解决方案。在解决问题后我发现了这个问题,我发现我的解决方案与Mark的很多共同之处。但是,这种方法有点不同。
主要问题是行为和触发器与特定对象关联,因此您不能将同一行为实例用于多个不同的关联对象。当您定义行为时,内联XAML会强制执行此一对一关系。但是,当您尝试在样式中设置某个行为时,该样式可以重用于其应用于的所有对象,这会在基本行为类中引发异常。事实上,作者们付出了相当大的努力来阻止我们甚至试图做到这一点,知道它不会工作。
第一个问题是,我们甚至无法构造行为设置器值,因为构造器是内部的。所以我们需要我们自己的行为并触发集合类。
下一个问题是行为和触发器附加属性没有setter,因此它们只能通过行内XAML添加。我们用我们自己的附加属性来解决这个问题,该属性操纵主要行为和触发属性。
第三个问题是我们的行为集合仅适用于单个样式目标。我们通过利用少量使用的XAML功能x:Shared="False"
来解决这个问题,每次引用时都会创建资源的新副本。
最后一个问题是,行为和触发器不像其他样式制定者;我们不想用新行为取代旧行为,因为他们可以做很大不同的事情。因此,如果我们接受一旦添加了一个行为就不能将其拿走(这就是当前行为的工作方式),我们可以得出结论,行为和触发器应该是叠加的,这可以通过我们附加的属性来处理。
下面是使用这种方法的例子:
<Grid>
<Grid.Resources>
<sys:String x:Key="stringResource1">stringResource1</sys:String>
<local:Triggers x:Key="debugTriggers" x:Shared="False">
<i:EventTrigger EventName="MouseLeftButtonDown">
<local:DebugAction Message="DataContext: {0}" MessageParameter="{Binding}"/>
<local:DebugAction Message="ElementName: {0}" MessageParameter="{Binding Text, ElementName=textBlock2}"/>
<local:DebugAction Message="Mentor: {0}" MessageParameter="{Binding Text, RelativeSource={RelativeSource AncestorType={x:Type FrameworkElement}}}"/>
</i:EventTrigger>
</local:Triggers>
<Style x:Key="debugBehavior" TargetType="FrameworkElement">
<Setter Property="local:SupplementaryInteraction.Triggers" Value="{StaticResource debugTriggers}"/>
</Style>
</Grid.Resources>
<StackPanel DataContext="{StaticResource stringResource1}">
<TextBlock Name="textBlock1" Text="textBlock1" Style="{StaticResource debugBehavior}"/>
<TextBlock Name="textBlock2" Text="textBlock2" Style="{StaticResource debugBehavior}"/>
<TextBlock Name="textBlock3" Text="textBlock3" Style="{StaticResource debugBehavior}"/>
</StackPanel>
</Grid>
的示例使用触发器,但行为的工作方式相同。在这个例子中,我们展示:
- 风格可以适用于多个文本块
- 几种类型的数据绑定的所有工作正常
- ,在输出窗口中生成的文本调试行动
下面是一个示例行为,我们的DebugAction
。更恰当地说,这是一种行动,但通过滥用语言我们称之为行为,触发器和行为“行为”。
public class DebugAction : TriggerAction<DependencyObject>
{
public string Message
{
get { return (string)GetValue(MessageProperty); }
set { SetValue(MessageProperty, value); }
}
public static readonly DependencyProperty MessageProperty =
DependencyProperty.Register("Message", typeof(string), typeof(DebugAction), new UIPropertyMetadata(""));
public object MessageParameter
{
get { return (object)GetValue(MessageParameterProperty); }
set { SetValue(MessageParameterProperty, value); }
}
public static readonly DependencyProperty MessageParameterProperty =
DependencyProperty.Register("MessageParameter", typeof(object), typeof(DebugAction), new UIPropertyMetadata(null));
protected override void Invoke(object parameter)
{
Debug.WriteLine(Message, MessageParameter, AssociatedObject, parameter);
}
}
最后,我们的集合和附加属性,使这一切工作。通过与Interaction.Behaviors
类比,您定位的属性被称为SupplementaryInteraction.Behaviors
,因为通过设置此属性,您将为Interaction.Behaviors
添加行为,同样也为触发器添加行为。
public class Behaviors : List<Behavior>
{
}
public class Triggers : List<TriggerBase>
{
}
public static class SupplementaryInteraction
{
public static Behaviors GetBehaviors(DependencyObject obj)
{
return (Behaviors)obj.GetValue(BehaviorsProperty);
}
public static void SetBehaviors(DependencyObject obj, Behaviors value)
{
obj.SetValue(BehaviorsProperty, value);
}
public static readonly DependencyProperty BehaviorsProperty =
DependencyProperty.RegisterAttached("Behaviors", typeof(Behaviors), typeof(SupplementaryInteraction), new UIPropertyMetadata(null, OnPropertyBehaviorsChanged));
private static void OnPropertyBehaviorsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var behaviors = Interaction.GetBehaviors(d);
foreach (var behavior in e.NewValue as Behaviors) behaviors.Add(behavior);
}
public static Triggers GetTriggers(DependencyObject obj)
{
return (Triggers)obj.GetValue(TriggersProperty);
}
public static void SetTriggers(DependencyObject obj, Triggers value)
{
obj.SetValue(TriggersProperty, value);
}
public static readonly DependencyProperty TriggersProperty =
DependencyProperty.RegisterAttached("Triggers", typeof(Triggers), typeof(SupplementaryInteraction), new UIPropertyMetadata(null, OnPropertyTriggersChanged));
private static void OnPropertyTriggersChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var triggers = Interaction.GetTriggers(d);
foreach (var trigger in e.NewValue as Triggers) triggers.Add(trigger);
}
}
并且您拥有它,通过样式应用功能齐全的行为和触发器。
伟大的东西,这个精美的作品。我注意到,如果您将样式放在UserControl资源中,那么e.NewValue最初可能为null(可能取决于所使用的控件 - 我在Infragistics XamDataTree中的XamDataTreeNodeControl上使用它)。所以我在OnPropertyTriggersChanged中添加了一点理智检查:if(e.NewValue!= null) – MetalMikester 2011-12-09 17:10:43
在**隐式** Style中应用Setter时,有没有人有过这种方法的问题?我已经使用非隐式样式(使用Key的一种样式)正常工作,但如果使用隐式样式,我会得到循环引用异常。 – 2013-04-01 15:35:52
不错的解决方案,但不幸的是它不能在WinRT中运行,因为x:Shared在此平台上不存在... – 2013-07-06 01:54:47