@ -7,6 +7,7 @@ using System;
using System.Collections ;
using System.Collections.Generic ;
using System.Collections.Specialized ;
using System.Diagnostics.CodeAnalysis ;
using System.Linq ;
using Avalonia.Controls.Utils ;
@ -15,29 +16,30 @@ using Avalonia.Controls.Utils;
namespace Avalonia.Controls
{
/// <summary>
/// Represents a standardized view of the supported interactions between a given
/// <see cref="ItemsControl"/> or <see cref="ItemsRepeater"/> and its items .
/// Represents a standardized view of the supported interactions between a given ItemsSource
/// object and an <see cref="ItemsRepeater"/> control .
/// </summary>
public class ItemsSourceView < T > : INotifyCollectionChanged , IDisposable , IReadOnlyList < T >
/// <remarks>
/// Components written to work with ItemsRepeater should consume the
/// <see cref="ItemsRepeater.Items"/> via ItemsSourceView since this provides a normalized
/// view of the Items. That way, each component does not need to know if the source is an
/// IEnumerable, an IList, or something else.
/// </remarks>
public class ItemsSourceView : INotifyCollectionChanged , IDisposable
{
/// <summary>
/// Gets an empty <see cref="ItemsSourceView{T}"/>
/// Gets an empty <see cref="ItemsSourceView"/>
/// </summary>
public static ItemsSourceView < T > Empty { get ; } = new ItemsSourceView < T > ( Array . Empty < T > ( ) ) ;
public static ItemsSourceView Empty { get ; } = new ItemsSourceView ( Array . Empty < object > ( ) ) ;
private readonly IList _ inner ;
private protected readonly IList _ inner ;
private INotifyCollectionChanged ? _ notifyCollectionChanged ;
/// <summary>
/// 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 )
: this ( ( IEnumerable ) source )
{
}
private protected ItemsSourceView ( IEnumerable source )
public ItemsSourceView ( IEnumerable source )
{
source = source ? ? throw new ArgumentNullException ( nameof ( source ) ) ;
@ -45,13 +47,13 @@ namespace Avalonia.Controls
{
_ inner = list ;
}
else if ( source is IEnumerable < object? > enumerable )
else if ( source is IEnumerable < object > obj ectE numerable)
{
_ inner = new List < object? > ( enumerable ) ;
_ inner = new List < object > ( obj ectE numerable) ;
}
else
{
_ inner = new List < object? > ( source . Cast < object? > ( ) ) ;
_ inner = new List < object > ( source . Cast < object > ( ) ) ;
}
ListenToCollectionChanges ( ) ;
@ -75,7 +77,7 @@ namespace Avalonia.Controls
/// </summary>
/// <param name="index">The index.</param>
/// <returns>The item.</returns>
public T this [ int index ] = > GetAt ( index ) ;
public object? this [ int index ] = > GetAt ( index ) ;
/// <summary>
/// Occurs when the collection has changed to indicate the reason for the change and which items changed.
@ -96,13 +98,13 @@ namespace Avalonia.Controls
/// </summary>
/// <param name="index">The index.</param>
/// <returns>The item.</returns>
public T GetAt ( int index ) = > _ inner is IList < T > typed ? typed [ index ] : ( T ) _ inner [ index ] ;
public object? GetAt ( int index ) = > _ inner [ index ] ;
public int IndexOf ( object? item ) = > _ inner . IndexOf ( item ) ;
public static ItemsSourceView < T > GetOrCreate ( IEnumerable < T > ? items )
public static ItemsSourceView GetOrCreate ( IEnumerable ? items )
{
if ( items is ItemsSourceView < T > isv )
if ( items is ItemsSourceView isv )
{
return isv ;
}
@ -112,7 +114,7 @@ namespace Avalonia.Controls
}
else
{
return new ItemsSourceView < T > ( items ) ;
return new ItemsSourceView ( items ) ;
}
}
@ -142,11 +144,6 @@ namespace Avalonia.Controls
throw new NotImplementedException ( ) ;
}
public IEnumerator < T > GetEnumerator ( ) = > _ inner is IList < T > typed ?
typed . GetEnumerator ( ) : _ inner . Cast < T > ( ) . GetEnumerator ( ) ;
IEnumerator IEnumerable . GetEnumerator ( ) = > _ inner . GetEnumerator ( ) ;
internal void AddListener ( ICollectionChangedListener listener )
{
if ( _ inner is INotifyCollectionChanged incc )
@ -183,11 +180,61 @@ namespace Avalonia.Controls
}
}
public class ItemsSourceView : ItemsSourceView < object? >
public class ItemsSourceView < T > : ItemsSourceView , IReadOnlyList < T >
{
public ItemsSourceView ( IEnumerable source )
/// <summary>
/// Gets an empty <see cref="ItemsSourceView"/>
/// </summary>
public new static ItemsSourceView < T > Empty { get ; } = new ItemsSourceView < T > ( Array . Empty < T > ( ) ) ;
/// <summary>
/// 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 )
: base ( source )
{
}
private ItemsSourceView ( IEnumerable source )
: base ( source )
{
}
/// <summary>
/// Retrieves the item at the specified index.
/// </summary>
/// <param name="index">The index.</param>
/// <returns>The item.</returns>
#pragma warning disable CS8603
public new T this [ int index ] = > GetAt ( index ) ;
#pragma warning restore CS8603
/// <summary>
/// Retrieves the item at the specified index.
/// </summary>
/// <param name="index">The index.</param>
/// <returns>The item.</returns>
[return: MaybeNull]
public new T GetAt ( int index ) = > ( T ) _ inner [ index ] ;
public IEnumerator < T > GetEnumerator ( ) = > _ inner . Cast < T > ( ) . GetEnumerator ( ) ;
IEnumerator IEnumerable . GetEnumerator ( ) = > _ inner . GetEnumerator ( ) ;
public static new ItemsSourceView < T > GetOrCreate ( IEnumerable ? items )
{
if ( items is ItemsSourceView < T > isv )
{
return isv ;
}
else if ( items is null )
{
return Empty ;
}
else
{
return new ItemsSourceView < T > ( items ) ;
}
}
}
}