У меня была такая же проблема, и я нашел решение. Я нашел этот вопрос после того, как решил его, и вижу, что мое решение имеет много общего с решением Марка. Однако этот подход немного отличается.
Основная проблема заключается в том, что поведения и триггеры связаны с определенным объектом, и поэтому вы не можете использовать один и тот же экземпляр поведения для нескольких разных связанных объектов. Когда вы определяете свое поведение, встроенный XAML обеспечивает это взаимно-однозначное отношение. Однако, когда вы пытаетесь установить поведение в стиле, этот стиль можно повторно использовать для всех объектов, к которым он применяется, и это вызовет исключения в базовых классах поведения. Фактически, авторы приложили значительные усилия, чтобы помешать нам даже попытаться это сделать, зная, что это не сработает.
Первая проблема заключается в том, что мы не можем даже создать значение установщика поведения, потому что конструктор является внутренним. Итак, нам нужно собственное поведение и триггерные классы коллекции.
Следующая проблема заключается в том, что свойства поведения и триггера не имеют установщиков и поэтому могут быть добавлены только с помощью встроенного XAML. Эту проблему мы решаем с помощью наших собственных присоединенных свойств, которые управляют основным поведением и свойствами триггера.
Третья проблема заключается в том, что наша коллекция поведения подходит только для одной цели стиля. Мы решаем эту проблему, используя малоиспользуемую функцию XAML, x:Shared="False"
которая создает новую копию ресурса при каждом обращении к нему.
Последняя проблема заключается в том, что поведение и триггеры не похожи на другие установщики стилей; мы не хотим заменять старое поведение новым, потому что они могут делать совершенно разные вещи. Итак, если мы согласимся с тем, что после того, как вы добавите поведение, вы не сможете его убрать (и именно так оно работает в настоящее время), мы можем сделать вывод, что поведения и триггеры должны быть аддитивными, и это может быть обработано нашими присоединенными свойствами.
Вот пример использования этого подхода:
<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);
}
}
Вот и все, полнофункциональное поведение и триггеры, применяемые с помощью стилей.