我們經常會抽取一些可重用的控件,某個屬性是否需要重用,直接決定了這個屬性的綁定方式。
1、完全不可重用的控件
有一些與業務強相關的控件,它們的屬性完全來自ViewModel,越是相對復雜的控件,越容易這樣。比如:
// ChooseUc.xaml
<UserControl>
<StackPanel Orientation="Horizontal">
<Label Content="選擇一個水果: "/>
<ComboBox ItemsSource="{Binding Fruits}"/>
</StackPanel>
</UserControl>
使用的時候直接 <my:ChooseUc />
即可直接綁定到ViewModel里的 List<Fruit> Fruits
,不用做額外的工作。好處是特別方便,代價是與vm完全耦合。
2、完全可重用的控件
類似的控件多了,就能抽出一些完全可重用的控件,這些控件與業務無關。為了實現這種重用性,要做到:
- 抽出所有可變的屬性,定義在控件內部
- 綁定控件內定義的這些屬性
- 外部使用時,再將這些屬性與vm綁定
具體如下:
// ChooseUc.xaml
<UserControl>
<StackPanel x:Name="root" Orientation="Horizontal">
<Label Content="{Binding Header}"/>
<ComboBox ItemsSource="{Binding Items}"/>
</StackPanel>
</UserControl>
相應的cs代碼為:
// ChooseUc.xaml.cs
public ChooseUc() {
InitializeComponent();
root.DataContext = this; // 這句很關鍵!
}
public static readonly DependencyProperty ItemsProperty = DependencyProperty.Register("Items", typeof(IEnumerable), typeof(ChooseUc));
public IEnumerable Items {
get { return (IEnumerable)GetValue(ItemsProperty); }
set { SetValue(ItemsProperty, value); }
}
//HeaderProperty類似,此處省略
使用的時候為:
<my:ChooseUc Header="{Binding FruitHeader}" Items="{Binding Fruits}" />
特別注意 root.DataContext = this;
,這一句把內部的控件與外界隔離了,內部事實上已經看不到ViewModel了,只能看到內部定義的屬性(Header、Items之類的)。麻煩一些,但好處是可以適用於1個標簽+1個下拉框的任何場景,復用性高。
缺點是,在級聯的自定義控件里,簡直是個噩夢。設想有3個UserControl:C>B>A (C包含B、B包含A)。只有C有對應的vm,這種情況下B要包含A的所有屬性。實際情況是,B要包含所有子控件的所有屬性,這無疑是讓人郁悶的!
好在多數情況下我們並不需要級聯嵌套自定義控件,而且我們也可以放棄一些復用性,以獲得默認綁定vm的便利。
3、部分可重用的控件
還有一類情況就是控件部分可重用,考慮這樣一個場景:用戶可以選擇2個水果,這時Fruits
可以直接綁到vm里,但SelectedFruit
需要分別綁定:
// ChooseUc.xaml
<UserControl x:Name="uc">
<StackPanel Orientation="Horizontal">
<Label Content="選擇一個水果: "/>
<ComboBox ItemsSource="{Binding Fruits}" SelectedItem="{Binding SelectedFruit, ElementName=uc}"/>
</StackPanel>
</UserControl>
// 類似的,ChooseUc.xaml.cs里定義SelectedFruit依賴屬性
使用的時候為:
<StackPanel>
<my:ChooseUc SelectedFruit="{Binding Fruit1}" />
<my:ChooseUc SelectedFruit="{Binding Fruit2}" />
</StackPanel>
這種情況下,即使是級聯,Fruits
也不需要重復定義,又獲得了SelectedFruit
的靈活性。這里的關鍵是:只把需要復用的屬性,綁定到控件內部,其他屬性繼承vm就好。
4、小結
實際開發的過程中,大部分的情況是1+3,即完全不重用+部分重用的控件。即使遇到完全重用的控件,也不大會形成級聯。