【问题标题】:WPF dependency property precedence & reference type Default ValuesWPF 依赖属性优先级和引用类型默认值
【发布时间】:2012-10-22 07:38:47
【问题描述】:

如果我创建一个这样的自定义控件:

public class MyControl : ContentControl
{
   public static readonly DependencyProperty ItemsProperty =               
         DependencyProperty.Register(
                "Items", 
                typeof(ObservableCollection<object>), 
                typeof(MyControl), 
                new PropertyMetadata(null));

   public MyControl()
   {   
       // Setup a default value to empty collection
       // so users of MyControl can call MyControl.Items.Add()
       Items = new ObservableCollection<object>();
   }

   public ObservableCollection<object> Items
   { 
      get { return (ObservableCollection<object>)GetValue(ItemsProperty); } 
      set { SetValue(ItemsProperty, value); } 
   } 
}

然后允许用户像这样在 Xaml 中绑定到它:

<DataTemplate>
    <MyControl Items="{Binding ItemsOnViewModel}"/>
</DataTemplate>

那么绑定永远不会起作用!这是由于Dependency Property Precedence 将CLR Set 值置于模板绑定之上!

所以,我明白为什么这不起作用,但我想知道是否有解决方案。是否可以为只想以编程方式添加项目的 MyControl 的懒惰消费者提供新的 ObservableCollection 的 ItemsProperty 的默认值,同时允许 My Control 的 MVVM 高级用户通过 DataTemplate 绑定到相同的属性?

这适用于 Silverlight 和 WPF。样式中的 DynamicResource 设置器似乎是一个解决方案,但这不适用于 Silverlight :(

更新:

我可以确认 SetCurrentValue(ItemsProperty, new ObservableCollection&lt;object&gt;()); 完全符合我的要求 - 在 WPF 中。它写入默认值,但它可以被模板绑定覆盖。 谁能推荐一个等效的 Silverlight? 说起来容易做起来难! :s

另一个更新:

显然,您可以使用值强制在 .NET3.5 中模拟 SetCurrentValue,并且可以使用这些技术在 Silverlight 中模拟值强制。也许这里有一个(冗长的)解决方法。

SetCurrentValue workaround for .NET3.5 using Value Coercion
Value Coercion workaround for Silverlight

【问题讨论】:

  • 您可以在构造函数中调用SetCurrentValue(ItemsProperty, new ObservableCollection&lt;object&gt;()); 而不是Items = new ObservableCollection&lt;object&gt;();。但这也只适用于 WPF,不适用于 Silverlight。
  • 谢谢,我们正在取得进展。为银光感到羞耻!另一种解决方案是将默认 via 样式设置为 DynamicResource。同样,没有银光:S
  • 您是否可以存储原始绑定,例如 var x = this.GetBindingExpression(ItemsProperty).ParentBinding;然后将项目设置为默认值,然后将绑定重置为原来的? this.SetBinding(ItemsProperty, x);不确定它会重置到什么级别 - 所以也许这可以工作?
  • 有可能——我在 StackOverflow 中搜索了 SetCurrentValue() silverlight 并找到了另一篇帖子,没有代码解决方案,但他们确实谈到了一些奇怪的绑定黑客来实现这一点。太可惜了,似乎没有本地方法可以做到这一点!
  • 它适用于 Silverlight。你有测试用例吗?

标签: wpf silverlight xaml dependency-properties


【解决方案1】:

当 ObservableCollection 属性出现异常时,我会尝试丢弃对该属性的赋值。我发现参考文献翻译不正确,绑定丢失了,不知何故。因此,我实际上避免设置 ObservableCollection 属性(相反,我更喜欢清除现有属性并向其添加元素)。这对于 DependencyProperty 变得真的草率,因为您将在您的 setter 中多次调用您的 getter。您可能要考虑改用 INotifyPropertyChanged。无论如何,这就是它的样子:

编辑:公然从 SteveL 的答案中窃取吸气剂。我对其进行了一点修改,以便您只需调用一次 GetValue,就可以了。很好的解决方法。

public ObservableCollection<object> Items
{ 
    get
    {
        ObservableCollection<object> coll = (ObservableCollection<object>)GetValue(ItemsProperty);
        if (coll == null)
        {
            coll = new ObservableCollection<object>();
            this.SetValue(ItemsProperty, coll);
        }

        return coll;
    }
    set 
    {
        ObservableCollection<object> coll = Items;
        coll.Clear();
        foreach(var item in value)
            coll.Add(item);
    }
} 

请注意,这取决于您的默认设置是否正确。这意味着将静态 ItemsProperty 默认更改为正确类型的新 ObservableCollection(即 new PropertyMetadata(new ObservableCollection())。您还必须在构造函数中删除该设置器。注意,我不知道是否这样'实际上会起作用。如果没有,您肯定会想要使用 INotifyPropertyChanged...

【讨论】:

  • 对此有相同的评论 -> new PropertyMetadata(new ObservableCollection()) 如果你这样做, ObservableCollection 将在所有 MyControl 实例之间共享!效果不佳
  • 非常正确。您可以使用不同版本的 PropertyMetadata 构造函数(提供属性更改回调的版本)。这可能是比 new ObservableCollection() 更简洁的选项,但它具有相同的限制,可能需要是静态的。这就是为什么我真的会转换为 INotifyPropertyChanged。
  • 或者结合 SteveL 的在 getter 中检查 null 的解决方案...我更喜欢这样...
【解决方案2】:

不能只指定依赖属性的默认属性吗:

  public static readonly DependencyProperty ItemsProperty = DependencyProperty.Register(
        "Items",
        typeof(ObservableCollection<object>),
        typeof(CaseDetailControl),
        new PropertyMetadata(new ObservableCollection<object>()));

还是我错过了你的追求?

编辑:

啊...在这种情况下,如何在 getter 上检查 null 呢?:

    public ObservableCollection<object> Items
    {
        get
        {
            if ((ObservableCollection<object>)GetValue(ItemsProperty) == null)
            {
                this.SetValue(ItemsProperty, new ObservableCollection<object>());
            }

            return (ObservableCollection<object>)GetValue(ItemsProperty);
        }

        set
        {
            this.SetValue(ItemsProperty, value);
        }
    }

【讨论】:

  • 如果你这样做, ObservableCollection 将在所有 MyControl 实例之间共享!效果不佳
  • 从来不知道它会那样做!那么如何在上面的编辑中检查 getter 中的 null 呢?没试过,但可能有用...
  • 好收获。我喜欢在 getter 中检查 null ......我偷了它作为我的答案,但是 +1 给你考虑它。
  • 除了依赖属性的 CLR 包装器中的 GetValue/SetValue 之外,您不应执行任何操作的规则怎么样(请参阅 Checklist for Defining a Dependency Property 中的 实现包装器 部分)?我怀疑这是否真的与 getter 相关,但 XAML 生成的代码可能会通过绕过 CLR 包装器并直接调用 GetValue 来获取属性值。
  • 伙计们,一旦您在属性上调用 SetValue(),由于 DependencyProperty 优先级,DataTemplate 绑定不再起作用。我必须 -1 这不是一个解决方案,但是,很好的尝试!
猜你喜欢
  • 2011-05-13
  • 2011-08-19
  • 2015-08-24
  • 1970-01-01
  • 2011-12-01
  • 2013-04-05
  • 1970-01-01
  • 2018-06-07
相关资源
最近更新 更多