我曾经遇到同样的问题并找到了解决办法。在我解决问题之后,我发现了这个问题,并且我看到我的解决方案与Mark的解决方案有很多相似之处。不过,这种方法有些不同。
主要问题是行为和触发器与特定对象相关联,因此您不能将相同实例的行为用于多个不同的相关对象。当您在inline XAML中定义行为时,XAML强制执行此一对一关系。但是,当您尝试在样式中设置行为时,该样式可以重复使用于其应用的所有对象,这将在基本行为类中引发异常。事实上,作者花了很多力气防止我们甚至尝试这样做,因为他们知道这样做是行不通的。
第一个问题是我们甚至无法构造行为设置器值,因为构造函数是内部的。因此,我们需要自己的行为和触发器集合类。
下一个问题是行为和触发器附加属性没有setter,因此它们只能在内联XAML中添加。我们通过自己的附加属性来解决此问题,以操纵主要的行为和触发器属性。
第三个问题是我们的行为集合仅适用于单个样式目标。我们通过利用一个不常用的XAML特性来解决这个问题,它在每次引用时都创建一个新的资源副本。
最后一个问题是行为和触发器不像其他样式设置器;我们不想用新行为替换旧行为,因为它们可能做完全不同的事情。因此,如果我们接受一旦添加了行为就不能将其删除(这就是行为当前的工作方式),我们可以得出行为和触发器应该是可添加的结论,而这可以通过我们的附加属性来处理。
以下是使用此方法的示例:
<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);
}
}
这就是全部,通过样式应用的完全功能行为和触发器。
x:Shared="False"
,但我仍然遇到了异常An instance of a Behavior cannot be attached to more than one object at a time
。原来在“嵌套”的字典中不支持共享属性,而且ResourceDictionary.MergedDictionaries
也算在内。然而,当资源定义在一个单独的 XAML 文件中,该文件只包含一个ResourceDictionary
时,我还是让它正常工作了。希望这能帮助到某些人... - Tim Sylvester