Ho avuto lo stesso problema e ho trovato una soluzione. Ho trovato questa domanda dopo averla risolta e vedo che la mia soluzione ha molto in comune con quella di Mark. Tuttavia, questo approccio è leggermente diverso.
Il problema principale è che comportamenti e trigger si associano a un oggetto specifico e quindi non è possibile utilizzare la stessa istanza di un comportamento per più oggetti associati diversi. Quando definisci il tuo comportamento, XAML in linea applica questa relazione uno a uno. Tuttavia, quando si tenta di impostare un comportamento in uno stile, lo stile può essere riutilizzato per tutti gli oggetti a cui si applica e questo genererà eccezioni nelle classi di comportamento di base. In effetti gli autori hanno fatto uno sforzo considerevole per impedirci anche solo di provare a farlo, sapendo che non avrebbe funzionato.
Il primo problema è che non possiamo nemmeno costruire un valore di impostazione del comportamento perché il costruttore è interno. Quindi abbiamo bisogno del nostro comportamento e di attivare le classi di raccolta.
Il problema successivo è che il comportamento e le proprietà associate al trigger non hanno setter e quindi possono essere aggiunti solo con XAML in linea. Questo problema lo risolviamo con le nostre proprietà associate che manipolano il comportamento primario e le proprietà di attivazione.
Il terzo problema è che la nostra raccolta di comportamenti è valida solo per un singolo target di stile. Questo viene risolto utilizzando una funzionalità XAML poco utilizzata x:Shared="False"
che crea una nuova copia della risorsa ogni volta che viene referenziata.
Il problema finale è che i comportamenti e i trigger non sono come gli altri setter di stile; non vogliamo sostituire i vecchi comportamenti con i nuovi comportamenti perché potrebbero fare cose completamente diverse. Quindi, se accettiamo che una volta aggiunto un comportamento non puoi rimuoverlo (e questo è il modo in cui i comportamenti funzionano attualmente), possiamo concludere che i comportamenti e i trigger dovrebbero essere additivi e questo può essere gestito dalle nostre proprietà associate.
Ecco un esempio che utilizza questo approccio:
<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'esempio utilizza trigger ma i comportamenti funzionano allo stesso modo. Nell'esempio mostriamo:
- lo stile può essere applicato a più blocchi di testo
- diversi tipi di data binding funzionano tutti correttamente
- un'azione di debug che genera testo nella finestra di output
Ecco un esempio di comportamento, il nostro DebugAction
. Più propriamente è un'azione, ma attraverso l'abuso del linguaggio chiamiamo comportamenti, trigger e azioni "comportamenti".
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);
}
}
Infine, le nostre collezioni e le proprietà annesse per far funzionare tutto questo. Per analogia con Interaction.Behaviors
, la proprietà targetizzata viene chiamata SupplementaryInteraction.Behaviors
perché impostando questa proprietà, aggiungerai comportamenti ai Interaction.Behaviors
trigger e allo stesso modo.
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 il gioco è fatto, comportamenti completamente funzionali e trigger applicati attraverso gli stili.