public static ReadOnlyDispatcherCollection <TViewModel> CreateReadOnlyDispatcherCollection <TModel, TViewModel>( [NotNull] IList <TModel> source, [NotNull] Func <TModel, TViewModel> converter, [NotNull] Dispatcher dispatcher) { if (source == null) { throw new ArgumentNullException(nameof(source)); } if (converter == null) { throw new ArgumentNullException(nameof(converter)); } if (dispatcher == null) { throw new ArgumentNullException(nameof(dispatcher)); } if (!(source is INotifyCollectionChanged sourceAsNotifyCollection)) { throw new ArgumentException("sourceがINotifyCollectionChangedを実装していません"); } var initCollection = new ObservableCollection <TViewModel>(); foreach (var model in source) { initCollection.Add(converter(model)); } var target = new DispatcherCollection <TViewModel>(initCollection, dispatcher); var result = new ReadOnlyDispatcherCollection <TViewModel>(target); var collectionChangedListener = new CollectionChangedEventListener(sourceAsNotifyCollection); result.EventListeners.Add(collectionChangedListener); collectionChangedListener.RegisterHandler((sender, e) => { if (e == null) { throw new ArgumentNullException(nameof(e)); } switch (e.Action) { case NotifyCollectionChangedAction.Add: var vm = converter((TModel)e.NewItems?[0]); InvokeOnDispatcher(() => target.Insert(e.NewStartingIndex, vm), dispatcher); break; case NotifyCollectionChangedAction.Move: InvokeOnDispatcher(() => target.Move(e.OldStartingIndex, e.NewStartingIndex), dispatcher); break; case NotifyCollectionChangedAction.Remove: if (typeof(IDisposable).IsAssignableFrom(typeof(TViewModel))) { ((IDisposable)target[e.OldStartingIndex])?.Dispose(); } InvokeOnDispatcher(() => target.RemoveAt(e.OldStartingIndex), dispatcher); break; case NotifyCollectionChangedAction.Replace: if (typeof(IDisposable).IsAssignableFrom(typeof(TViewModel))) { ((IDisposable)target[e.NewStartingIndex])?.Dispose(); } var replaceVm = converter((TModel)e.NewItems?[0]); InvokeOnDispatcher(() => target[e.NewStartingIndex] = replaceVm, dispatcher); break; case NotifyCollectionChangedAction.Reset: if (typeof(IDisposable).IsAssignableFrom(typeof(TViewModel))) { foreach (var item in target.OfType <IDisposable>()) { item.Dispose(); } } InvokeOnDispatcher(target.Clear, dispatcher); break; default: throw new ArgumentException(); } }); return(result); }
public static ReadOnlyDispatcherCollection <TViewModel> CreateReadOnlyDispatcherCollection <TModel, TViewModel>(IList <TModel> source, Func <TModel, TViewModel> converter, Dispatcher dispatcher) { if (source == null) { throw new ArgumentNullException("source"); } var sourceAsNotifyCollection = source as INotifyCollectionChanged; if (sourceAsNotifyCollection == null) { throw new ArgumentException("sourceがINotifyCollectionChangedを実装していません"); } var initCollection = new ObservableCollection <TViewModel>(); var internalLock = new object(); lock (internalLock) { var target = new DispatcherCollection <TViewModel>(initCollection, dispatcher); var result = new ReadOnlyDispatcherCollection <TViewModel>(target); var collectionChangedListener = new CollectionChangedEventListener(sourceAsNotifyCollection); result.EventListeners.Add(collectionChangedListener); collectionChangedListener.RegisterHandler((sender, e) => { lock (internalLock) { switch (e.Action) { case NotifyCollectionChangedAction.Add: target.Insert(e.NewStartingIndex, converter((TModel)e.NewItems[0])); break; case NotifyCollectionChangedAction.Move: target.Move(e.OldStartingIndex, e.NewStartingIndex); break; case NotifyCollectionChangedAction.Remove: if (typeof(IDisposable).IsAssignableFrom(typeof(TViewModel))) { ((IDisposable)target[e.OldStartingIndex]).Dispose(); } target.RemoveAt(e.OldStartingIndex); break; case NotifyCollectionChangedAction.Replace: if (typeof(IDisposable).IsAssignableFrom(typeof(TViewModel))) { ((IDisposable)target[e.NewStartingIndex]).Dispose(); } target[e.NewStartingIndex] = converter((TModel)e.NewItems[0]); break; case NotifyCollectionChangedAction.Reset: if (typeof(IDisposable).IsAssignableFrom(typeof(TViewModel))) { foreach (IDisposable item in target) { item.Dispose(); } } target.Clear(); break; default: throw new ArgumentException(); } } }); foreach (var model in source) { initCollection.Add(converter(model)); } return(result); } }