依赖属性不起作用(使复合行为)

问题描述:

我试图使任意简单行为组成的复合行为。我发现行为非常灵活的制作自定义控件的方式。依赖属性不起作用(使复合行为)

目前我有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修好了;) –

我发现了。 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() 
    { 
    } 
}