// This source file is adapted from the WinUI project. // (https://github.com/microsoft/microsoft-ui-xaml) // // Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation. using System; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.Linq; namespace Avalonia.Controls { /// /// Represents a standardized view of the supported interactions between a given ItemsSource /// object and an control. /// /// /// Components written to work with ItemsRepeater should consume the /// 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. /// public class ItemsSourceView : INotifyCollectionChanged, IDisposable { private readonly IList _inner; private INotifyCollectionChanged _notifyCollectionChanged; private int _cachedSize = -1; /// /// Initializes a new instance of the ItemsSourceView class for the specified data source. /// /// The data source. public ItemsSourceView(IEnumerable source) { Contract.Requires(source != null); if (source is IList list) { _inner = list; } else if (source is IEnumerable objectEnumerable) { _inner = new List(objectEnumerable); } else { _inner = new List(source.Cast()); } ListenToCollectionChanges(); } /// /// Gets the number of items in the collection. /// public int Count { get { if (_cachedSize == -1) { _cachedSize = _inner.Count; } return _cachedSize; } } /// /// Gets a value that indicates whether the items source can provide a unique key for each item. /// /// /// TODO: Not yet implemented in Avalonia. /// public bool HasKeyIndexMapping => false; /// /// Occurs when the collection has changed to indicate the reason for the change and which items changed. /// public event NotifyCollectionChangedEventHandler CollectionChanged; /// public void Dispose() { if (_notifyCollectionChanged != null) { _notifyCollectionChanged.CollectionChanged -= OnCollectionChanged; } } /// /// Retrieves the item at the specified index. /// /// The index. /// the item. public object GetAt(int index) => _inner[index]; public int IndexOf(object item) => _inner.IndexOf(item); /// /// Retrieves the index of the item that has the specified unique identifier (key). /// /// The index. /// The key /// /// TODO: Not yet implemented in Avalonia. /// public string KeyFromIndex(int index) { throw new NotImplementedException(); } /// /// Retrieves the unique identifier (key) for the item at the specified index. /// /// The key. /// The index. /// /// TODO: Not yet implemented in Avalonia. /// public int IndexFromKey(string key) { throw new NotImplementedException(); } protected void OnItemsSourceChanged(NotifyCollectionChangedEventArgs args) { _cachedSize = _inner.Count; CollectionChanged?.Invoke(this, args); } private void ListenToCollectionChanges() { if (_inner is INotifyCollectionChanged incc) { incc.CollectionChanged += OnCollectionChanged; _notifyCollectionChanged = incc; } } private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { OnItemsSourceChanged(e); } } }