2017-01-09 3 views
1

私は任意の単純な動作で構成される複合的な動作をしようとしています。私は、ビヘイビアがカスタムコントロールを作成する非常に柔軟な方法を見つけました。依存関係のプロパティが機能しない(複合的な動作をする)

現在、私は5つの動作をスライダーに実装しています。彼らは互いに衝突する可能性があります。

これらの動作は、1つのコントロール用に設計されています。私はそれらのそれぞれが互いに矛盾することなく独立して働くように設計することができました(私はこれを実行し、うまくいきましたが、ちょうど醜いのですべてを取り除いた)。

ポイント、私はすべての動作のために同じコードを書き直したくありません。

私は1つのコントロールに対して複合的な動作をさせようとしています。この振る舞いには、すべての包含動作で共有されるいくつかの添付プロパティがあります。そのためにこれらの行動は互いに衝突しない。また、多くのコードの冗長性がなくなりました。今や行動を含むことがずっと簡単になります。

ここでは、より良いアイデアを得るための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; 
    } 
} 

は、今私が見ることができる2つの問題がある(唯一の依存関係が簡略化のために示されている)です。

  • 振る舞いを含む内部の依存性プロパティ定義はまったく機能しません。

  • 依存プロパティのオーバーライドメタデータは、プロパティを含むためには機能しません。 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は発生しません。メインOnSourceValueChangedSliderCompositeBehaviorの内部がまだ発火します。新しいメタデータは何もしません。

これらの問題を解決するにはどうすればよいですか?なぜ私は挙動を含む内部の依存関係のプロパティが機能しないのか分かりません。何らかの理由で説明できますか?どうもありがとうございます。

+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() 
    { 
    } 
} 
関連する問題