Miałem ten sam problem i znalazłem rozwiązanie. Znalazłem to pytanie po rozwiązaniu go i widzę, że moje rozwiązanie ma wiele wspólnego z rozwiązaniem Marka. Jednak to podejście jest trochę inne.
Głównym problemem jest to, że zachowania i wyzwalacze są skojarzone z określonym obiektem, więc nie można użyć tego samego wystąpienia zachowania dla wielu różnych skojarzonych obiektów. Gdy definiujesz swoje zachowanie, wbudowany kod XAML wymusza tę relację jeden do jednego. Jednak gdy spróbujesz ustawić zachowanie w stylu, styl ten może zostać ponownie użyty dla wszystkich obiektów, do których ma zastosowanie, co spowoduje zgłoszenie wyjątków w podstawowych klasach zachowania. W rzeczywistości autorzy dołożyli wszelkich starań, aby powstrzymać nas przed próbą zrobienia tego, wiedząc, że to nie zadziała.
Pierwszym problemem jest to, że nie możemy nawet skonstruować wartości ustawiającej zachowanie, ponieważ konstruktor jest wewnętrzny. Potrzebujemy więc własnego zachowania i klas kolekcji wyzwalaczy.
Następnym problemem jest to, że właściwości związane z zachowaniem i wyzwalaczem nie mają metod ustawiających, dlatego można je dodawać tylko za pomocą wbudowanego kodu XAML. Ten problem rozwiązujemy za pomocą naszych własnych dołączonych właściwości, które manipulują podstawowym zachowaniem i właściwościami wyzwalacza.
Trzeci problem polega na tym, że nasza kolekcja zachowań jest dobra tylko dla jednego celu stylu. Ten problem rozwiązujemy, wykorzystując rzadko używaną funkcję XAML, x:Shared="False"
która tworzy nową kopię zasobu za każdym razem, gdy występuje do niego odwołanie.
Ostatnim problemem jest to, że zachowania i wyzwalacze nie są podobne do innych osób wyznaczających styl; nie chcemy zastępować starych zachowań nowymi, ponieważ mogą robić bardzo różne rzeczy. Jeśli więc zaakceptujemy, że po dodaniu zachowania nie można go usunąć (i tak właśnie obecnie działają zachowania), możemy wywnioskować, że zachowania i wyzwalacze powinny być addytywne i można to obsłużyć za pomocą naszych dołączonych właściwości.
Oto próbka wykorzystująca to podejście:
<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>
W przykładzie zastosowano wyzwalacze, ale zachowania działają w ten sam sposób. W przykładzie pokazujemy:
- styl można zastosować do wielu bloków tekstu
- kilka typów powiązań danych działa poprawnie
- akcja debugująca, która generuje tekst w oknie wyjściowym
Oto przykład zachowania, nasz DebugAction
. Właściwie jest to działanie, ale poprzez nadużycie języka nazywamy zachowania, wyzwalacze i działania „zachowaniami”.
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);
}
}
Wreszcie nasze kolekcje i dołączone właściwości, aby to wszystko działało. Analogicznie Interaction.Behaviors
do docelowej właściwości jest wywoływana, SupplementaryInteraction.Behaviors
ponieważ ustawiając tę właściwość, dodasz zachowania do Interaction.Behaviors
i podobnie dla wyzwalaczy.
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);
}
}
i oto masz w pełni funkcjonalne zachowania i wyzwalacze zastosowane poprzez style.