Tôi gặp vấn đề tương tự và tôi đã đưa ra giải pháp. Tôi tìm thấy câu hỏi này sau khi tôi giải quyết nó và tôi thấy rằng giải pháp của tôi mang nhiều điểm chung với Mark. Tuy nhiên, cách tiếp cận này hơi khác một chút.
Vấn đề chính là hành vi và trình kích hoạt liên kết với một đối tượng cụ thể và do đó bạn không thể sử dụng cùng một thể hiện hành vi cho nhiều đối tượng liên quan khác nhau. Khi bạn xác định hành vi XAML nội tuyến của bạn thực thi mối quan hệ một-một này. Tuy nhiên, khi bạn cố gắng thiết lập một hành vi trong một kiểu, kiểu có thể được tái sử dụng cho tất cả các đối tượng mà nó áp dụng và điều này sẽ ném ngoại lệ trong các lớp hành vi cơ bản. Trong thực tế, các tác giả đã nỗ lực đáng kể để ngăn chúng tôi thậm chí cố gắng làm điều này, biết rằng nó sẽ không hoạt động.
Vấn đề đầu tiên là chúng tôi thậm chí không thể xây dựng giá trị setter hành vi vì hàm tạo là nội bộ. Vì vậy, chúng ta cần hành vi của riêng chúng ta và các lớp thu kích hoạt.
Vấn đề tiếp theo là hành vi và kích hoạt thuộc tính đính kèm không có setters và do đó họ chỉ có thể được thêm vào với XAML trong dòng. Vấn đề này chúng ta giải quyết với các thuộc tính của riêng chúng ta, điều khiển hành vi chính và các thuộc tính kích hoạt.
Vấn đề thứ ba là bộ sưu tập hành vi của chúng tôi chỉ tốt cho một mục tiêu kiểu duy nhất. Điều này chúng ta giải quyết bằng cách sử dụng một tính năng XAML ít được sử dụng x:Shared="False"
tạo ra một bản sao tài nguyên mới mỗi khi nó được tham chiếu.
Vấn đề cuối cùng là hành vi và trình kích hoạt không giống như các trình định kiểu khác; chúng ta không muốn thay thế những hành vi cũ với những hành vi mới bởi vì chúng có thể làm những điều cực kỳ khác nhau. Vì vậy, nếu chúng tôi chấp nhận rằng khi bạn thêm hành vi, bạn không thể lấy đi (và đó là cách hành vi hiện đang hoạt động), chúng tôi có thể kết luận rằng hành vi và trình kích hoạt phải là phụ gia và điều này có thể được xử lý bởi các thuộc tính đính kèm của chúng tôi.
Đây là một mẫu sử dụng phương pháp này:
<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>
Ví dụ sử dụng trigger nhưng hành vi làm việc theo cách tương tự.Trong ví dụ này, chúng ta thấy:
- phong cách có thể được áp dụng cho nhiều khối văn bản
- một số loại dữ liệu ràng buộc tất cả các công việc một cách chính xác
- một hành động debug mà tạo ra văn bản trong cửa sổ đầu ra
Dưới đây là hành vi mẫu, DebugAction
của chúng tôi. Đúng hơn nó là một hành động nhưng thông qua việc lạm dụng ngôn ngữ chúng ta gọi là hành vi, gây nên và hành động "hành vi".
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);
}
}
Cuối cùng, bộ sưu tập của chúng tôi và thuộc tính đính kèm để làm cho tất cả hoạt động. Bằng cách tương tự với Interaction.Behaviors
, thuộc tính bạn nhắm mục tiêu được gọi là SupplementaryInteraction.Behaviors
vì bằng cách đặt thuộc tính này, bạn sẽ thêm hành vi vào Interaction.Behaviors
và tương tự như vậy đối với trình kích hoạt.
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);
}
}
và ở đó bạn có nó, hành vi đầy đủ chức năng và trình kích hoạt được áp dụng thông qua các kiểu.
Great stuff, điều này làm việc tuyệt vời. Tôi nhận thấy rằng nếu bạn đặt phong cách, ví dụ, trong các tài nguyên UserControl, sau đó e.NewValue có thể là null lúc đầu (có thể phụ thuộc vào điều khiển được sử dụng - Tôi đang sử dụng này trên XamDataTreeNodeControl trong một Infamistics XamDataTree). Vì vậy, tôi thêm một chút kiểm tra sanity trong OnPropertyTriggersChanged: if (e.NewValue! = Null) – MetalMikester
Có ai đã có một vấn đề với phương pháp này khi áp dụng Setter trong một ** ngầm ** Style? Tôi đã nhận nó để làm việc tốt với một phong cách không tiềm ẩn (một với một Key), nhưng tôi nhận được một ngoại lệ tham khảo cyclic nếu nó trong một phong cách tiềm ẩn. –
Giải pháp tốt, nhưng tiếc là nó không hoạt động trong WinRT, vì x: Shared không tồn tại trên nền tảng này ... –