Assuming that the ItemsSource is an ObservableCollection, my solution was to implement a ReverseObservableCollection:
public class ReverseObservableCollection<T> : IReadOnlyList<T>, INotifyCollectionChanged, INotifyPropertyChanged
{
#region Private fields
private readonly ObservableCollection<T> _observableCollection;
#endregion Private fields
#region Constructor
public ReverseObservableCollection(ObservableCollection<T> observableCollection)
{
_observableCollection = observableCollection;
observableCollection.CollectionChanged += ObservableCollection_CollectionChanged;
}
#endregion
#region Event handlers
private void ObservableCollection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (new[] { NotifyCollectionChangedAction.Add, NotifyCollectionChangedAction.Remove, NotifyCollectionChangedAction.Reset }.Contains(e.Action))
{
OnPropertyChanged(nameof(Count));
}
OnPropertyChanged(Binding.IndexerName); // ObservableCollection does this to improve WPF performance.
var newItems = Reverse(e.NewItems);
var oldItems = Reverse(e.OldItems);
int newStartingIndex = e.NewItems != null ? _observableCollection.Count - e.NewStartingIndex - e.NewItems.Count : -1;
//int oldCount = _observableCollection.Count - (e.NewItems?.Count ?? 0) + (e.OldItems?.Count ?? 0);
//int oldStartingIndex = e.OldItems != null ? oldCount - e.OldStartingIndex - e.OldItems.Count : -1;
int oldStartingIndex = e.OldItems != null ? _observableCollection.Count - e.OldStartingIndex - (e.NewItems?.Count ?? 0) : -1;
var eventArgs = e.Action switch
{
NotifyCollectionChangedAction.Add => new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, newItems, newStartingIndex),
NotifyCollectionChangedAction.Remove => new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, oldItems, oldStartingIndex),
NotifyCollectionChangedAction.Replace => new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, newItems, oldItems, oldStartingIndex),
NotifyCollectionChangedAction.Move => new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Move, oldItems, newStartingIndex, oldStartingIndex),
NotifyCollectionChangedAction.Reset => new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset),
_ => throw new ArgumentException("Unexpected Action", nameof(e)),
};
OnCollectionChanged(eventArgs);
}
#endregion
#region IReadOnlyList<T> implementation
public T this[int index] => _observableCollection[_observableCollection.Count - index - 1];
public int Count => _observableCollection.Count;
public IEnumerator<T> GetEnumerator()
{
for (int i = _observableCollection.Count - 1; i >= 0; --i)
{
yield return _observableCollection[i];
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
#region INotifyCollectionChanged implementation
public event NotifyCollectionChangedEventHandler? CollectionChanged;
private void OnCollectionChanged(NotifyCollectionChangedEventArgs args)
{
CollectionChanged?.Invoke(this, args);
}
#endregion
#region INotifyPropertyChanged implementation
public event PropertyChangedEventHandler? PropertyChanged;
private void OnPropertyChanged(string? propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
#region Private methods
private IList? Reverse(IList? list)
{
if (list == null) return null;
object[] result = new object[list.Count];
for (int i = 0; i < list.Count; ++i)
{
result[i] = list[list.Count - i - 1];
}
return result;
}
#endregion
}
Then, you just add a new property to the ViewModel and bind to it:
public class ViewModel
{
// Your old ItemsSource:
public ObservableCollection<string> Collection { get; } = new ObservableCollection<string>();
// New ItemsSource:
private ReverseObservableCollection<string>? _reverseCollection = null;
public ReverseObservableCollection<string> ReverseCollection => _reverseCollection ??= new ReverseObservableCollection<string>(Collection);
}