Skip to content

Commit ec86ec4

Browse files
authored
Merge pull request #310 from w-ahmad/feat/enumerable_source
feat: Add support IEnumerable data sources
2 parents ed762ca + 2320d08 commit ec86ec4

File tree

9 files changed

+734
-33
lines changed

9 files changed

+734
-33
lines changed

.github/workflows/ci-build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ jobs:
3636
/p:Version=0.0.${{ github.run_number }}-dev
3737
3838
- name: Setup VSTest
39-
uses: darenm/Setup-VSTest@v1
39+
uses: darenm/Setup-VSTest@v1.3
4040

4141
- name: Build Tests
4242
run: |

src/ColumnFilterHandler.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Collections;
23
using System.Collections.Generic;
34
using System.Diagnostics.CodeAnalysis;
45
using System.Linq;
@@ -40,7 +41,7 @@ public virtual IList<TableViewFilterItem> GetFilterItems(TableViewColumn column,
4041
}));
4142
}
4243

43-
collectionView.Source = column.TableView.ItemsSource;
44+
collectionView.Source = (column.TableView.ItemsSource as IEnumerable) ?? Enumerable.Empty<object>();
4445

4546
var items = _tableView.ShowFilterItemsCount ?
4647
GetFilterItemsWithCount(column, searchText, collectionView) :

src/Extensions/CollectionExtensions.cs

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
using System;
1+
using Microsoft.UI.Xaml.Data;
2+
using System;
3+
using System.Collections;
24
using System.Collections.Generic;
35
using System.Linq;
46

@@ -38,4 +40,83 @@ public static void RemoveWhere<T>(this ICollection<T> collection, Predicate<T> p
3840
collection.Remove(item);
3941
}
4042
}
43+
44+
/// <summary>
45+
/// Gets the index of the first occurrence of an item in the enumerable.
46+
/// </summary>
47+
/// <param name="enumerable">The enumerable to search.</param>
48+
/// <param name="item">The item to find.</param>
49+
/// <returns>The index of the item, or -1 if not found.</returns>
50+
public static int IndexOf(this IEnumerable enumerable, object? item)
51+
{
52+
if (enumerable is IList list)
53+
return list.IndexOf(item);
54+
55+
if (enumerable is ICollectionView collectionView)
56+
return collectionView.IndexOf(item);
57+
58+
59+
var index = 0;
60+
foreach (var element in enumerable)
61+
{
62+
if (Equals(element, item))
63+
return index;
64+
65+
index++;
66+
}
67+
68+
return -1;
69+
}
70+
71+
/// <summary>
72+
/// Gets a value indicating whether the enumerable is read-only.
73+
/// </summary>
74+
/// <param name="enumerable">The enumerable to check.</param>
75+
/// <returns></returns>
76+
public static bool IsReadOnly(this IEnumerable enumerable)
77+
{
78+
if (enumerable is IList list)
79+
return list.IsReadOnly;
80+
81+
if (enumerable is ICollectionView collectionView)
82+
return collectionView.IsReadOnly;
83+
84+
return true;
85+
}
86+
87+
public static void Add(this IEnumerable enumerable, object? item)
88+
{
89+
if (enumerable is IList list)
90+
list.Add(item);
91+
92+
if (enumerable is ICollectionView collectionView)
93+
collectionView.Add(item);
94+
}
95+
96+
public static void Insert(this IEnumerable enumerable, int index, object? item)
97+
{
98+
if (enumerable is IList list)
99+
list.Insert(index, item);
100+
101+
if (enumerable is ICollectionView collectionView)
102+
collectionView.Insert(index, item);
103+
}
104+
105+
public static void Remove(this IEnumerable enumerable, object? item)
106+
{
107+
if (enumerable is IList list)
108+
list.Remove(item);
109+
110+
if (enumerable is ICollectionView collectionView)
111+
collectionView.Remove(item);
112+
}
113+
114+
public static void Clear(this IEnumerable enumerable)
115+
{
116+
if (enumerable is IList list)
117+
list.Clear();
118+
119+
if (enumerable is ICollectionView collectionView)
120+
collectionView.Clear();
121+
}
41122
}

src/Extensions/ObjectExtensions.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,7 @@ private static bool TryCreateGenericIndexerExpression(Type type, ParameterExpres
400400
var listType = list.GetType();
401401
Type? itemType = null;
402402
var isICustomTypeProvider = false;
403+
var isWinRTObject = false;
403404

404405
// If it's a generic enumerable, get the generic type.
405406

@@ -410,11 +411,14 @@ private static bool TryCreateGenericIndexerExpression(Type type, ParameterExpres
410411
if (itemType != null)
411412
{
412413
isICustomTypeProvider = typeof(ICustomTypeProvider).IsAssignableFrom(itemType);
414+
#if WINDOWS
415+
isWinRTObject = typeof(WinRT.IInspectable).IsAssignableFrom(itemType);
416+
#endif
413417
}
414418

415419
// Bare IEnumerables mean that result type will be object. In that case, try to get something more interesting.
416420
// Or, if the itemType implements ICustomTypeProvider, try to retrieve the custom type from one of the object instances.
417-
if (itemType == null || itemType == typeof(object) || isICustomTypeProvider)
421+
if (itemType == null || itemType == typeof(object) || isICustomTypeProvider || isWinRTObject)
418422
{
419423
// No type was located yet. Does the list have anything in it?
420424
Type? firstItemType = null;

src/ItemsSource/CollectionView.Properties.cs

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
using Microsoft.UI.Xaml.Data;
22
using System.Collections;
33
using System.Collections.Generic;
4-
using System.Collections.Specialized;
54
using Windows.Foundation.Collections;
5+
using WinUI.TableView.Extensions;
66

77
namespace WinUI.TableView;
88

@@ -11,24 +11,22 @@ partial class CollectionView
1111
/// <summary>
1212
/// Gets or sets the source collection.
1313
/// </summary>
14-
public IList Source
14+
public IEnumerable Source
1515
{
1616
get => _source;
1717
set
1818
{
1919
if (_source == value) return;
2020

21-
if (_source is not null) DetachPropertyChangedHandlers(_source);
21+
DetachCollectionChangedHandlers(_source);
22+
DetachPropertyChangedHandlers(_source);
2223

2324
_source = value;
24-
AttachPropertyChangedHandlers(_source);
2525

26-
_collectionChangedListener?.Detach();
26+
AttachCollectionChangedHandlers(_source);
27+
AttachPropertyChangedHandlers(_source);
2728

28-
if (_source is INotifyCollectionChanged sourceNcc)
29-
{
30-
_collectionChangedListener = new(this, sourceNcc, OnSourceCollectionChanged);
31-
}
29+
CreateItemsCopy(_source);
3230

3331
HandleSourceChanged();
3432
OnPropertyChanged();
@@ -103,7 +101,7 @@ public object? CurrentItem
103101
/// <summary>
104102
/// Gets a value indicating whether the collection is read-only.
105103
/// </summary>
106-
public bool IsReadOnly => _source == null || _source.IsReadOnly;
104+
public bool IsReadOnly => _source == null || _source.IsReadOnly();
107105

108106
/// <summary>
109107
/// Gets or sets a value indicating whether live shaping is enabled.

src/ItemsSource/CollectionView.cs

Lines changed: 112 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
using System.Linq;
99
using Windows.Foundation;
1010
using Windows.Foundation.Collections;
11-
using WinUI.TableView.Helpers;
11+
using WinUI.TableView.Extensions;
1212

1313
namespace WinUI.TableView;
1414

@@ -17,19 +17,19 @@ namespace WinUI.TableView;
1717
/// </summary>
1818
internal partial class CollectionView : ICollectionView, ISupportIncrementalLoading, INotifyPropertyChanged, IComparer<object?>
1919
{
20-
private IList _source = default!;
20+
private IEnumerable _source = new List<object>();
21+
private object[] _itemsCopy = []; // In case the source is ICollection, keep a copy of the items to keep track of removed items.
2122
private bool _allowLiveShaping;
2223
private readonly List<object?> _view = [];
2324
private readonly ObservableCollection<FilterDescription> _filterDescriptions = [];
2425
private readonly ObservableCollection<SortDescription> _sortDescriptions = [];
25-
private CollectionChangedListener<CollectionView>? _collectionChangedListener;
2626

2727
/// <summary>
2828
/// Initializes a new instance of the <see cref="CollectionView"/> class.
2929
/// </summary>
3030
/// <param name="source">The source collection.</param>
3131
/// <param name="liveShapingEnabled">Indicates whether live shaping is enabled.</param>
32-
public CollectionView(IList? source = null, bool liveShapingEnabled = true)
32+
public CollectionView(IEnumerable? source = null, bool liveShapingEnabled = true)
3333
{
3434
_filterDescriptions.CollectionChanged += OnFilterDescriptionsCollectionChanged;
3535
_sortDescriptions.CollectionChanged += OnSortDescriptionsCollectionChanged;
@@ -64,6 +64,36 @@ private void OnSortDescriptionsCollectionChanged(object? sender, NotifyCollectio
6464
HandleSortChanged();
6565
}
6666

67+
/// <summary>
68+
/// Attaches collection changed handlers to the source collection.
69+
/// </summary>
70+
private void AttachCollectionChangedHandlers(IEnumerable source)
71+
{
72+
if (source is INotifyCollectionChanged sourceNcc)
73+
{
74+
sourceNcc.CollectionChanged += OnSourceCollectionChanged;
75+
}
76+
else if (source is ICollectionView sourceCV)
77+
{
78+
sourceCV.VectorChanged += OnSourceVectorChanged;
79+
}
80+
}
81+
82+
/// <summary>
83+
/// Detaches collection changed handlers from the source collection.
84+
/// </summary>
85+
private void DetachCollectionChangedHandlers(IEnumerable source)
86+
{
87+
if (source is INotifyCollectionChanged sourceNcc)
88+
{
89+
sourceNcc.CollectionChanged -= OnSourceCollectionChanged;
90+
}
91+
else if (source is ICollectionView sourceCV)
92+
{
93+
sourceCV.VectorChanged -= OnSourceVectorChanged;
94+
}
95+
}
96+
6797
/// <summary>
6898
/// Attaches property changed handlers to the items in the collection.
6999
/// </summary>
@@ -92,6 +122,75 @@ private void DetachPropertyChangedHandlers(IEnumerable? items)
92122
}
93123
}
94124

125+
/// <summary>
126+
/// Handles changes to the source vector.
127+
/// </summary>
128+
private void OnSourceVectorChanged(IObservableVector<object> sender, IVectorChangedEventArgs args)
129+
{
130+
var index = (int)args.Index;
131+
132+
switch (args.CollectionChange)
133+
{
134+
case CollectionChange.ItemInserted:
135+
if (_deferCounter <= 0)
136+
{
137+
if (index < Count)
138+
{
139+
var item = sender[index];
140+
AttachPropertyChangedHandlers(new object[] { item });
141+
HandleItemAdded(index, item);
142+
}
143+
else
144+
{
145+
HandleSourceChanged();
146+
}
147+
}
148+
149+
break;
150+
case CollectionChange.ItemRemoved:
151+
if (_deferCounter <= 0)
152+
{
153+
if (index < _itemsCopy.Length)
154+
{
155+
var item = _itemsCopy[index];
156+
DetachPropertyChangedHandlers(new object[] { item });
157+
HandleItemRemoved(index, item);
158+
}
159+
else
160+
{
161+
HandleSourceChanged();
162+
}
163+
}
164+
165+
break;
166+
case CollectionChange.ItemChanged:
167+
case CollectionChange.Reset:
168+
if (_deferCounter <= 0)
169+
{
170+
HandleSourceChanged();
171+
}
172+
173+
DetachPropertyChangedHandlers(_itemsCopy);
174+
AttachPropertyChangedHandlers(_source);
175+
176+
break;
177+
}
178+
179+
CreateItemsCopy(_source);
180+
}
181+
182+
/// <summary>
183+
/// Creates a copy of the items from the collection if it implements ICollectionView.
184+
/// </summary>
185+
private void CreateItemsCopy(IEnumerable source)
186+
{
187+
if (source is ICollectionView collectionView)
188+
{
189+
_itemsCopy = new object[collectionView.Count];
190+
collectionView.CopyTo(_itemsCopy, 0);
191+
}
192+
}
193+
95194
/// <summary>
96195
/// Handles changes to the source collection.
97196
/// </summary>
@@ -137,6 +236,9 @@ private void OnSourceCollectionChanged(object? arg1, NotifyCollectionChangedEven
137236
HandleSourceChanged();
138237
}
139238

239+
DetachPropertyChangedHandlers(e.OldItems);
240+
AttachPropertyChangedHandlers(_source);
241+
140242
break;
141243
}
142244
}
@@ -242,7 +344,7 @@ private void HandleSourceChanged()
242344
/// </summary>
243345
private void HandleFilterChanged()
244346
{
245-
if (FilterDescriptions.Any())
347+
if (FilterDescriptions.Count > 0)
246348
{
247349
for (var index = 0; index < _view.Count; index++)
248350
{
@@ -259,19 +361,21 @@ private void HandleFilterChanged()
259361

260362
var viewHash = new HashSet<object?>(_view);
261363
var viewIndex = 0;
262-
for (var index = 0; index < _source.Count; index++)
364+
var i = 0;
365+
foreach (var item in _source)
263366
{
264-
var item = _source[index]!;
265367
if (viewHash.Contains(item))
266368
{
267369
viewIndex++;
268370
continue;
269371
}
270372

271-
if (HandleItemAdded(index, item, viewIndex))
373+
if (HandleItemAdded(i, item, viewIndex))
272374
{
273375
viewIndex++;
274376
}
377+
378+
i++;
275379
}
276380
}
277381

0 commit comments

Comments
 (0)