2012-11-01 82 views
1

如果我這樣創建一個自定義的控制:WPF依賴屬性優先和引用類型的默認值

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設置值置於模板綁定之上!

所以,我明白爲什麼這不起作用,但我想知道是否有解決方案。是否有可能爲MyControl的懶消費者提供默認值ItemsProperty給新的ObservableCollection,只需要以編程方式添加Items,同時允許MyControl的MVVM高級用戶通過DataTemplate綁定到相同的屬性?

這是用於Silverlight的& WPF。在樣式DynamicResource制定者似乎是一個解決方案,但不會爲Silverlight :(工作

更新:

我可以證實SetCurrentValue(ItemsProperty, new ObservableCollection<object>());不正是我想要的東西 - 在WPF它寫入默認值,但它可以通過模板綁定覆蓋任何人都可以提出一個Silverlight相當於說起來容易做:?!小號

另一個更新:

顯然,您可以使用值強制模擬.NET3.5中的SetCurrentValue,並且可以使用這些技術在Silverlight中模擬值強制。也許這裏有一個(冗長的)解決方法。

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

+1

而不是'檢查空項目=新的ObservableCollection ();'你可以調用'SetCurrentValue (ItemsProperty,new ObservableCollection ());'在構造函數中。但是,這也只適用於WPF,而不適用於Silverlight。 – Clemens

+0

謝謝,我們正在取得進展。對Silverlight感到羞恥!另一種解決方案是將默認通過樣式設置爲DynamicResource。再次,沒有silverlight:S –

+0

你可能會存儲原始綁定,例如var x = this.GetBindingExpression(ItemsProperty).ParentBinding;然後將項目設置爲默認值,然後重置綁定到他們是什麼? this.SetBinding(ItemsProperty,x);不知道它在什麼級別重置 - 所以也許這可以工作? – SteveL

回答

0

當的ObservableCollection性能表現不好,我試圖拋出分配給該屬性。我發現引用不會翻譯正確和綁定丟失,不知何故。因此,我實際上避免了設置 ObservableCollection屬性(而不是清除現有屬性並向其中添加元素)。這變成了真的隨着DependencyProperty馬虎,因爲你要在你的setter中多次調用你的getter。您可能需要考慮使用INotifyPropertyChanged。無論如何,這是它看起來像:

編輯:公然偷走從史蒂夫L的答案的答案。我重新修改了它,這樣你只需要一次調用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(即新的PropertyMetadata(new ObservableCollection())。您還必須在構造函數中移除該setter。注意,我不知道該實際上工作,如果沒有,你會想要移動到使用INotifyPropertyChanged肯定...

+0

相同的評論 - >'新的PropertyMetadata(新的ObservableCollection())'如果你這樣做,ObservableCollection 將在所有MyControl實例之間共享!不是一個理想的效果 –

+0

非常真實。您可以使用PropertyMetadata構造函數的不同版本(提供屬性更改回調的版本)。這可能是比新的ObservableCollection ()更清潔的選項,但它有可能需要靜態的相同限制。這就是爲什麼我會*轉換爲INotifyPropertyChanged。 –

+0

或者使用與SteveL的解決方案在getter中檢查null的組合...我更喜歡那個... –

0

你就不能指定依賴項屬性的默認屬性:

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

還是我失去了你所追求的?

編輯:

啊......在這種情況下怎麼樣吸氣劑?:

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); 
     } 
    } 
+1

如果你這樣做,ObservableCollection 將在所有MyControl實例中共享!不是一個理想的效果 –

+0

永遠不會知道它會這麼做!那麼如何在上面的編輯中檢查getter中的null?不嘗試它,但可能會工作... – SteveL

+0

好抓。我喜歡在getter中檢查null ...我偷了它作爲我的回答,但爲了思考它而+1。 –