2017-01-09 16 views
1

我試圖使任意簡單行爲組成的複合行爲。我發現行爲非常靈活的製作自定義控件的方式。依賴屬性不起作用(使複合行爲)

目前我有5個行爲實施滑塊。但他們可以相互衝突。

這些行爲是爲一個控件設計的。我可以設計它們中的每一個獨立工作而不會相互衝突(值得一提的是我做了這個並且它成功地工作了,但是我把它全部移除了,因爲它只是醜陋而已。)

有很多份額點,我不想爲每個行爲重寫相同的代碼。

所以我想爲一個控件做一個複合行爲。此行爲具有一些附加的屬性,這些屬性爲其所有包含的行爲共享。因此這些行爲不會相互衝突。而且很多代碼冗餘都沒有了。現在包含行爲變得更加簡單。

以下是XAML示例,供您更好地理解。

<i:Interaction.Behaviors> 
    <b:SliderCompositeBehavior SourceValue="{Binding SharedValue}"> 
     <sb:FreeSlideBehavior/> 
     <sb:LockOnDragBehavior/> 
     <sb:CancellableDragBehavior/> 
     <sb:KeepRatioBehavior/> 
     <sb:DragCompletedCommandBehavior Command="{Binding SeekTo}"/> 
    </b:SliderCompositeBehavior> 
</i:Interaction.Behaviors> 

此外,所有這些行爲都旨在獨立工作。即把它像這樣工作得很好。

<i:Interaction.Behaviors> 
    <sb:FreeSlideBehavior/> 
</i:Interaction.Behaviors> 

這裏是CompositeBehavior<T> : Behavior<T>

[ContentProperty(nameof(BehaviorCollection))] 
public abstract class CompositeBehavior<T> : Behavior<T> 
    where T : DependencyObject 
{ 
    public static readonly DependencyProperty BehaviorCollectionProperty = 
     DependencyProperty.Register(
      $"{nameof(CompositeBehavior<T>)}<{typeof(T).Name}>", 
      typeof(ObservableCollection<Behavior<T>>), 
      typeof(CompositeBehavior<T>), 
      new FrameworkPropertyMetadata(
       null, 
       FrameworkPropertyMetadataOptions.NotDataBindable)); 

    public ObservableCollection<Behavior<T>> BehaviorCollection 
    { 
     get 
     { 
      var collection = GetValue(BehaviorCollectionProperty) as ObservableCollection<Behavior<T>>; 

      if (collection == null) 
      { 
       collection = new ObservableCollection<Behavior<T>>(); 
       collection.CollectionChanged += OnCollectionChanged; 
       SetValue(BehaviorCollectionProperty, collection); 
      } 

      return collection; 
     } 
    } 

    private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs args) 
    { 
     // some code to throw exception when same behavior is set more than once. 
    } 

    protected override void OnAttached() 
    { 
     foreach (var behavior in BehaviorCollection) 
     { 
      behavior.Attach(AssociatedObject); 
     } 
    } 

    protected override void OnDetaching() 
    { 
     foreach (var behavior in BehaviorCollection) 
     { 
      behavior.Detach(); 
     } 
    } 
} 

這裏是(被示出爲簡單起見,僅一個從屬關係)的SliderCompositeBehavior : CompositeBehavior<Slider>

public sealed class SliderCompositeBehavior : CompositeBehavior<Slider> 
{ 
    private Slider Host => AssociatedObject; 

    public static readonly DependencyProperty SourceValueProperty = 
     DependencyProperty.Register(
      nameof(SourceValue), 
      typeof(double), 
      typeof(SliderCompositeBehavior), 
      new FrameworkPropertyMetadata(
       0d, 
       FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, 
       OnSourceValueChanged)); 

    // does the binding 
    public double SourceValue 
    { 
     get { return (double)Host.GetValue(SourceValueProperty); } 
     set { Host.SetValue(SourceValueProperty, value); } 
    } 

    // attached property for containing behaviors. 
    public static void SetSourceValue(Slider host, double value) 
    { 
     host.SetValue(SourceValueProperty, value); 
    } 

    public static double GetSourceValue(Slider host) 
    { 
     return (double)host.GetValue(SourceValueProperty); 
    } 

    private static void OnSourceValueChanged(DependencyObject dpo, DependencyPropertyChangedEventArgs args) 
    { 
     var soruce = (SliderCompositeBehavior)dpo; 

     soruce.Host.Value = (double)args.NewValue; 
    } 
} 

現在有兩個問題,我可以看到。

  • 包含行爲的依賴屬性定義完全不起作用。

  • 重寫依賴項屬性的元數據不適用於包含屬性。

裏面DragCompletedCommandBehavior : Behavior<Slider>

public sealed class DragCompletedCommandBehavior : Behavior<Slider> 
{ 
    public ICommand Command 
    { 
     get { return (ICommand)GetValue(CommandProperty); } 
     set { SetValue(CommandProperty, value); } 
    } 

    public static readonly DependencyProperty CommandProperty = 
     DependencyProperty.Register(
      nameof(Command), 
      typeof(ICommand), 
      typeof(DragCompletedCommandBehavior)); 
} 

我得到的輸出此錯誤。 (這並不拋出異常。它在輸出顯示的某處隱藏程序啓動後。)

System.Windows.Data Error: 2 : Cannot find governing FrameworkElement or FrameworkContentElement for target element. BindingExpression:Path=SeekTo; DataItem=null; target element is 'DragCompletedCommandBehavior' (HashCode=52056421); target property is 'Command' (type 'ICommand')

在另一種行爲,我有這個。

public sealed class LockOnDragBehavior : Behavior<Slider> 
{ 
    static LockOnDragBehavior() 
    { 
     SliderCompositeBehavior.SourceValueProperty.OverrideMetadata(
      typeof(LockOnDragBehavior), 
      new FrameworkPropertyMetadata(
       0d, 
       FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, 
       OnSourceValueChanged)); 
    } 

    private static void OnSourceValueChanged(DependencyObject dpo, DependencyPropertyChangedEventArgs args) 
    { 
     // do something 
    } 
} 

但是OnSourceValueChanged絕不會發生火災。但SliderCompositeBehavior內部的主要OnSourceValueChanged仍在觸發。但新的元數據只是無所事事。

我該如何解決這些問題?我不明白爲什麼包含行爲中的依賴屬性不起作用。有人可以解釋爲什麼嗎?非常感謝。

+0

是的。但正如我所說,如果直接添加它們,這些行爲可能會相互衝突。我能夠做你的話。但是每一種行爲都會變得更大。因爲我必須爲每個行爲寫同樣的東西。例如共享的「Thumb」屬性。或者我用來同步這些行爲的一些其他屬性。 –

+0

這裏學習速度慢..現在得到了。 – WPFUser

+0

@WPFUser修好了;) –

回答

2

我發現了。 after reading this post我明白,元素(在我的情況下嵌套行爲)不是視覺或邏輯樹的一部分。所以數據上下文無法訪問。因此綁定不起作用。

但我沒有使用ProxyBindingwhich was used here我想出了更好的解決方案。

特殊集合BehaviorCollection附加行爲時會發揮一些魔力。但我正在使用ObservableCollection therfor行爲未正確附加。

不幸的是,BehaviorCollection的構造函數是內部的。但是當你有反思的力量時,誰在乎呢? ;)

使用BehaviorCollection代替本質上解決了綁定問題。

如何重寫元數據問題仍然沒有解決。但我想我會嘗試其他方法(如使用另一個依賴屬性),而不是重寫依賴項屬性的元數據。

這裏是對CompositeBehavior<T>類的更正。

[ContentProperty(nameof(BehaviorCollection))] 
public abstract class CompositeBehavior<T> : Behavior<T> 
    where T : DependencyObject 
{ 
    #region Behavior Collection 

    public static readonly DependencyProperty BehaviorCollectionProperty = 
     DependencyProperty.Register(
      $"{nameof(CompositeBehavior<T>)}<{typeof(T).Name}>", 
      typeof(BehaviorCollection), 
      typeof(CompositeBehavior<T>), 
      new FrameworkPropertyMetadata(
       null, 
       FrameworkPropertyMetadataOptions.NotDataBindable)); 

    public BehaviorCollection BehaviorCollection 
    { 
     get 
     { 
      var collection = GetValue(BehaviorCollectionProperty) as BehaviorCollection; 

      if (collection == null) 
      { 
       var constructor = typeof(BehaviorCollection) 
        .GetConstructor(
         BindingFlags.NonPublic | BindingFlags.Instance, 
         null, Type.EmptyTypes, null); 

       collection = (BehaviorCollection) constructor.Invoke(null); 
       collection.Changed += OnCollectionChanged; 
       SetValue(BehaviorCollectionProperty, collection); 
      } 

      return collection; 
     } 
    } 

    private void OnCollectionChanged(object sender, EventArgs eventArgs) 
    { 
     var hashset = new HashSet<Type>(); 
     foreach (var behavior in BehaviorCollection) 
     { 
      if (behavior is Behavior<T> == false) 
      { 
       throw new InvalidOperationException($"{behavior.GetType()} does not inherit from {typeof(Behavior<T>)}."); 
      } 
      if (hashset.Add(behavior.GetType()) == false) 
      { 
       throw new InvalidOperationException($"{behavior.GetType()} is set more than once."); 
      } 
     } 
    } 

    #endregion 

    protected sealed override void OnAttached() 
    { 
     OnSelfAttached(); 
     foreach (var behavior in BehaviorCollection) 
     { 
      behavior.Attach(AssociatedObject); 
     } 
    } 

    protected sealed override void OnDetaching() 
    { 
     OnSelfDetaching(); 
     foreach (var behavior in BehaviorCollection) 
     { 
      behavior.Detach(); 
     } 
    } 

    protected virtual void OnSelfAttached() 
    { 
    } 

    protected virtual void OnSelfDetaching() 
    { 
    } 
}