File: Items\SelectionList.cs
Web Access
Project: src\src\Controls\src\Core\Controls.Core.csproj (Microsoft.Maui.Controls)
#nullable disable
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
 
namespace Microsoft.Maui.Controls
{
	// Used by the SelectableItemsView to keep track of (and respond to changes in) the SelectedItems property
	internal class SelectionList : IList<object>
	{
		static readonly IList<object> s_empty = new List<object>(0);
		readonly SelectableItemsView _selectableItemsView;
		readonly IList<object> _internal;
		IList<object> _shadow;
		bool _externalChange;
 
		public SelectionList(SelectableItemsView selectableItemsView, IList<object> items = null)
		{
			_selectableItemsView = selectableItemsView ?? throw new ArgumentNullException(nameof(selectableItemsView));
			_internal = items ?? new List<object>();
			_shadow = Copy();
 
			if (items is INotifyCollectionChanged incc)
			{
				incc.CollectionChanged += OnCollectionChanged;
			}
		}
 
		public object this[int index] { get => _internal[index]; set => _internal[index] = value; }
 
		public int Count => _internal.Count;
 
		public bool IsReadOnly => false;
 
		public void Add(object item)
		{
			_externalChange = true;
			_internal.Add(item);
			_externalChange = false;
 
			_selectableItemsView.SelectedItemsPropertyChanged(_shadow, _internal);
			_shadow.Add(item);
		}
 
		public void Clear()
		{
			_externalChange = true;
			_internal.Clear();
			_externalChange = false;
 
			_selectableItemsView.SelectedItemsPropertyChanged(_shadow, s_empty);
			_shadow.Clear();
		}
 
		public bool Contains(object item)
		{
			return _internal.Contains(item);
		}
 
		public void CopyTo(object[] array, int arrayIndex)
		{
			_internal.CopyTo(array, arrayIndex);
		}
 
		public IEnumerator<object> GetEnumerator()
		{
			return _internal.GetEnumerator();
		}
 
		public int IndexOf(object item)
		{
			return _internal.IndexOf(item);
		}
 
		public void Insert(int index, object item)
		{
			_externalChange = true;
			_internal.Insert(index, item);
			_externalChange = false;
 
			_selectableItemsView.SelectedItemsPropertyChanged(_shadow, _internal);
			_shadow.Insert(index, item);
		}
 
		public bool Remove(object item)
		{
			_externalChange = true;
			var removed = _internal.Remove(item);
			_externalChange = false;
 
			if (removed)
			{
				_selectableItemsView.SelectedItemsPropertyChanged(_shadow, _internal);
				_shadow.Remove(item);
			}
 
			return removed;
		}
 
		public void RemoveAt(int index)
		{
			_externalChange = true;
			_internal.RemoveAt(index);
			_externalChange = false;
 
			_selectableItemsView.SelectedItemsPropertyChanged(_shadow, _internal);
			_shadow.RemoveAt(index);
		}
 
		IEnumerator IEnumerable.GetEnumerator()
		{
			return _internal.GetEnumerator();
		}
 
		List<object> Copy()
		{
			var items = new List<object>();
			for (int n = 0; n < _internal.Count; n++)
			{
				items.Add(_internal[n]);
			}
 
			return items;
		}
 
		void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs args)
		{
			if (_externalChange)
			{
				// If this change was initiated by a renderer or direct manipulation of ColllectionView.SelectedItems,
				// we don't need to send a selection change notification
				return;
			}
 
			// This change is coming from a bound viewmodel property
			// Emit a selection change notification, then bring the shadow copy up-to-date
			_selectableItemsView.SelectedItemsPropertyChanged(_shadow, _internal);
			_shadow = Copy();
		}
	}
}