经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » ASP.net » 查看文章
Windows Community Toolkit 4.0 - DataGrid - Part01
来源:cnblogs  作者:shaomeng  时间:2018/9/25 20:40:07  对本文有异议

概述

在上面一篇 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 调用和卸载做了定义,对于集合改变事件做了绑定,并对布尔类型的属性做了初始设置;

  1. public CollectionView(IEnumerable collection)
  2. {
  3. _sourceCollection = collection ?? throw new ArgumentNullException("collection");
  4. // forward collection change events from underlying collection to our listeners.
  5. INotifyCollectionChanged incc = collection as INotifyCollectionChanged;
  6. if (incc != null)
  7. {
  8. _sourceWeakEventListener =
  9. new WeakEventListener<CollectionView, object, NotifyCollectionChangedEventArgs>(this)
  10. {
  11. // Call the actual collection changed event
  12. OnEventAction = (source, changed, arg) => OnCollectionChanged(source, arg),
  13. // The source doesn't exist anymore
  14. OnDetachAction = (listener) => incc.CollectionChanged -= _sourceWeakEventListener.OnEvent
  15. };
  16. incc.CollectionChanged += _sourceWeakEventListener.OnEvent;
  17. }
  18. _currentItem = null;
  19. _currentPosition = -1;
  20. SetFlag(CollectionViewFlags.IsCurrentBeforeFirst, _currentPosition < 0);
  21. SetFlag(CollectionViewFlags.IsCurrentAfterLast, _currentPosition < 0);
  22. SetFlag(CollectionViewFlags.CachedIsEmpty, _currentPosition < 0);
  23. }

2). OnCollectionChanged()

集合变化的处理,包括对变化动画的判断,当变化不是替换时,触发 count 属性变化;以及对于集合空的判断,空和为空切换时,触发 isEmpty 属性变化,前面在属性说明中我们提提到了;

  1. protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs args)
  2. {
  3. if (args == null)
  4. {
  5. throw new ArgumentNullException("args");
  6. }
  7. unchecked
  8. {
  9. // invalidate enumerators because of a change
  10. ++_timestamp;
  11. }
  12. CollectionChanged?.Invoke(this, args);
  13. // Collection changes change the count unless an item is being
  14. // replaced or moved within the collection.
  15. if (args.Action != NotifyCollectionChangedAction.Replace)
  16. {
  17. OnPropertyChanged(CountPropertyName);
  18. }
  19. bool isEmpty = IsEmpty;
  20. if (isEmpty != CheckFlag(CollectionViewFlags.CachedIsEmpty))
  21. {
  22. SetFlag(CollectionViewFlags.CachedIsEmpty, isEmpty);
  23. OnPropertyChanged(IsEmptyPropertyName);
  24. }
  25. }

3). SetCurrent()

根据当前选择的元素,当前位置和元素数量设置当前选中;新元素不为空时,设置 IsCurrentBeforeFirst 和 IsCurrentAfterLast 属性为 false;当集合为空时,设置两个属性为 true,设置新的选中位置为 -1;否则,根据 newPosition 的值来设置这两个属性;

  1. protected void SetCurrent(object newItem, int newPosition, int count)
  2. {
  3. if (newItem != null)
  4. {
  5. // non-null item implies position is within range.
  6. // We ignore count - it's just a placeholder
  7. SetFlag(CollectionViewFlags.IsCurrentBeforeFirst, false);
  8. SetFlag(CollectionViewFlags.IsCurrentAfterLast, false);
  9. }
  10. else if (count == 0)
  11. {
  12. // empty collection - by convention both flags are true and position is -1
  13. SetFlag(CollectionViewFlags.IsCurrentBeforeFirst, true);
  14. SetFlag(CollectionViewFlags.IsCurrentAfterLast, true);
  15. newPosition = -1;
  16. }
  17. else
  18. {
  19. // null item, possibly within range.
  20. SetFlag(CollectionViewFlags.IsCurrentBeforeFirst, newPosition < 0);
  21. SetFlag(CollectionViewFlags.IsCurrentAfterLast, newPosition >= count);
  22. }
  23. _currentItem = newItem;
  24. _currentPosition = newPosition;
  25. }

 

2. EnumerableCollectionView

该类是 CollectionView 类的子类,支持枚举类型的数据集合。下面我们主要分享它基于 CollectionView 的特殊实现部分:

1). EnumerableCollectionView()

先看看构造方法,首先根据数据源设置当前元素和位置等,绑定集合改变,属性改变和当前的改变和改变后事件;重点说一下 OnCurrentChanging 和 OnCurrentChanged 事件,分别可以在改变前做干预处理,改变后做对应处理;

  1. internal EnumerableCollectionView(IEnumerable source)
  2. : base(source)
  3. {
  4. _snapshot = new ObservableCollection<object>();
  5. LoadSnapshotCore(source);
  6. if (_snapshot.Count > 0)
  7. {
  8. SetCurrent(_snapshot[0], 0, 1);
  9. }
  10. else
  11. {
  12. SetCurrent(null, -1, 0);
  13. }
  14. // If the source doesn't raise collection change events, try to detect changes by polling the enumerator.
  15. _pollForChanges = !(source is INotifyCollectionChanged);
  16. _view = new ListCollectionView(_snapshot);
  17. INotifyCollectionChanged incc = _view as INotifyCollectionChanged;
  18. incc.CollectionChanged += new NotifyCollectionChangedEventHandler(EnumerableCollectionView_OnViewChanged);
  19. INotifyPropertyChanged ipc = _view as INotifyPropertyChanged;
  20. ipc.PropertyChanged += new PropertyChangedEventHandler(EnumerableCollectionView_OnPropertyChanged);
  21. _view.CurrentChanging += new CurrentChangingEventHandler(EnumerableCollectionView_OnCurrentChanging);
  22. _view.CurrentChanged += new EventHandler<object>(EnumerableCollectionView_OnCurrentChanged);
  23. }

2). ProcessCollectionChanged()

处理集合变化事件的方法,主要对改变做了 Add,Remove,Replace 和 Reset 四种情况的处理;分别看一下处理内容:

  • Add - Add 操作后,对 snapshot 集合做对应变化,当新增索引 < 0 或小于当前开始索引时,加到集合开始位置,否则插入对应位置;
  • Remove - Remove 操作后,在 snapshot 集合中删除对应位置的元素;
  • Replace - Replace 操作后,在 snapshot 集合中替换对应位置的元素;
  • Reset - Reset 操作后,对应重置 snapshot 集合;
  1. protected override void ProcessCollectionChanged(NotifyCollectionChangedEventArgs args)
  2. {
  3. // Apply the change to the snapshot
  4. switch (args.Action)
  5. {
  6. case NotifyCollectionChangedAction.Add:
  7. if (args.NewStartingIndex < 0 || _snapshot.Count <= args.NewStartingIndex)
  8. { // Append
  9. for (int i = 0; i < args.NewItems.Count; ++i)
  10. {
  11. _snapshot.Add(args.NewItems[i]);
  12. }
  13. }
  14. else
  15. { // Insert
  16. for (int i = args.NewItems.Count - 1; i >= 0; --i)
  17. {
  18. _snapshot.Insert(args.NewStartingIndex, args.NewItems[i]);
  19. }
  20. }
  21. break;
  22. case NotifyCollectionChangedAction.Remove:
  23. if (args.OldStartingIndex < 0)
  24. {
  25. throw CollectionViewsError.EnumerableCollectionView.RemovedItemNotFound();
  26. }
  27. for (int i = args.OldItems.Count - 1, index = args.OldStartingIndex + i; i >= 0; --i, --index)
  28. {
  29. if (!object.Equals(args.OldItems[i], _snapshot[index]))
  30. {
  31. throw CollectionViewsError.CollectionView.ItemNotAtIndex("removed");
  32. }
  33. _snapshot.RemoveAt(index);
  34. }
  35. break;
  36. case NotifyCollectionChangedAction.Replace:
  37. for (int i = args.NewItems.Count - 1, index = args.NewStartingIndex + i; i >= 0; --i, --index)
  38. {
  39. if (!object.Equals(args.OldItems[i], _snapshot[index]))
  40. {
  41. throw CollectionViewsError.CollectionView.ItemNotAtIndex("replaced");
  42. }
  43. _snapshot[index] = args.NewItems[i];
  44. }
  45. break;
  46. case NotifyCollectionChangedAction.Reset:
  47. LoadSnapshot(SourceCollection);
  48. break;
  49. }
  50. }

3). LoadSnapshot() 

加载 snapshot 方法,根据重新加载的元素集合,判断以下属性是否需要响应变化:IsCurrentAfterLast,IsCurrentBeforeFirst,CurrentPosition 和 CurrentItem。

  1. private void LoadSnapshot(IEnumerable source)
  2. {
  3. // Force currency off the collection (gives user a chance to save dirty information).
  4. OnCurrentChanging();
  5. // Remember the values of the scalar properties, so that we can restore
  6. // them and raise events after reloading the data
  7. object oldCurrentItem = CurrentItem;
  8. int oldCurrentPosition = CurrentPosition;
  9. bool oldIsCurrentBeforeFirst = IsCurrentBeforeFirst;
  10. bool oldIsCurrentAfterLast = IsCurrentAfterLast;
  11. // Reload the data
  12. LoadSnapshotCore(source);
  13. // Tell listeners everything has changed
  14. OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
  15. OnCurrentChanged();
  16. if (IsCurrentAfterLast != oldIsCurrentAfterLast)
  17. {
  18. OnPropertyChanged(new PropertyChangedEventArgs(IsCurrentAfterLastPropertyName));
  19. }
  20. if (IsCurrentBeforeFirst != oldIsCurrentBeforeFirst)
  21. {
  22. OnPropertyChanged(new PropertyChangedEventArgs(IsCurrentBeforeFirstPropertyName));
  23. }
  24. if (oldCurrentPosition != CurrentPosition)
  25. {
  26. OnPropertyChanged(new PropertyChangedEventArgs(CurrentPositionPropertyName));
  27. }
  28. if (oldCurrentItem != CurrentItem)
  29. {
  30. OnPropertyChanged(new PropertyChangedEventArgs(CurrentItemPropertyName));
  31. }
  32. }

 

3. ListCollectionView

该类是 CollectionView 类的子类,支持列表类型的数据集合。下面我们也会主要分享它基于 CollectionView 的特殊实现部分:

1). ListCollectionView()

ListCollectionView 类的构造方法,当支持编辑行为时,需要刷新可增加,可删除,可取消编辑的判断;然后设置当前位置和元素;当支持分组时,注册分组描述,分组改变和分组依据的变化处理事件;

  1. public ListCollectionView(IList list) : base(list)
  2. {
  3. _internalList = list;
  4. #if FEATURE_IEDITABLECOLLECTIONVIEW
  5. RefreshCanAddNew();
  6. RefreshCanRemove();
  7. RefreshCanCancelEdit();
  8. #endif
  9. if (InternalList.Count == 0)
  10. {
  11. // don't call virtual IsEmpty in ctor
  12. SetCurrent(null, -1, 0);
  13. }
  14. else
  15. {
  16. SetCurrent(InternalList[0], 0, 1);
  17. }
  18. #if FEATURE_ICOLLECTIONVIEW_GROUP
  19. _group = new CollectionViewGroupRoot(this);
  20. _group.GroupDescriptionChanged += new EventHandler(OnGroupDescriptionChanged);
  21. ((INotifyCollectionChanged)_group).CollectionChanged += new NotifyCollectionChangedEventHandler(OnGroupChanged);
  22. ((INotifyCollectionChanged)_group.GroupDescriptions).CollectionChanged += new NotifyCollectionChangedEventHandler(OnGroupByChanged);
  23. #endif
  24. }

2). ProcessCollectionChangedWithAdjustedIndex()

处于集合变化和索引调整的方法,首先判断当前动作的类型:Add,Remove 或 Replace,并针对每种不同类型的操作,进行分别的处理;再对 afterLastHasChanged,beforeFirstHasChanged,currentPositionHasChanged 和 currentItemHasChanged 属性进行设置;

  1. private void ProcessCollectionChangedWithAdjustedIndex(EffectiveNotifyCollectionChangedAction action, object oldItem, object newItem, int adjustedOldIndex, int adjustedNewIndex)
  2. {
  3. EffectiveNotifyCollectionChangedAction effectiveAction = action;
  4. if (adjustedOldIndex == adjustedNewIndex && adjustedOldIndex >= 0)
  5. {
  6. effectiveAction = EffectiveNotifyCollectionChangedAction.Replace;
  7. }
  8. else if (adjustedOldIndex == -1)
  9. {
  10. if (adjustedNewIndex < 0)
  11. {
  12. if (action == EffectiveNotifyCollectionChangedAction.Add)
  13. {
  14. return;
  15. }
  16. effectiveAction = EffectiveNotifyCollectionChangedAction.Remove;
  17. }
  18. }
  19. else if (adjustedOldIndex < -1)
  20. { ... }
  21. else
  22. { ... }
  23. int originalCurrentPosition = CurrentPosition;
  24. int oldCurrentPosition = CurrentPosition;
  25. object oldCurrentItem = CurrentItem;
  26. bool oldIsCurrentAfterLast = IsCurrentAfterLast;
  27. bool oldIsCurrentBeforeFirst = IsCurrentBeforeFirst;
  28. NotifyCollectionChangedEventArgs args = null, args2 = null;
  29. switch (effectiveAction)
  30. {
  31. case EffectiveNotifyCollectionChangedAction.Add:
  32. // insert into private view
  33. #if FEATURE_ICOLLECTIONVIEW_SORT_OR_FILTER
  34. #if FEATURE_IEDITABLECOLLECTIONVIEW
  35. // (unless it's a special item (i.e. new item))
  36. if (UsesLocalArray && (!IsAddingNew || !object.Equals(_newItem, newItem)))
  37. #else
  38. if (UsesLocalArray)
  39. #endif
  40. {
  41. InternalList.Insert(adjustedNewIndex, newItem);
  42. }
  43. #endif
  44. if (!IsGrouping)
  45. {
  46. AdjustCurrencyForAdd(adjustedNewIndex);
  47. args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, newItem, adjustedNewIndex);
  48. }
  49. #if FEATURE_ICOLLECTIONVIEW_GROUP
  50. else
  51. {
  52. AddItemToGroups(newItem);
  53. }
  54. #endif
  55. break;
  56. case EffectiveNotifyCollectionChangedAction.Remove:
  57. ...
  58. case EffectiveNotifyCollectionChangedAction.Replace:
  59. ...
  60. case EffectiveNotifyCollectionChangedAction.Move:
  61. ...
  62. default:
  63. Debug.Assert(false, "Unexpected Effective Collection Change Action");
  64. break;
  65. }
  66. bool afterLastHasChanged = IsCurrentAfterLast != oldIsCurrentAfterLast;
  67. bool beforeFirstHasChanged = IsCurrentBeforeFirst != oldIsCurrentBeforeFirst;
  68. bool currentPositionHasChanged = CurrentPosition != oldCurrentPosition;
  69. bool currentItemHasChanged = CurrentItem != oldCurrentItem;
  70. oldIsCurrentAfterLast = IsCurrentAfterLast;
  71. oldIsCurrentBeforeFirst = IsCurrentBeforeFirst;
  72. oldCurrentPosition = CurrentPosition;
  73. oldCurrentItem = CurrentItem;
  74. // base class will raise an event to our listeners
  75. if (!IsGrouping)
  76. {
  77. Debug.Assert(!CurrentChangedMonitor.Busy, "Expected _currentChangedMonitor.Busy is false.");
  78. CurrentChangedMonitor.Enter();
  79. using (CurrentChangedMonitor)
  80. {
  81. OnCollectionChanged(args);
  82. if (args2 != null)
  83. {
  84. OnCollectionChanged(args2);
  85. }
  86. }
  87. // Any scalar properties that changed don't need a further notification,
  88. // but do need a new snapshot
  89. ...
  90. }
  91. // currency has to change after firing the deletion event,
  92. // so event handlers have the right picture
  93. if (_currentElementWasRemoved)
  94. {
  95. int oldCurPos = originalCurrentPosition;
  96. #if FEATURE_ICOLLECTIONVIEW_GROUP
  97. if (_newGroupedItem != null)
  98. {
  99. oldCurPos = IndexOf(_newGroupedItem);
  100. }
  101. #endif
  102. MoveCurrencyOffDeletedElement(oldCurPos);
  103. // changes to the scalar properties need notification
  104. afterLastHasChanged = afterLastHasChanged || (IsCurrentAfterLast != oldIsCurrentAfterLast);
  105. beforeFirstHasChanged = beforeFirstHasChanged || (IsCurrentBeforeFirst != oldIsCurrentBeforeFirst);
  106. currentPositionHasChanged = currentPositionHasChanged || (CurrentPosition != oldCurrentPosition);
  107. currentItemHasChanged = currentItemHasChanged || (CurrentItem != oldCurrentItem);
  108. }
  109. RaiseCurrencyChanges(false, currentItemHasChanged, currentPositionHasChanged, beforeFirstHasChanged, afterLastHasChanged);
  110. }

 

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 !!!

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号