Skip to content

Commit

Permalink
feat: ItemsSource-bound ItemCollection
Browse files Browse the repository at this point in the history
  • Loading branch information
MartinZikmund committed Nov 17, 2020
1 parent 96a0fc3 commit 2b4d4aa
Showing 1 changed file with 80 additions and 11 deletions.
91 changes: 80 additions & 11 deletions src/Uno.UI/UI/Xaml/Controls/ItemCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,17 @@
using Uno.Extensions;
using Uno.Extensions.Specialized;
using System.Linq;
using Uno.Disposables;
using System.Collections.Specialized;

namespace Windows.UI.Xaml.Controls
{
public sealed partial class ItemCollection : IList<object>, IEnumerable<object>, IObservableVector<object>
{
private readonly IList<object> _inner = new List<object>();

private IEnumerable _itemsSource = null;
private IList _itemsSource = null;
private readonly SerialDisposable _itemsSourceCollectionChangeDisposable = new SerialDisposable();

public event VectorChangedEventHandler<object> VectorChanged;

Expand Down Expand Up @@ -58,7 +61,7 @@ public void Clear()

public bool Contains(object item)
{
if (_itemsSource != null)
if (_itemsSource == null)
{
return _inner.Contains(item);
}
Expand All @@ -70,13 +73,18 @@ public bool Contains(object item)

public void CopyTo(object[] array, int arrayIndex)
{
if (_itemsSource != null)
if (_itemsSource == null)
{
_inner.CopyTo(array, arrayIndex);
}
else
{
throw new NotImplementedException();
int targetIndex = arrayIndex;
foreach (var item in _itemsSource)
{
array[targetIndex] = item;
targetIndex++;
}
}
}

Expand Down Expand Up @@ -138,22 +146,83 @@ public object this[int index]

internal void SetItemsSource(object itemsSource)
{
var unwrappedSource = UnwrapItemsSource(itemsSource);
if (_itemsSource == itemsSource)
{
// Items source did not actually change.
return;
}

if (unwrappedSource is IList itemsSourceList)
if (itemsSource == null)
{
_itemsSource = itemsSourceList;
_itemsSource = null;
}
else if (unwrappedSource is IEnumerable itemsSourceEnumerable)
else
{
_itemsSource = itemsSourceEnumerable.ToObjectArray();
var unwrappedSource = UnwrapItemsSource(itemsSource);

if (unwrappedSource is IList itemsSourceList)
{
_itemsSource = itemsSourceList;
}
else if (unwrappedSource is IEnumerable itemsSourceEnumerable)
{
_itemsSource = itemsSourceEnumerable.ToObjectArray();
}
else
{
throw new InvalidOperationException("Only IList- or IEnumerable-based ItemsSource is supported.");
}

ObserveCollectionChanged();
}

VectorChanged?.Invoke(this, new VectorChangedEventArgs(CollectionChange.Reset, 0));
}

private void ObserveCollectionChanged()
{
if (_itemsSource is INotifyCollectionChanged existingObservable)
{
// This is a workaround for a bug with EventRegistrationTokenTable on Xamarin, where subscribing/unsubscribing to a class method directly won't
// remove the handler.
NotifyCollectionChangedEventHandler handler = OnItemsSourceCollectionChanged;
_itemsSourceCollectionChangeDisposable.Disposable = Disposable.Create(() =>
existingObservable.CollectionChanged -= handler
);
existingObservable.CollectionChanged += handler;
}
else if (_itemsSource is IObservableVector<object> observableVector)
{
// This is a workaround for a bug with EventRegistrationTokenTable on Xamarin, where subscribing/unsubscribing to a class method directly won't
// remove the handler.
VectorChangedEventHandler<object> handler = OnItemsSourceVectorChanged;
_itemsSourceCollectionChangeDisposable.Disposable = Disposable.Create(() =>
observableVector.VectorChanged -= handler
);
observableVector.VectorChanged += handler;
}
else if (_itemsSource is IObservableVector genericObservableVector)
{
VectorChangedEventHandler handler = OnItemsSourceVectorChanged;
_itemsSourceCollectionChangeDisposable.Disposable = Disposable.Create(() =>
genericObservableVector.UntypedVectorChanged -= handler
);
genericObservableVector.UntypedVectorChanged += handler;
}
else
{
throw new InvalidOperationException("Only IList- or IEnumerable-based ItemsSource is supported.");
_itemsSourceCollectionChangeDisposable.Disposable = null;
}
}

//TODO: Observe items source changes to raise VectorChanged
private void OnItemsSourceVectorChanged(object sender, IVectorChangedEventArgs args)
{
VectorChanged?.Invoke(this, args);
}

private void OnItemsSourceCollectionChanged(object sender, NotifyCollectionChangedEventArgs args)
{
VectorChanged?.Invoke(this, args.ToVectorChangedEventArgs());
}

private void ThrowIfItemsSourceSet()
Expand Down

0 comments on commit 2b4d4aa

Please sign in to comment.