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<CollectionView, object, NotifyCollectionChangedEventArgs>(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 = -;
SetFlag(CollectionViewFlags.IsCurrentBeforeFirst, _currentPosition < );
SetFlag(CollectionViewFlags.IsCurrentAfterLast, _currentPosition < );
SetFlag(CollectionViewFlags.CachedIsEmpty, _currentPosition < );
}
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);
}
)
{
// empty collection - by convention both flags are true and position is -1
SetFlag(CollectionViewFlags.IsCurrentBeforeFirst, true);
SetFlag(CollectionViewFlags.IsCurrentAfterLast, true);
newPosition = -;
}
else
{
// null item, possibly within range.
SetFlag(CollectionViewFlags.IsCurrentBeforeFirst, newPosition < );
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<object>();
LoadSnapshotCore(source);
)
{
SetCurrent(_snapshot[], , );
}
else
{
SetCurrent(, );
}
// If the source doesn't raise collection change events, try to detect changes by polling the enumerator.
_pollForChanges = !(source is INotifyCollectionChanged);
_view = new ListCollectionView(_snapshot);
INotifyCollectionChanged incc = _view as INotifyCollectionChanged;
incc.CollectionChanged += new NotifyCollectionChangedEventHandler(EnumerableCollectionView_OnViewChanged);
INotifyPropertyChanged ipc = _view as INotifyPropertyChanged;
ipc.PropertyChanged += new PropertyChangedEventHandler(EnumerableCollectionView_OnPropertyChanged);
_view.CurrentChanging += new CurrentChangingEventHandler(EnumerableCollectionView_OnCurrentChanging);
_view.CurrentChanged += new EventHandler<object>(EnumerableCollectionView_OnCurrentChanged);
}
2). ProcessCollectionChanged()
处理集合变化事件的方法,主要对改变做了 Add,Remove,Replace 和 Reset 四种情况的处理;分别看一下处理内容:
- Add - Add 操作后,对 snapshot 集合做对应变化,当新增索引 < 0 或小于当前开始索引时,加到集合开始位置,否则插入对应位置;
- Remove - Remove 操作后,在 snapshot 集合中删除对应位置的元素;
- Replace - Replace 操作后,在 snapshot 集合中替换对应位置的元素;
- Reset - Reset 操作后,对应重置 snapshot 集合;
protected override void ProcessCollectionChanged(NotifyCollectionChangedEventArgs args)
{
// Apply the change to the snapshot
switch (args.Action)
{
case NotifyCollectionChangedAction.Add:
|| _snapshot.Count <= args.NewStartingIndex)
{ // Append
; i < args.NewItems.Count; ++i)
{
_snapshot.Add(args.NewItems[i]);
}
}
else
{ // Insert
; i >= ; --i)
{
_snapshot.Insert(args.NewStartingIndex, args.NewItems[i]);
}
}
break;
case NotifyCollectionChangedAction.Remove:
)
{
throw CollectionViewsError.EnumerableCollectionView.RemovedItemNotFound();
}
, index = args.OldStartingIndex + i; i >= ; --i, --index)
{
if (!object.Equals(args.OldItems[i], _snapshot[index]))
{
throw CollectionViewsError.CollectionView.ItemNotAtIndex("removed");
}
_snapshot.RemoveAt(index);
}
break;
case NotifyCollectionChangedAction.Replace:
, index = args.NewStartingIndex + i; i >= ; --i, --index)
{
if (!object.Equals(args.OldItems[i], _snapshot[index]))
{
throw CollectionViewsError.CollectionView.ItemNotAtIndex("replaced");
}
_snapshot[index] = args.NewItems[i];
}
break;
case NotifyCollectionChangedAction.Reset:
LoadSnapshot(SourceCollection);
break;
}
}
3). LoadSnapshot()
加载 snapshot 方法,根据重新加载的元素集合,判断以下属性是否需要响应变化:IsCurrentAfterLast,IsCurrentBeforeFirst,CurrentPosition 和 CurrentItem。
private void LoadSnapshot(IEnumerable source)
{
// Force currency off the collection (gives user a chance to save dirty information).
OnCurrentChanging();
// Remember the values of the scalar properties, so that we can restore
// them and raise events after reloading the data
object oldCurrentItem = CurrentItem;
int oldCurrentPosition = CurrentPosition;
bool oldIsCurrentBeforeFirst = IsCurrentBeforeFirst;
bool oldIsCurrentAfterLast = IsCurrentAfterLast;
// Reload the data
LoadSnapshotCore(source);
// Tell listeners everything has changed
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
OnCurrentChanged();
if (IsCurrentAfterLast != oldIsCurrentAfterLast)
{
OnPropertyChanged(new PropertyChangedEventArgs(IsCurrentAfterLastPropertyName));
}
if (IsCurrentBeforeFirst != oldIsCurrentBeforeFirst)
{
OnPropertyChanged(new PropertyChangedEventArgs(IsCurrentBeforeFirstPropertyName));
}
if (oldCurrentPosition != CurrentPosition)
{
OnPropertyChanged(new PropertyChangedEventArgs(CurrentPositionPropertyName));
}
if (oldCurrentItem != CurrentItem)
{
OnPropertyChanged(new PropertyChangedEventArgs(CurrentItemPropertyName));
}
}
3. ListCollectionView
该类是 CollectionView 类的子类,支持列表类型的数据集合。下面我们也会主要分享它基于 CollectionView 的特殊实现部分:
1). ListCollectionView()
ListCollectionView 类的构造方法,当支持编辑行为时,需要刷新可增加,可删除,可取消编辑的判断;然后设置当前位置和元素;当支持分组时,注册分组描述,分组改变和分组依据的变化处理事件;
public ListCollectionView(IList list) : base(list)
{
_internalList = list;
#if FEATURE_IEDITABLECOLLECTIONVIEW
RefreshCanAddNew();
RefreshCanRemove();
RefreshCanCancelEdit();
#endif
)
{
// don't call virtual IsEmpty in ctor
SetCurrent(, );
}
else
{
SetCurrent(InternalList[], , );
}
#if FEATURE_ICOLLECTIONVIEW_GROUP
_group = new CollectionViewGroupRoot(this);
_group.GroupDescriptionChanged += new EventHandler(OnGroupDescriptionChanged);
((INotifyCollectionChanged)_group).CollectionChanged += new NotifyCollectionChangedEventHandler(OnGroupChanged);
((INotifyCollectionChanged)_group.GroupDescriptions).CollectionChanged += new NotifyCollectionChangedEventHandler(OnGroupByChanged);
#endif
}
2). ProcessCollectionChangedWithAdjustedIndex()
处于集合变化和索引调整的方法,首先判断当前动作的类型:Add,Remove 或 Replace,并针对每种不同类型的操作,进行分别的处理;再对 afterLastHasChanged,beforeFirstHasChanged,currentPositionHasChanged 和 currentItemHasChanged 属性进行设置;
private void ProcessCollectionChangedWithAdjustedIndex(EffectiveNotifyCollectionChangedAction action, object oldItem, object newItem, int adjustedOldIndex, int adjustedNewIndex)
{
EffectiveNotifyCollectionChangedAction effectiveAction = action;
)
{
effectiveAction = EffectiveNotifyCollectionChangedAction.Replace;
}
)
{
)
{
if (action == EffectiveNotifyCollectionChangedAction.Add)
{
return;
}
effectiveAction = EffectiveNotifyCollectionChangedAction.Remove;
}
}
)
{ ... }
else
{ ... }
int originalCurrentPosition = CurrentPosition;
int oldCurrentPosition = CurrentPosition;
object oldCurrentItem = CurrentItem;
bool oldIsCurrentAfterLast = IsCurrentAfterLast;
bool oldIsCurrentBeforeFirst = IsCurrentBeforeFirst;
NotifyCollectionChangedEventArgs args = null, args2 = null;
switch (effectiveAction)
{
case EffectiveNotifyCollectionChangedAction.Add:
// insert into private view
#if FEATURE_ICOLLECTIONVIEW_SORT_OR_FILTER
#if FEATURE_IEDITABLECOLLECTIONVIEW
// (unless it's a special item (i.e. new item))
if (UsesLocalArray && (!IsAddingNew || !object.Equals(_newItem, newItem)))
#else
if (UsesLocalArray)
#endif
{
InternalList.Insert(adjustedNewIndex, newItem);
}
#endif
if (!IsGrouping)
{
AdjustCurrencyForAdd(adjustedNewIndex);
args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, newItem, adjustedNewIndex);
}
#if FEATURE_ICOLLECTIONVIEW_GROUP
else
{
AddItemToGroups(newItem);
}
#endif
break;
case EffectiveNotifyCollectionChangedAction.Remove:
...
case EffectiveNotifyCollectionChangedAction.Replace:
...
case EffectiveNotifyCollectionChangedAction.Move:
...
default:
Debug.Assert(false, "Unexpected Effective Collection Change Action");
break;
}
bool afterLastHasChanged = IsCurrentAfterLast != oldIsCurrentAfterLast;
bool beforeFirstHasChanged = IsCurrentBeforeFirst != oldIsCurrentBeforeFirst;
bool currentPositionHasChanged = CurrentPosition != oldCurrentPosition;
bool currentItemHasChanged = CurrentItem != oldCurrentItem;
oldIsCurrentAfterLast = IsCurrentAfterLast;
oldIsCurrentBeforeFirst = IsCurrentBeforeFirst;
oldCurrentPosition = CurrentPosition;
oldCurrentItem = CurrentItem;
// base class will raise an event to our listeners
if (!IsGrouping)
{
Debug.Assert(!CurrentChangedMonitor.Busy, "Expected _currentChangedMonitor.Busy is false.");
CurrentChangedMonitor.Enter();
using (CurrentChangedMonitor)
{
OnCollectionChanged(args);
if (args2 != null)
{
OnCollectionChanged(args2);
}
}
// Any scalar properties that changed don't need a further notification,
// but do need a new snapshot
...
}
// currency has to change after firing the deletion event,
// so event handlers have the right picture
if (_currentElementWasRemoved)
{
int oldCurPos = originalCurrentPosition;
#if FEATURE_ICOLLECTIONVIEW_GROUP
if (_newGroupedItem != null)
{
oldCurPos = IndexOf(_newGroupedItem);
}
#endif
MoveCurrencyOffDeletedElement(oldCurPos);
// changes to the scalar properties need notification
afterLastHasChanged = afterLastHasChanged || (IsCurrentAfterLast != oldIsCurrentAfterLast);
beforeFirstHasChanged = beforeFirstHasChanged || (IsCurrentBeforeFirst != oldIsCurrentBeforeFirst);
currentPositionHasChanged = currentPositionHasChanged || (CurrentPosition != oldCurrentPosition);
currentItemHasChanged = currentItemHasChanged || (CurrentItem != oldCurrentItem);
}
RaiseCurrencyChanges(false, currentItemHasChanged, currentPositionHasChanged, beforeFirstHasChanged, afterLastHasChanged);
}
4. CollectionViewsError
CollectionViewsError 类中主要定义了 DataGrid 控件数据,就是 CollectionView 中的错误,我们来看一下都定义了哪些错误:
- EnumeratorVersionChanged - InvalidOperationException,“Collection was modified; enumeration operation cannot execute.”
- MemberNotAllowedDuringAddOrEdit - InvalidOperationException,"'{0}' is not allowed during an AddNew or EditItem transaction."
- NoAccessWhileChangesAreDeferred - InvalidOperationException,"This value cannot be accessed while changes are deferred."
- ItemNotAtIndex - InvalidOperationException,"The {0} item is not in the collection."
- RemovedItemNotFound - InvalidOperationException,"The removed item is not found in the source collection."
- CollectionChangedOutOfRange - InvalidOperationException,"The collection change is out of bounds of the original collection."
- AddedItemNotInCollection - InvalidOperationException,"The added item is not in the collection."
- CancelEditNotSupported - InvalidOperationException,"CancelEdit is not supported for the current edit item."
- MemberNotAllowedDuringTransaction - InvalidOperationException,"'{0}' is not allowed during a transaction started by '{1}'."
- MemberNotAllowedForView - InvalidOperationException,"'{0}' is not allowed for this view."
总结
这里我们把 DataGrid 的 CollectionView 相关类介绍完成了,作为 DataGrid 相关分享的第一篇,后面我们会继续分享 Utilities 和最重要的 DataGrid 的相关重点。
最后,再跟大家安利一下 WindowsCommunityToolkit 的官方微博:https://weibo.com/u/6506046490, 大家可以通过微博关注最新动态。
衷心感谢 WindowsCommunityToolkit 的作者们杰出的工作,感谢每一位贡献者,Thank you so much, ALL WindowsCommunityToolkit AUTHORS !!!
Windows Community Toolkit 4.0 - DataGrid - Part01的更多相关文章
- Windows Community Toolkit 4.0 - DataGrid - Part02
概述 在上面一篇 Windows Community Toolkit 4.0 - DataGrid - Part01 中,我们针对 DataGrid 控件的 CollectionView 部分做了详细 ...
- Windows Community Toolkit 4.0 - DataGrid - Part03
概述 在上面一篇 Windows Community Toolkit 4.0 - DataGrid - Part02 中,我们针对 DataGrid 控件的 Utilities 部分做了详细分享.而在 ...
- Windows Community Toolkit 4.0 - DataGrid - Overview
概述 Windows Community Toolkit 4.0 于 2018 月 8 月初发布:Windows Community Toolkit 4.0 Release Note. 4.0 版本相 ...
- Windows Community Toolkit 3.0 - UniformGrid
概述 UniformGrid 控件是一个响应式的布局控件,允许把 items 排列在一组均匀分布的行或列中,以填充整体的可用显示空间,形成均匀的多个网格.默认情况下,网格中的每个单元格大小相同. 这是 ...
- Windows Community Toolkit 3.0 - InfiniteCanvas
概述 InfiniteCanvas 是一个 Canvas 控件,它支持无限画布的滚动,支持 Ink,文本,格式文本,画布缩放操作,撤销重做操作,导入和导出数据. 这是一个非常实用的控件,在“来画视频” ...
- Windows Community Toolkit 3.0 - Gaze Interaction
概述 Gaze Input & Tracking - 也就是视觉输入和跟踪,是一种和鼠标/触摸屏输入非常不一样的交互方式,利用人类眼球的识别和眼球方向角度的跟踪,来判断人眼的目标和意图,从而非 ...
- Windows Community Toolkit 3.0 - CameraPreview
概述 Windows Community Toolkit 3.0 于 2018 年 6 月 2 日 Release,同时正式更名为 Windows Community Toolkit,原名为 UWP ...
- Windows Community Toolkit 3.0 新功能 在WinForms 和 WPF 使用 UWP 控件
本文告诉大家一个令人震惊的消息,Windows Community Toolkit 有一个大更新,现在的版本是 3.0 .最大的提升就是 WinForm 和 WPF 程序可以使用部分 UWP 控件. ...
- 与众不同 windows phone (44) - 8.0 位置和地图
[源码下载] 与众不同 windows phone (44) - 8.0 位置和地图 作者:webabcd 介绍与众不同 windows phone 8.0 之 位置和地图 位置(GPS) - Loc ...
随机推荐
- RHEL 5.7 使用rpm安装XtraBackup问题总结
在Red Hat Enterprise Linux Server release 5.7 (Tikanga)上使用RPM方式安装Percona Xtrabackup 2.4.6时遇到了一些问题,特意总 ...
- 设计模式java----单例模式
一.懒汉式单例 在第一次调用的时候实例化自己,Singleton的唯一实例只能通过getInstance()方法访问.线程不安全 /** * Created by Admin on 2017/3/19 ...
- 【MM系列】SAP基本计量单位更改
公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[MM系列]SAP基本计量单位更改 前言部分 ...
- 转:RowVersion 用法
在数据表更新时,如何表征每个数据行更新时间的先后顺序?最简单的做法是使用RowVersion(行版本)字段,它和时间戳(TimeStamp)类型的功能相似,只不过TimeStamp 已过时,应避免用于 ...
- This network connection does not exist
This network connection does not exist 在windows server 2008上面map了一个磁盘,共享的folder被我停止共享后,点击该磁盘的disconn ...
- 关于Numba开源库(Python语法代码加速处理,看过一个例子,速度可提高6倍)
关于Numba你可能不了解的七个方面 https://yq.aliyun.com/articles/222523 Python GPU加速 (很详细,有代码练习)https://blog.csdn.n ...
- K-means算法的matlab程序(初步)
K-means算法的matlab程序 在https://www.cnblogs.com/kailugaji/p/9648369.html 文章中已经介绍了K-means算法,现在用matlab程序实现 ...
- ECharts图表之柱状折线混合图
Echarts 官网主页 http://echarts.baidu.com/index.html Echarts 更多项目案例 http://echarts.baidu.com/echarts2/ ...
- 部署与管理ZooKeeper(版本有点老,3.4.3)
本文以ZooKeeper3.4.3版本的官方指南为基础:http://zookeeper.apache.org/doc/r3.4.3/zookeeperAdmin.html,补充一些作者运维实践中的要 ...
- P1140 相似基因 这个和之前有一个题目特别像 dp
题目背景 大家都知道,基因可以看作一个碱基对序列.它包含了444种核苷酸,简记作A,C,G,TA,C,G,TA,C,G,T.生物学家正致力于寻找人类基因的功能,以利用于诊断疾病和发明药物. 在一个人类 ...