Aynı sorunu yaşadım ve bir çözüm buldum. Bu soruyu çözdükten sonra buldum ve çözümümün Mark'la çok ortak yönleri olduğunu görüyorum. Ancak bu yaklaşım biraz farklıdır.
Temel sorun, davranışların ve tetikleyicilerin belirli bir nesneyle ilişkilendirilmesi ve bu nedenle birden çok farklı ilişkili nesne için aynı davranış örneğini kullanamamanızdır. Davranışınızı tanımladığınızda satır içi XAML bu bire bir ilişkiyi zorlar. Bununla birlikte, bir stilde bir davranış ayarlamaya çalıştığınızda, stil uygulandığı tüm nesneler için yeniden kullanılabilir ve bu, temel davranış sınıflarında istisnalar atar. Aslında yazarlar, işe yaramayacağını bilerek, bunu yapmaya çalışmamızı önlemek için büyük çaba sarf ettiler.
İlk sorun, yapıcı dahili olduğu için bir davranış belirleyici değeri bile oluşturamayacağımızdır. Bu yüzden kendi davranışımıza ihtiyacımız var ve toplama sınıflarını tetikliyoruz.
Bir sonraki sorun, davranış ve tetikleyici ekli özelliklerin ayarlayıcılara sahip olmaması ve bu nedenle bunlara yalnızca satır içi XAML ile eklenebilmeleridir. Bu sorunu, birincil davranışı işleyen ve özellikleri tetikleyen kendi ekli özelliklerimizle çözüyoruz.
Üçüncü sorun, davranış koleksiyonumuzun yalnızca tek bir stil hedef için iyi olmasıdır. x:Shared="False"
Bunu, her başvurulduğunda kaynağın yeni bir kopyasını oluşturan az kullanılan bir XAML özelliğini kullanarak çözüyoruz .
Son sorun, davranışların ve tetikleyicilerin diğer stil belirleyiciler gibi olmamasıdır; eski davranışları yeni davranışlarla değiştirmek istemiyoruz çünkü çılgınca farklı şeyler yapabilirler. Dolayısıyla, bir davranış eklediğinizde onu ortadan kaldıramayacağınızı kabul edersek (ve davranışların şu anda çalışma şekli budur), davranışların ve tetikleyicilerin ilave olması gerektiği sonucuna varabiliriz ve bu, ekli özelliklerimiz tarafından ele alınabilir.
İşte bu yaklaşımı kullanan bir örnek:
<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>
Örnek, tetikleyicileri kullanır ancak davranışlar aynı şekilde çalışır. Örnekte şunları gösteriyoruz:
- stil birden çok metin bloğuna uygulanabilir
- çeşitli veri bağlama türlerinin tümü doğru çalışır
- çıktı penceresinde metin oluşturan bir hata ayıklama eylemi
İşte örnek bir davranış, bizim DebugAction
. Daha doğrusu bir eylemdir, ancak dilin kötüye kullanılması yoluyla davranışlar, tetikleyiciler ve eylemlere "davranışlar" diyoruz.
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);
}
}
Son olarak, tüm bunların çalışmasını sağlamak için koleksiyonlarımız ve ekli mülklerimiz. İle benzer şekilde Interaction.Behaviors
, hedeflediğiniz özellik çağrılır SupplementaryInteraction.Behaviors
çünkü bu özelliği ayarlayarak, Interaction.Behaviors
tetikleyicilere davranışlar ekleyeceksiniz .
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);
}
}
ve orada, tamamen işlevsel davranışlar ve stiller aracılığıyla uygulanan tetikleyiciler var.