Tive o mesmo problema e encontrei uma solução. Descobri essa questão depois de resolvê-la e vejo que minha solução tem muito em comum com a de Mark. No entanto, essa abordagem é um pouco diferente.
O principal problema é que comportamentos e gatilhos se associam a um objeto específico e, portanto, você não pode usar a mesma instância de um comportamento para vários objetos associados diferentes. Quando você define seu comportamento em linha, o XAML impõe esse relacionamento um a um. No entanto, quando você tenta definir um comportamento em um estilo, o estilo pode ser reutilizado para todos os objetos aos quais se aplica e isso lançará exceções nas classes de comportamento base. Na verdade, os autores fizeram um esforço considerável para nos impedir de tentar fazer isso, sabendo que não funcionaria.
O primeiro problema é que não podemos nem construir um valor configurador de comportamento porque o construtor é interno. Portanto, precisamos de nosso próprio comportamento e classes de coleção de gatilho.
O próximo problema é que o comportamento e as propriedades anexadas ao gatilho não têm configuradores e, portanto, só podem ser adicionados com XAML in-line. Resolvemos esse problema com nossas próprias propriedades anexadas que manipulam o comportamento primário e as propriedades do gatilho.
O terceiro problema é que nossa coleção de comportamento só é boa para um único destino de estilo. Isso nós resolvemos utilizando um recurso XAML pouco usado x:Shared="False"
que cria uma nova cópia do recurso cada vez que ele é referenciado.
O problema final é que os comportamentos e gatilhos não são como outros definidores de estilo; não queremos substituir os antigos comportamentos pelos novos, porque eles poderiam fazer coisas totalmente diferentes. Portanto, se aceitarmos que, depois de adicionar um comportamento, você não pode removê-lo (e é assim que os comportamentos funcionam atualmente), podemos concluir que os comportamentos e os gatilhos devem ser aditivos e isso pode ser tratado por nossas propriedades anexadas.
Aqui está um exemplo usando esta abordagem:
<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>
O exemplo usa gatilhos, mas os comportamentos funcionam da mesma maneira. No exemplo, mostramos:
- o estilo pode ser aplicado a vários blocos de texto
- vários tipos de vinculação de dados funcionam corretamente
- uma ação de depuração que gera texto na janela de saída
Aqui está um exemplo de comportamento, nosso DebugAction
. Mais propriamente, é uma ação, mas através do abuso da linguagem chamamos comportamentos, gatilhos e ações de "comportamentos".
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);
}
}
Finalmente, nossas coleções e propriedades anexadas para fazer tudo isso funcionar. Por analogia com Interaction.Behaviors
, a propriedade de destino é chamada SupplementaryInteraction.Behaviors
porque, ao definir essa propriedade, você adicionará comportamentos a Interaction.Behaviors
e da mesma forma para gatilhos.
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);
}
}
e aí está, comportamentos totalmente funcionais e gatilhos aplicados por meio de estilos.