Browse Source

Make ItemsSourceView ctors private.

So in future we could have different `ItemSourceView` concrete implementations that e.g. have an inner `IReadOnlyList<T>` in order to address #8764.
pull/9677/head
Steven Kirk 4 years ago
parent
commit
518391b37d
  1. 2
      src/Avalonia.Controls/ItemsControl.cs
  2. 71
      src/Avalonia.Controls/ItemsSourceView.cs
  3. 2
      src/Avalonia.Controls/Repeater/ItemsRepeater.cs
  4. 2
      src/Avalonia.Controls/Selection/SelectionNodeBase.cs
  5. 11
      tests/Avalonia.Controls.UnitTests/ItemsSourceViewTests.cs

2
src/Avalonia.Controls/ItemsControl.cs

@ -96,7 +96,7 @@ namespace Avalonia.Controls
/// </summary>
public ItemsControl()
{
_itemsView = new(_items);
_itemsView = ItemsSourceView.GetOrCreate(_items);
_itemsView.PostCollectionChanged += ItemsCollectionChanged;
UpdatePseudoClasses(0);
}

71
src/Avalonia.Controls/ItemsSourceView.cs

@ -34,7 +34,7 @@ namespace Avalonia.Controls
/// Initializes a new instance of the ItemsSourceView class for the specified data source.
/// </summary>
/// <param name="source">The data source.</param>
public ItemsSourceView(IEnumerable source)
private protected ItemsSourceView(IEnumerable source)
{
_inner = source switch
{
@ -166,6 +166,48 @@ namespace Avalonia.Controls
};
}
/// <summary>
/// Gets or creates an <see cref="ItemsSourceView{T}"/> for the specified enumerable.
/// </summary>
/// <param name="items">The enumerable.</param>
/// <remarks>
/// This method handles the following three cases:
/// - If <paramref name="items"/> is null, returns <see cref="Empty"/>
/// - If <paramref name="items"/> is an <see cref="ItemsSourceView"/> returns the existing
/// <see cref="ItemsSourceView"/>
/// - Otherwise creates a new <see cref="ItemsSourceView"/>
/// </remarks>
public static ItemsSourceView<T> GetOrCreate<T>(IEnumerable? items)
{
return items switch
{
ItemsSourceView<T> isv => isv,
null => ItemsSourceView<T>.Empty,
_ => new ItemsSourceView<T>(items)
};
}
/// <summary>
/// Gets or creates an <see cref="ItemsSourceView{T}"/> for the specified enumerable.
/// </summary>
/// <param name="items">The enumerable.</param>
/// <remarks>
/// This method handles the following three cases:
/// - If <paramref name="items"/> is null, returns <see cref="Empty"/>
/// - If <paramref name="items"/> is an <see cref="ItemsSourceView"/> returns the existing
/// <see cref="ItemsSourceView"/>
/// - Otherwise creates a new <see cref="ItemsSourceView"/>
/// </remarks>
public static ItemsSourceView<T> GetOrCreate<T>(IEnumerable<T>? items)
{
return items switch
{
ItemsSourceView<T> isv => isv,
null => ItemsSourceView<T>.Empty,
_ => new ItemsSourceView<T>(items)
};
}
public IEnumerator<object?> GetEnumerator()
{
static IEnumerator<object> EnumerateItems(IList list)
@ -210,7 +252,7 @@ namespace Avalonia.Controls
internal string KeyFromIndex(int index) => throw new NotImplementedException();
}
public class ItemsSourceView<T> : ItemsSourceView, IReadOnlyList<T>
public sealed class ItemsSourceView<T> : ItemsSourceView, IReadOnlyList<T>
{
/// <summary>
/// Gets an empty <see cref="ItemsSourceView"/>
@ -221,12 +263,12 @@ namespace Avalonia.Controls
/// Initializes a new instance of the ItemsSourceView class for the specified data source.
/// </summary>
/// <param name="source">The data source.</param>
public ItemsSourceView(IEnumerable<T> source)
internal ItemsSourceView(IEnumerable<T> source)
: base(source)
{
}
private ItemsSourceView(IEnumerable source)
internal ItemsSourceView(IEnumerable source)
: base(source)
{
}
@ -263,26 +305,5 @@ namespace Avalonia.Controls
}
IEnumerator IEnumerable.GetEnumerator() => Inner.GetEnumerator();
/// <summary>
/// Gets or creates an <see cref="ItemsSourceView{T}"/> for the specified enumerable.
/// </summary>
/// <param name="items">The enumerable.</param>
/// <remarks>
/// This method handles the following three cases:
/// - If <paramref name="items"/> is null, returns <see cref="Empty"/>
/// - If <paramref name="items"/> is an <see cref="ItemsSourceView"/> returns the existing
/// <see cref="ItemsSourceView"/>
/// - Otherwise creates a new <see cref="ItemsSourceView"/>
/// </remarks>
public static new ItemsSourceView<T> GetOrCreate(IEnumerable? items)
{
return items switch
{
ItemsSourceView<T> isv => isv,
null => Empty,
_ => new ItemsSourceView<T>(items)
};
}
}
}

2
src/Avalonia.Controls/Repeater/ItemsRepeater.cs

@ -442,7 +442,7 @@ namespace Avalonia.Controls
var newDataSource = newEnumerable as ItemsSourceView;
if (newEnumerable != null && newDataSource == null)
{
newDataSource = new ItemsSourceView(newEnumerable);
newDataSource = ItemsSourceView.GetOrCreate(newEnumerable);
}
OnDataSourcePropertyChanged(ItemsSourceView, newDataSource);

2
src/Avalonia.Controls/Selection/SelectionNodeBase.cs

@ -24,7 +24,7 @@ namespace Avalonia.Controls.Selection
if (ItemsView?.Inner is INotifyCollectionChanged inccOld)
CollectionChangedEventManager.Instance.RemoveListener(inccOld, this);
_source = value;
ItemsView = value is object ? ItemsSourceView<T>.GetOrCreate(value) : null;
ItemsView = value is object ? ItemsSourceView.GetOrCreate<T>(value) : null;
if (ItemsView?.Inner is INotifyCollectionChanged inccNew)
CollectionChangedEventManager.Instance.AddListener(inccNew, this);
}

11
tests/Avalonia.Controls.UnitTests/ItemsSourceViewTests.cs

@ -15,7 +15,7 @@ namespace Avalonia.Controls.UnitTests
public void Only_Subscribes_To_Source_CollectionChanged_When_CollectionChanged_Subscribed()
{
var source = new AvaloniaList<string>();
var target = new ItemsSourceView<string>(source);
var target = ItemsSourceView.GetOrCreate(source);
var debug = (INotifyCollectionChangedDebug)source;
Assert.Null(debug.GetCollectionChangedSubscribers());
@ -31,18 +31,11 @@ namespace Avalonia.Controls.UnitTests
Assert.Null(debug.GetCollectionChangedSubscribers());
}
[Fact]
public void Cannot_Wrap_An_ItemsSourceView_In_Another()
{
var source = new ItemsSourceView<string>(new string[0]);
Assert.Throws<ArgumentException>(() => new ItemsSourceView<string>(source));
}
[Fact]
public void Cannot_Create_ItemsSourceView_With_Collection_That_Implements_INCC_But_Not_List()
{
var source = new InvalidCollection();
Assert.Throws<ArgumentException>(() => new ItemsSourceView<string>(source));
Assert.Throws<ArgumentException>(() => ItemsSourceView.GetOrCreate(source));
}
private class InvalidCollection : INotifyCollectionChanged, IEnumerable<string>

Loading…
Cancel
Save