Windows Community Toolkit 4.0 - DataGrid - Part01
概述
在上面一篇 Windows Community Toolkit 4.0 - DataGrid - Overview 中,我们对 DataGrid 控件做了一个概览的介绍,今天开始我们会做进一步的详细分享。
按照概述中分析代码结构的顺序,今天我们先对 CollectionViews 文件夹中的类做详细的分析。
下面是 Windows Community Toolkit Sample App 的示例截图和 code/doc 地址:
Windows Community Toolkit Doc - DataGrid
Windows Community Toolkit Source Code - DataGrid
Namespace: Microsoft.Toolkit.Uwp.UI.Controls; Nuget: Microsoft.Toolkit.Uwp.UI.Controls.DataGrid;
开发过程
首先再来看一下 CollectionViews 文件夹的代码结构:
4 个类中,CollectionView 是 EnumerableCollectionView 和 ListCollectionView 的基类,而 CollectionViewsError 是和 DataGrid 数据源中错误的处理类,接下来我们来分别看一下:
1. CollectionView
CollectionView 类是 DataGrid 数据相关处理的基类,这个类里的处理方法和属性设置很多,同时还针对 FILTER,SORT 和 GROUP 特性做了处理,下面先来看看类中定义的属性:
Count - 表示 DataGrid 控件数据的数量,在 OnCollectionChanged 事件处理中,非 Replace 情况下触发;
IsEmpty - 表示 DataGrid 控件中数据是否为空,同样在 OnCollectionChanged 事件处理中,空和非空状态切换时触发;
Culture - 表示 DataGrid 控件的区域性信息,在 Culture 变化时,包括名称,日历系统,字符排序等会发生变化;
CurrentPosition - 表示 DataGrid 控件的当前位置,在子类的 RaiseCurrencyChanges 和 LoadSnapshot 事件中被使用;
CurrentItem - 表示 DataGrid 控件当前选中的元素,同样在子类的 RaiseCurrencyChanges 和 LoadSnapshot 事件中被使用;
IsCurrentBeforeFirst - 表示 DataGrid 控件中当前选中是否在首个元素之前;
IsCurrentAfterLast - 表示 DataGrid 控件中当前选中是否在最后一个元素之后;
接下来看几个重要的方法:
1). CollectionView()
CollectionView 类的构造方法,可以看到方法中创建了监听器,对时间的 Action 调用和卸载做了定义,对于集合改变事件做了绑定,并对布尔类型的属性做了初始设置;
复制代码
public CollectionView(IEnumerable collection)
{
_sourceCollection = collection ?? throw new ArgumentNullException("collection");
// forward collection change events from underlying collection to our listeners.
INotifyCollectionChanged incc = collection as INotifyCollectionChanged;
if (incc != null)
{
_sourceWeakEventListener =
new WeakEventListener(this)
{
// Call the actual collection changed event
OnEventAction = (source, changed, arg) => OnCollectionChanged(source, arg),
// The source doesn't exist anymore
OnDetachAction = (listener) => incc.CollectionChanged -= _sourceWeakEventListener.OnEvent
};
incc.CollectionChanged += _sourceWeakEventListener.OnEvent;
}
_currentItem = null;
_currentPosition = -1;
SetFlag(CollectionViewFlags.IsCurrentBeforeFirst, _currentPosition < 0);
SetFlag(CollectionViewFlags.IsCurrentAfterLast, _currentPosition < 0);
SetFlag(CollectionViewFlags.CachedIsEmpty, _currentPosition < 0);
}
复制代码
2). OnCollectionChanged()
集合变化的处理,包括对变化动画的判断,当变化不是替换时,触发 count 属性变化;以及对于集合空的判断,空和为空切换时,触发 isEmpty 属性变化,前面在属性说明中我们提提到了;
复制代码
protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs args)
{
if (args == null)
{
throw new ArgumentNullException("args");
}
unchecked
{
// invalidate enumerators because of a change
++_timestamp;
}
CollectionChanged?.Invoke(this, args);
// Collection changes change the count unless an item is being
// replaced or moved within the collection.
if (args.Action != NotifyCollectionChangedAction.Replace)
{
OnPropertyChanged(CountPropertyName);
}
bool isEmpty = IsEmpty;
if (isEmpty != CheckFlag(CollectionViewFlags.CachedIsEmpty))
{
SetFlag(CollectionViewFlags.CachedIsEmpty, isEmpty);
OnPropertyChanged(IsEmptyPropertyName);
}
}
复制代码
3). SetCurrent()
根据当前选择的元素,当前位置和元素数量设置当前选中;新元素不为空时,设置 IsCurrentBeforeFirst 和 IsCurrentAfterLast 属性为 false;当集合为空时,设置两个属性为 true,设置新的选中位置为 -1;否则,根据 newPosition 的值来设置这两个属性;
复制代码
protected void SetCurrent(object newItem, int newPosition, int count)
{
if (newItem != null)
{
// non-null item implies position is within range.
// We ignore count - it's just a placeholder
SetFlag(CollectionViewFlags.IsCurrentBeforeFirst, false);
SetFlag(CollectionViewFlags.IsCurrentAfterLast, false);
}
else if (count == 0)
{
// empty collection - by convention both flags are true and position is -1
SetFlag(CollectionViewFlags.IsCurrentBeforeFirst, true);
SetFlag(CollectionViewFlags.IsCurrentAfterLast, true);
newPosition = -1;
}
else
{
// null item, possibly within range.
SetFlag(CollectionViewFlags.IsCurrentBeforeFirst, newPosition < 0);
SetFlag(CollectionViewFlags.IsCurrentAfterLast, newPosition >= count);
}
_currentItem = newItem;
_currentPosition = newPosition;
}
复制代码
2. EnumerableCollectionView
该类是 CollectionView 类的子类,支持枚举类型的数据集合。下面我们主要分享它基于 CollectionView 的特殊实现部分:
1). EnumerableCollectionView()
先看看构造方法,首先根据数据源设置当前元素和位置等,绑定集合改变,属性改变和当前的改变和改变后事件;重点说一下 OnCurrentChanging 和 OnCurrentChanged 事件,分别可以在改变前做干预处理,改变后做对应处理;
复制代码
internal EnumerableCollectionView(IEnumerable source)
: base(source)
{
_snapshot = new ObservableCollection