2009-10-30 15 views
76

Tôi đã thực hiện hành vi Blend cho Button. Làm thế nào tôi có thể đặt nó cho tất cả các nút của tôi trong ứng dụng.Cách thêm Hành vi Blend trong Style Setter

<Button ...> 
    <i:Interaction.Behaviors> 
    <local:MyBehavior /> 
    </i:Interaction.Behaviors> 
</Button> 

Tuy nhiên, khi tôi cố gắng:

<Style> 
    <Setter Property="i:Interaction.Behaviors"> 
    <Setter.Value> 
     <local:MyBehavior /> 
    </Setter.Value> 
    </Setter> 
</Style> 

tôi nhận được lỗi

Thuộc tính "Hành vi" không có một setter thể truy cập.

Trả lời

0

Mã hành vi mong đợi hình ảnh, vì vậy chúng tôi chỉ có thể thêm hình ảnh trên hình ảnh. Vì vậy, tùy chọn duy nhất tôi có thể thấy là thêm vào một trong các phần tử bên trong ControlTemplate để có được hành vi được thêm vào Style và ảnh hưởng đến tất cả cá thể của một điều khiển cụ thể.

71

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.

+0

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

+0

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. –

+1

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 ... –

4

Tôi không thể tìm thấy bài viết gốc nhưng tôi đã có thể tạo lại hiệu ứng.

#region Attached Properties Boilerplate 

    public static readonly DependencyProperty IsActiveProperty = DependencyProperty.RegisterAttached("IsActive", typeof(bool), typeof(ScrollIntoViewBehavior), new PropertyMetadata(false, OnIsActiveChanged)); 

    public static bool GetIsActive(FrameworkElement control) 
    { 
     return (bool)control.GetValue(IsActiveProperty); 
    } 

    public static void SetIsActive(
     FrameworkElement control, bool value) 
    { 
     control.SetValue(IsActiveProperty, value); 
    } 

    private static void OnIsActiveChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
    { 
     var behaviors = Interaction.GetBehaviors(d); 
     var newValue = (bool)e.NewValue; 

     if (newValue) 
     { 
      //add the behavior if we don't already have one 
      if (!behaviors.OfType<ScrollIntoViewBehavior>().Any()) 
      { 
       behaviors.Add(new ScrollIntoViewBehavior()); 
      } 
     } 
     else 
     { 
      //remove any instance of the behavior. (There should only be one, but just in case.) 
      foreach (var item in behaviors.ToArray()) 
      { 
       if (item is ScrollIntoViewBehavior) 
        behaviors.Remove(item); 
      } 
     } 
    } 


    #endregion 
<Style TargetType="Button"> 
    <Setter Property="Blah:ScrollIntoViewBehavior.IsActive" Value="True" /> 
</Style> 
+0

Phải viết thứ là cho mỗi hành vi là một chút của một PITA mặc dù. –

0

Bài viết Introduction to Attached Behaviors in WPF thực hiện một hành vi gắn chỉ sử dụng Style, và cũng có thể liên quan hoặc hữu ích.

Kỹ thuật trong bài viết "Giới thiệu về hành vi được đính kèm" tránh các thẻ Tương tác hoàn toàn, sử dụng trên Kiểu. Tôi không biết nếu điều này chỉ là bởi vì nó là một kỹ thuật ngày hơn, hoặc, nếu điều đó vẫn còn tạo ra một số lợi ích mà người ta nên thích nó trong một số kịch bản.

+1

Đây không phải là hành vi Blend, đó là "hành vi" thông qua một thuộc tính đính kèm đơn giản. –

16

1.Create đính kèm tài sản

public static class DataGridCellAttachedProperties 
{ 
    //Register new attached property 
    public static readonly DependencyProperty IsSingleClickEditModeProperty = 
     DependencyProperty.RegisterAttached("IsSingleClickEditMode", typeof(bool), typeof(DataGridCellAttachedProperties), new UIPropertyMetadata(false, OnPropertyIsSingleClickEditModeChanged)); 

    private static void OnPropertyIsSingleClickEditModeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
    { 
     var dataGridCell = d as DataGridCell; 
     if (dataGridCell == null) 
      return; 

     var isSingleEditMode = GetIsSingleClickEditMode(d); 
     var behaviors = Interaction.GetBehaviors(d); 
     var singleClickEditBehavior = behaviors.SingleOrDefault(x => x is SingleClickEditDataGridCellBehavior); 

     if (singleClickEditBehavior != null && !isSingleEditMode) 
      behaviors.Remove(singleClickEditBehavior); 
     else if (singleClickEditBehavior == null && isSingleEditMode) 
     { 
      singleClickEditBehavior = new SingleClickEditDataGridCellBehavior(); 
      behaviors.Add(singleClickEditBehavior); 
     } 
    } 

    public static bool GetIsSingleClickEditMode(DependencyObject obj) 
    { 
     return (bool) obj.GetValue(IsSingleClickEditModeProperty); 
    } 

    public static void SetIsSingleClickEditMode(DependencyObject obj, bool value) 
    { 
     obj.SetValue(IsSingleClickEditModeProperty, value); 
    } 
} 

2.Create một hành vi

public class SingleClickEditDataGridCellBehavior:Behavior<DataGridCell> 
     { 
      protected override void OnAttached() 
      { 
       base.OnAttached(); 
       AssociatedObject.PreviewMouseLeftButtonDown += DataGridCellPreviewMouseLeftButtonDown; 
      } 

      protected override void OnDetaching() 
      { 
       base.OnDetaching(); 
       AssociatedObject.PreviewMouseLeftButtonDown += DataGridCellPreviewMouseLeftButtonDown; 
      } 

      void DataGridCellPreviewMouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e) 
      { 
       DataGridCell cell = sender as DataGridCell; 
       if (cell != null && !cell.IsEditing && !cell.IsReadOnly) 
       { 
        if (!cell.IsFocused) 
        { 
         cell.Focus(); 
        } 
        DataGrid dataGrid = LogicalTreeWalker.FindParentOfType<DataGrid>(cell); //FindVisualParent<DataGrid>(cell); 
        if (dataGrid != null) 
        { 
         if (dataGrid.SelectionUnit != DataGridSelectionUnit.FullRow) 
         { 
          if (!cell.IsSelected) 
           cell.IsSelected = true; 
         } 
         else 
         { 
          DataGridRow row = LogicalTreeWalker.FindParentOfType<DataGridRow>(cell); //FindVisualParent<DataGridRow>(cell); 
          if (row != null && !row.IsSelected) 
          { 
           row.IsSelected = true; 
          } 
         } 
        } 
       } 
      }  
     } 

3.Tạo một Style và thiết lập thuộc tính

 <Style TargetType="{x:Type DataGridCell}"> 
      <Setter Property="Behaviors:DataGridCellAttachedProperties.IsSingleClickEditMode" Value="True"/> 
     </Style> 
+0

Khi tôi cố gắng truy cập DependencyProperty từ kiểu nó nói IsSingleClickEditMode không được nhận dạng hoặc không truy cập được? –

+0

Xin lỗi tôi .. xấu ngay sau khi tôi nhận xét tôi nhận ra GetIsSingleClickEditMode nên phù hợp với chuỗi bạn vượt qua vào DependencyProperty.RegisterAttached –

0

gắn Tôi thích cách tiếp cận thể hiện bởi các câu trả lời của Roman Dvoskin và Jonathan Allen trong chủ đề này. Khi tôi lần đầu tiên học kỹ thuật đó, tôi được hưởng lợi từ số this blog post, cung cấp thêm giải thích về kỹ thuật này. Và để xem mọi thứ trong ngữ cảnh, here is the entire source code cho lớp mà tác giả nói về bài đăng trên blog của anh ấy.

7

Tôi có ý tưởng khác, để tránh việc tạo ra một tài sản gắn liền với mỗi hành vi:

  1. giao diện tác giả Hành vi:

    public interface IBehaviorCreator 
    { 
        Behavior Create(); 
    } 
    
  2. bộ sưu tập helper nhỏ:

    public class BehaviorCreatorCollection : Collection<IBehaviorCreator> { } 
    
  3. Lớp trình trợ giúp có hành vi:

    public static class BehaviorInStyleAttacher 
    { 
        #region Attached Properties 
    
        public static readonly DependencyProperty BehaviorsProperty = 
         DependencyProperty.RegisterAttached(
          "Behaviors", 
          typeof(BehaviorCreatorCollection), 
          typeof(BehaviorInStyleAttacher), 
          new UIPropertyMetadata(null, OnBehaviorsChanged)); 
    
        #endregion 
    
        #region Getter and Setter of Attached Properties 
    
        public static BehaviorCreatorCollection GetBehaviors(TreeView treeView) 
        { 
         return (BehaviorCreatorCollection)treeView.GetValue(BehaviorsProperty); 
        } 
    
        public static void SetBehaviors(
         TreeView treeView, BehaviorCreatorCollection value) 
        { 
         treeView.SetValue(BehaviorsProperty, value); 
        } 
    
        #endregion 
    
        #region on property changed methods 
    
        private static void OnBehaviorsChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs e) 
        { 
         if (e.NewValue is BehaviorCreatorCollection == false) 
          return; 
    
         BehaviorCreatorCollection newBehaviorCollection = e.NewValue as BehaviorCreatorCollection; 
    
         BehaviorCollection behaviorCollection = Interaction.GetBehaviors(depObj); 
         behaviorCollection.Clear(); 
         foreach (IBehaviorCreator behavior in newBehaviorCollection) 
         { 
          behaviorCollection.Add(behavior.Create()); 
         } 
        } 
    
        #endregion 
    } 
    
  4. Bây giờ cư xử của bạn, mà thực hiện IBehaviorCreator:

    public class SingleClickEditDataGridCellBehavior:Behavior<DataGridCell>, IBehaviorCreator 
    { 
        //some code ... 
    
        public Behavior Create() 
        { 
         // here of course you can also set properties if required 
         return new SingleClickEditDataGridCellBehavior(); 
        } 
    } 
    
  5. Và bây giờ sử dụng nó trong XAML:

    <Style TargetType="{x:Type DataGridCell}"> 
        <Setter Property="helper:BehaviorInStyleAttacher.Behaviors" > 
        <Setter.Value> 
         <helper:BehaviorCreatorCollection> 
         <behaviors:SingleClickEditDataGridCellBehavior/> 
         </helper:BehaviorCreatorCollection> 
        </Setter.Value> 
        </Setter> 
    </Style> 
    
14

câu trả lời Tổng hợp và bài viết tuyệt vời này Blend Behaviors in Styles, tôi đến giải pháp chung ngắn gọn và thuận lợi này:

Tôi đã thực hiện lớp học chung chung, có thể được thừa hưởng bởi bất kỳ hành vi nào.

public class AttachableForStyleBehavior<TComponent, TBehavior> : Behavior<TComponent> 
     where TComponent : System.Windows.DependencyObject 
     where TBehavior : AttachableForStyleBehavior<TComponent, TBehavior> , new() 
    { 
     public static DependencyProperty IsEnabledForStyleProperty = 
      DependencyProperty.RegisterAttached("IsEnabledForStyle", typeof(bool), 
      typeof(AttachableForStyleBehavior<TComponent, TBehavior>), new FrameworkPropertyMetadata(false, OnIsEnabledForStyleChanged)); 

     public bool IsEnabledForStyle 
     { 
      get { return (bool)GetValue(IsEnabledForStyleProperty); } 
      set { SetValue(IsEnabledForStyleProperty, value); } 
     } 

     private static void OnIsEnabledForStyleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
     { 
      UIElement uie = d as UIElement; 

      if (uie != null) 
      { 
       var behColl = Interaction.GetBehaviors(uie); 
       var existingBehavior = behColl.FirstOrDefault(b => b.GetType() == 
         typeof(TBehavior)) as TBehavior; 

       if ((bool)e.NewValue == false && existingBehavior != null) 
       { 
        behColl.Remove(existingBehavior); 
       } 

       else if ((bool)e.NewValue == true && existingBehavior == null) 
       { 
        behColl.Add(new TBehavior()); 
       }  
      } 
     } 
    } 

Vì vậy, bạn chỉ đơn giản là có thể tái sử dụng nó với nhiều thành phần như thế này:

public class ComboBoxBehaviour : AttachableForStyleBehavior<ComboBox, ComboBoxBehaviour> 
    { ... } 

Và trong XAML đủ để tuyên bố:

<Style TargetType="ComboBox"> 
      <Setter Property="behaviours:ComboBoxBehaviour.IsEnabledForStyle" Value="True"/> 

Vì vậy, về cơ bản lớp AttachableForStyleBehavior làm XAML sự vật, đăng ký thể hiện hành vi cho từng thành phần theo kiểu. Để biết thêm chi tiết, vui lòng xem liên kết.

+0

Làm việc như một say mê! Với Scrollingbehavior của tôi kết hợp tôi đã thoát khỏi Inner RowDetailsTemplate-Datagrids không di chuyển Datagrids cha. –

+0

Rất vui được giúp đỡ, hãy tận hưởng =) –

+0

điều gì về ràng buộc dữ liệu với các thuộc tính phụ thuộc trong Hành vi? – JobaDiniz

1

Khai báo cá nhân hành vi/kích hoạt như Resources:

<Window.Resources> 

    <i:EventTrigger x:Key="ET1" EventName="Click"> 
     <ei:ChangePropertyAction PropertyName="Background"> 
      <ei:ChangePropertyAction.Value> 
       <SolidColorBrush Color="#FFDAD32D"/> 
      </ei:ChangePropertyAction.Value> 
     </ei:ChangePropertyAction> 
    </i:EventTrigger> 

</Window.Resources> 

Chèn chúng trong bộ sưu tập:

<Button x:Name="Btn1" Content="Button"> 

     <i:Interaction.Triggers> 
      <StaticResourceExtension ResourceKey="ET1"/> 
     </i:Interaction.Triggers> 

</Button> 
+0

Làm thế nào để trả lời OP? Trình kích hoạt không được thêm thông qua một kiểu trong câu trả lời của bạn. – Kryptos