绑定到Collection与绑定到CollectionViewSource的不同及解决方案
在WPF中,所有的ItemsControl都可以将ItemsSource绑定到后台的数据源上。一般情况下,会将这个ItemsSource直接绑定到一个ObservableCollection上。
比如我们要做一个旅游线路选择的界面。有两个ComboBox用来选择旅游的出发地和目的地。假设Province类中有一个静态的列表包含了中国所有的省份名称。一般就会把这两个ComboBox的ItemsSource属性直接绑定到这个省份列表上。
但是这样做有一个问题。虽然每个ComboBox都有自己的Items.Filter属性用于Item过滤。但是由于绑定到了同一个Collection上,给其中任意一个ComboBox设置Filter就相当于同时给所有的ComboBox设置了Filter。这可不是我们想要的行为。
产生这个行为的原因是,ItemsControl在绑定到Collection上时,会先去获取这个Collection的ICollectionView。由于是同一个Collection,所以不同的ComboBox绑定到同一个Collection上时,获取到的也是同一个ICollectionView。而ComboBox的Filter属性也就是这个ICollectionView的Filter。不单单Filter是这样,与View相同的Sort之类也是这样的。
但是我们的确就是要不同的ItemsControl绑定到同一个Collection上,但是又分别地控制View。不能从Collection上做手脚,就在View上做手脚。我们可以让ComboBox绑定到不同的View上。这些View共享同一个Collection不就行了?代码如下。
<CollectionViewSource x:Key="ProvinceSource" x:Shared="False"
Source="{x:Static m:Province.Provinces}" />
然后使用如下的代码绑定到ComboBox的ItemsSource上。
<ComboBox ItemsSource="{Binding Source={StaticResource ProvinceSource}}"/>
其中起作用的就是ViewSource上面设置的x:Shared属性。有了这个属性,不同的控件实际绑定到的就是不同的View了。
但是在使用这个方法的时候发现了一个问题。就是这个方式与直接绑定到Collection上是不同的。
直接绑定到数据源上,ComboBox显示出来的时候是没有选择任何一项的。
但是绑定到ViewSource上,CombBox一显示出来,默认会选择第一项。(网上已经有人抱怨这个问题了)
如果仅仅是显示上有些不同也就算了。但是,默认选择第一项,CombBox就会去更新SelectedItem所绑定到的后台数据。这样后台原有的数据就被清掉了。这个是不可容忍的。
解决方案倒是有的。
1. 使用一个Converter,在Convert函数中把这个View的CurrentPosition设置为-1就可以了。不过这个Converter也太恶心了。
2. 把Province类的Provinces的get函数改写如下。
get
{
return new ObservableCollection<Province>(allProvince);
}
就是每次都实例化一个新的Collection喽。同时,还要把使用了这个属性的DataTemplate的x:Shared设置为False。这个也比较恶心。不过还好,也就多占用几个Collection本身的内存。
3. 自己写一个CollectionViewSoure,在View生成之后把View的CurrentPosition设置为-1。试了一下,没做出来L
4. 让Province类直接显露一个CollectionViewSource类型的省份数据源。并在get函数里先SetCurrentPosition为-1再return。目前来看这个方案是最佳的。
5. 跑去Microsoft Connect,报个Bug,看看微软的人怎么解释。还没有试。Connect网站太慢了。
整体而言这个还算是个小问题了。就是新手们可能遇到问题的时候会不知道是怎么回事,不知道为什么会有这个问题。