M
To use the proxy section Collection<T> You'll still have to adjust the model. Your collection of books should run the interface. IList<Book>e.g.:public virtual List<Book> Books { get; set; } = new List<Book>();
No further changes to the model will be made. In order for your UI to receive notifications when changing the contents of the book collection - removal, delivery, etc., we need to use the interface. INotifyCollectionChanged♪ It is not trivial, but fortunately, there is one example of implementation in the standard library - at least this is a class. ObservableCollection<T>: https://referencesource.microsoft.com/#system/compmod/system/collections/objectmodel/observablecollection.cs As can be seen in the reference code, the class is inherited from the proxy-collection. Collection<T>but for unknown reasons, the developers hid the proxy from us.Well, I just copied the implementation. ObservableCollection<T> and has written in lieu of existing designers:public class ProxyObservableCollection<T> : Collection<T>, INotifyCollectionChanged, INotifyPropertyChanged
{
public ProxyObservableCollection() : base() { }
public ProxyObservableCollection(IEnumerable<T> collection)
{
if (collection == null)
throw new ArgumentNullException("collection");
CopyFrom(collection);
}
public ProxyObservableCollection(IList<T> list) : base(list) { }
Of particular interest here is the last designer-- it's (as opposed to the replicator) and creates proxy for the transferable designer. IList<T>♪You can use it now. Add to VM:ProxyObservableCollection<Book> _booksOfCurrentAuthor;
public ProxyObservableCollection<Book> BooksOfCurrentAuthor
{
get
{
return _booksOfCurrentAuthor;
}
private set
{
_booksOfCurrentAuthor = value;
RaisePropertyChanged(nameof(BooksOfCurrentAuthor));
}
}
We'll add to our properties. CurrentAuthor:public Author CurrentAuthor
{
get
{
...
}
set
{
_author = value;
BooksOfCurrentAuthor = new ProxyObservableCollection(CurrentAuthor.Books);
RaisePropertyChanged("CurrentAuthor");
}
Now there's no copy of the collection, but at the same time, all changes in the collection of books will alert UI: <ListView
...
ItemsSource="{Binding Path=BooksOfCurrentAuthor}"
Just in case I bring the full code. ProxyObservableCollection<T>:using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
namespace MyNamespace
{
public class ProxyObservableCollection<T> : Collection<T>, INotifyCollectionChanged, INotifyPropertyChanged
{
public ProxyObservableCollection() : base() { }
public ProxyObservableCollection(IEnumerable<T> collection)
{
if (collection == null)
throw new ArgumentNullException("collection");
CopyFrom(collection);
}
public ProxyObservableCollection(IList<T> list) : base(list) { }
private void CopyFrom(IEnumerable<T> collection)
{
IList<T> items = Items;
if (collection != null && items != null)
{
using (IEnumerator<T> enumerator = collection.GetEnumerator())
{
while (enumerator.MoveNext())
{
items.Add(enumerator.Current);
}
}
}
}
public void Move(int oldIndex, int newIndex)
{
MoveItem(oldIndex, newIndex);
}
event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged
{
add
{
PropertyChanged += value;
}
remove
{
PropertyChanged -= value;
}
}
public virtual event NotifyCollectionChangedEventHandler CollectionChanged;
protected override void ClearItems()
{
CheckReentrancy();
base.ClearItems();
OnPropertyChanged(CountString);
OnPropertyChanged(IndexerName);
OnCollectionReset();
}
protected override void RemoveItem(int index)
{
CheckReentrancy();
T removedItem = this[index];
base.RemoveItem(index);
OnPropertyChanged(CountString);
OnPropertyChanged(IndexerName);
OnCollectionChanged(NotifyCollectionChangedAction.Remove, removedItem, index);
}
protected override void InsertItem(int index, T item)
{
CheckReentrancy();
base.InsertItem(index, item);
OnPropertyChanged(CountString);
OnPropertyChanged(IndexerName);
OnCollectionChanged(NotifyCollectionChangedAction.Add, item, index);
}
protected override void SetItem(int index, T item)
{
CheckReentrancy();
T originalItem = this[index];
base.SetItem(index, item);
OnPropertyChanged(IndexerName);
OnCollectionChanged(NotifyCollectionChangedAction.Replace, originalItem, item, index);
}
protected virtual void MoveItem(int oldIndex, int newIndex)
{
CheckReentrancy();
T removedItem = this[oldIndex];
base.RemoveItem(oldIndex);
base.InsertItem(newIndex, removedItem);
OnPropertyChanged(IndexerName);
OnCollectionChanged(NotifyCollectionChangedAction.Move, removedItem, newIndex, oldIndex);
}
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
=> PropertyChanged?.Invoke(this, e);
protected virtual event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (CollectionChanged != null)
{
using (BlockReentrancy())
{
CollectionChanged(this, e);
}
}
}
protected IDisposable BlockReentrancy()
{
_monitor.Enter();
return _monitor;
}
protected void CheckReentrancy()
{
if (_monitor.Busy)
{
if ((CollectionChanged != null) && (CollectionChanged.GetInvocationList().Length > 1))
throw new InvalidOperationException();
}
}
private void OnPropertyChanged(string propertyName)
=> OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
private void OnCollectionChanged(NotifyCollectionChangedAction action, object item, int index)
=> OnCollectionChanged(new NotifyCollectionChangedEventArgs(action, item, index));
private void OnCollectionChanged(NotifyCollectionChangedAction action, object item, int index, int oldIndex)
=> OnCollectionChanged(new NotifyCollectionChangedEventArgs(action, item, index, oldIndex));
private void OnCollectionChanged(NotifyCollectionChangedAction action, object oldItem, object newItem, int index)
=> OnCollectionChanged(new NotifyCollectionChangedEventArgs(action, newItem, oldItem, index));
private void OnCollectionReset()
=> OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
private class SimpleMonitor : IDisposable
{
public void Enter()
{
++_busyCount;
}
public void Dispose()
{
--_busyCount;
}
public bool Busy { get { return _busyCount > 0; } }
int _busyCount;
}
private const string CountString = "Count";
private const string IndexerName = "Item[]";
private SimpleMonitor _monitor = new SimpleMonitor();
}
}