背景:在项目中遇到了这样的问题,使用的DataGrid需要默认选中第一条数据,即数据加载后,无需用户点击,即可默认选中一项,并且,DataGrid支持筛选操作,需要完成这样的功能,数据源中的数据项的某些属性更新时,需要刷新UI,并且需要保持当前的多选项。
问题:上面的背景中提到了两个问题,一是需要默认选中第一项;二是DataGrid相关的视图进行刷新时需要保持上次的多选项。默认选中第一项可以在数据加载完成后使用一个双向绑定完成,而保持上次的多选项,DataGrid提供了一个内部属性用来控制,即为IsSynchronizedWithCurrentItem,其值为false时,会保持上次的多选项,但此时设置当前选中的。这样只可以实现,便却要判断每一个当前选中项可以会空的场景,并及时的设置当前选中项为视图中的第一项,太过复杂,容易出错。
解决方案:自定一个集合及所使用的视图类,在视图类中判断当前选中项为空且视图列表不为空的情况下,设置当前选中项为列表中第一项,否则为需要保持多选项的情况,即需要控件的IsSynchronizedWithCurrentItem=false来保证,在视图中设置默认选中项时又需要设置IsSynchronizedWithCurrentItem=true才能保持视图中的更改可以及时的刷新到UI上去。因此需要做好的两种情况的判断,从而正确切换IsSynchronizedWithCurrentItem,保证UI的正确性。
实现:数据的筛选需要重新Filter一次,需要调用视图的Refresh操作,通过分析源码,分析出刷新的过程,实现的示例图可参照下图:
注意:IsSynchronizedWithCurrentItem主要是用来同步当前选中项,在多UI使用同一数据源时可以同步当前选中项,但这里的实现并不支持,因为需要视图类与UI耦合。
实现源码:
public class CustomCollection<T> : ObservableCollection<T>, ICollectionViewFactory { public CustomCollection(Selector selector) { _selector = selector; } private readonly Selector _selector; public ICollectionView CreateView() { return new MyView(this, _selector); } } public class MyView : ListCollectionView { public MyView(IList list, Selector selector) : base(list) { if (null != selector) { _selector = selector; _dgSynchronized = _selector.IsSynchronizedWithCurrentItem; } } private readonly Selector _selector; private readonly bool? _dgSynchronized; private object _preCurrentItem = null; protected override void RefreshOverride() { _preCurrentItem = CurrentItem; base.RefreshOverride(); } protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs args) { if (null != _selector) { //some scen, CurrentItem can be set by Func before this method, avoid this if (_preCurrentItem != CurrentItem) SetCurrent(null, -1); if (null == CurrentItem && InternalCount > 0) { _selector.IsSynchronizedWithCurrentItem = true; SetCurrent(InternalList[0], 0); } else _selector.IsSynchronizedWithCurrentItem = false; } base.OnCollectionChanged(args); } protected override void OnCurrentChanged() { base.OnCurrentChanged(); if (null != _selector) { _selector.IsSynchronizedWithCurrentItem = _dgSynchronized; } } }