J'ai eu le même problème et j'ai trouvé une solution. J'ai trouvé cette question après l'avoir résolue et je vois que ma solution a beaucoup en commun avec celle de Mark. Cependant, cette approche est un peu différente.
Le problème principal est que les comportements et les déclencheurs s'associent à un objet spécifique et que vous ne pouvez donc pas utiliser la même instance d'un comportement pour plusieurs objets associés différents. Lorsque vous définissez votre comportement en ligne, XAML applique cette relation un-à-un. Cependant, lorsque vous essayez de définir un comportement dans un style, le style peut être réutilisé pour tous les objets auxquels il s'applique et cela lèvera des exceptions dans les classes de comportement de base. En fait, les auteurs ont déployé des efforts considérables pour nous empêcher même d'essayer de le faire, sachant que cela ne fonctionnerait pas.
Le premier problème est que nous ne pouvons même pas construire une valeur de paramètre de comportement car le constructeur est interne. Nous avons donc besoin de notre propre comportement et de déclencher des classes de collecte.
Le problème suivant est que le comportement et les propriétés jointes du déclencheur n'ont pas de setters et ne peuvent donc être ajoutés qu'avec du XAML en ligne. Nous résolvons ce problème avec nos propres propriétés attachées qui manipulent le comportement principal et déclenchent les propriétés.
Le troisième problème est que notre collection de comportements n'est valable que pour une seule cible de style. Nous résolvons ce problème en utilisant une fonctionnalité XAML peu utilisée x:Shared="False"
qui crée une nouvelle copie de la ressource chaque fois qu'elle est référencée.
Le dernier problème est que les comportements et les déclencheurs ne sont pas comme les autres créateurs de style; nous ne voulons pas remplacer les anciens comportements par les nouveaux, car ils pourraient faire des choses très différentes. Donc, si nous acceptons qu'une fois que vous avez ajouté un comportement, vous ne pouvez pas le supprimer (et c'est ainsi que les comportements fonctionnent actuellement), nous pouvons conclure que les comportements et les déclencheurs doivent être additifs et cela peut être géré par nos propriétés attachées.
Voici un exemple utilisant cette approche:
<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>
L'exemple utilise des déclencheurs mais les comportements fonctionnent de la même manière. Dans l'exemple, nous montrons:
- le style peut être appliqué à plusieurs blocs de texte
- plusieurs types de liaison de données fonctionnent tous correctement
- une action de débogage qui génère du texte dans la fenêtre de sortie
Voici un exemple de comportement, notre DebugAction
. Plus exactement, c'est une action, mais par l'abus du langage, nous appelons les comportements, les déclencheurs et les actions des «comportements».
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);
}
}
Enfin, nos collections et propriétés attachées pour que tout cela fonctionne. Par analogie avec Interaction.Behaviors
, la propriété que vous ciblez est appelée SupplementaryInteraction.Behaviors
car en définissant cette propriété, vous ajouterez des comportements à Interaction.Behaviors
et de même pour les déclencheurs.
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);
}
}
et voilà, des comportements et des déclencheurs entièrement fonctionnels appliqués à travers les styles.