Last active
September 29, 2015 17:41
-
-
Save mfakane/8d665c29a9aa9ce81375 to your computer and use it in GitHub Desktop.
M→VM 用コレクションラッパ
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // usage: | |
| // | |
| // Model のコレクションの変化を自動的に反映する ViewModel コレクションを作成 | |
| // ObservableCollectionWrapper.Create(modelCollection, (Model m) => new ViewModel(m)) | |
| // | |
| // 任意の ViewModel 側コレクションを Model コレクションに同期させる | |
| // ViewModel コレクションへの操作を Model 側に反映させることもできる (Caliburn.Micro での Conductor の Items に使用するなど) | |
| // ObservableCollectionWrapper.Create(modelCollection, viewModelCollection, (Model m) => new ViewModel(m), vm => vm.Model) | |
| using System; | |
| using System.Collections; | |
| using System.Collections.Generic; | |
| using System.Collections.ObjectModel; | |
| using System.Collections.Specialized; | |
| namespace Linearstar.Coah | |
| { | |
| public class ObservableCollectionWrapper<TInputCollection, TInput, TOutputCollection, TOutput> : IReadOnlyList<TOutput>, INotifyCollectionChanged, IDisposable | |
| where TInputCollection : IList<TInput>, INotifyCollectionChanged | |
| where TOutputCollection : IList<TOutput>, INotifyCollectionChanged | |
| { | |
| readonly Func<TInput, TOutput> converter; | |
| readonly Func<TOutput, TInput> reverseSelector; | |
| bool isUpdating; | |
| public TInputCollection InputCollection { get; } | |
| public TOutputCollection OutputCollection { get; } | |
| public event NotifyCollectionChangedEventHandler CollectionChanged | |
| { | |
| add | |
| { | |
| this.OutputCollection.CollectionChanged += value; | |
| } | |
| remove | |
| { | |
| this.OutputCollection.CollectionChanged -= value; | |
| } | |
| } | |
| public TOutput this[int index] => this.OutputCollection[index]; | |
| public int Count => this.OutputCollection.Count; | |
| public ObservableCollectionWrapper(TInputCollection inputCollection, TOutputCollection outputCollection, Func<TInput, TOutput> converter) | |
| { | |
| this.InputCollection = inputCollection; | |
| this.OutputCollection = outputCollection; | |
| this.converter = converter; | |
| foreach (var i in this.InputCollection) | |
| this.OutputCollection.Add(converter(i)); | |
| inputCollection.CollectionChanged += InputCollection_CollectionChanged; | |
| } | |
| public ObservableCollectionWrapper(TInputCollection inputCollection, TOutputCollection outputCollection, Func<TInput, TOutput> converter, Func<TOutput, TInput> reverseSelector) | |
| : this(inputCollection, outputCollection, converter) | |
| { | |
| this.reverseSelector = reverseSelector; | |
| outputCollection.CollectionChanged += OutputCollection_CollectionChanged; | |
| } | |
| void InputCollection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) | |
| { | |
| if (isUpdating) | |
| return; | |
| isUpdating = true; | |
| switch (e.Action) | |
| { | |
| case NotifyCollectionChangedAction.Add: | |
| foreach (TInput i in e.NewItems) | |
| this.OutputCollection.Add(converter(i)); | |
| break; | |
| case NotifyCollectionChangedAction.Move: | |
| var item = this.OutputCollection[e.OldStartingIndex]; | |
| this.OutputCollection.RemoveAt(e.OldStartingIndex); | |
| this.OutputCollection.Insert(e.NewStartingIndex - (e.NewStartingIndex > e.OldStartingIndex ? 1 : 0), item); | |
| break; | |
| case NotifyCollectionChangedAction.Remove: | |
| for (var i = e.OldItems.Count - 1; i >= 0; i--) | |
| this.OutputCollection.RemoveAt(e.OldStartingIndex + i); | |
| break; | |
| case NotifyCollectionChangedAction.Replace: | |
| for (int i = 0; i < e.OldItems.Count; i++) | |
| this.OutputCollection[i + e.OldStartingIndex] = converter((TInput)e.NewItems[i]); | |
| break; | |
| case NotifyCollectionChangedAction.Reset: | |
| this.OutputCollection.Clear(); | |
| foreach (var i in this.InputCollection) | |
| this.OutputCollection.Add(converter(i)); | |
| break; | |
| } | |
| isUpdating = false; | |
| } | |
| void OutputCollection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) | |
| { | |
| if (isUpdating) | |
| return; | |
| isUpdating = true; | |
| switch (e.Action) | |
| { | |
| case NotifyCollectionChangedAction.Add: | |
| foreach (TOutput i in e.NewItems) | |
| this.InputCollection.Add(reverseSelector(i)); | |
| break; | |
| case NotifyCollectionChangedAction.Move: | |
| var item = this.InputCollection[e.OldStartingIndex]; | |
| this.InputCollection.RemoveAt(e.OldStartingIndex); | |
| this.InputCollection.Insert(e.NewStartingIndex - (e.NewStartingIndex > e.OldStartingIndex ? 1 : 0), item); | |
| break; | |
| case NotifyCollectionChangedAction.Remove: | |
| for (var i = e.OldItems.Count - 1; i >= 0; i--) | |
| this.InputCollection.RemoveAt(e.OldStartingIndex + i); | |
| break; | |
| case NotifyCollectionChangedAction.Replace: | |
| for (int i = 0; i < e.OldItems.Count; i++) | |
| this.InputCollection[i + e.OldStartingIndex] = reverseSelector((TOutput)e.NewItems[i]); | |
| break; | |
| case NotifyCollectionChangedAction.Reset: | |
| this.InputCollection.Clear(); | |
| foreach (var i in this.OutputCollection) | |
| this.InputCollection.Add(reverseSelector(i)); | |
| break; | |
| } | |
| isUpdating = false; | |
| } | |
| public IEnumerator<TOutput> GetEnumerator() => this.OutputCollection.GetEnumerator(); | |
| IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); | |
| public void Dispose() | |
| { | |
| this.InputCollection.CollectionChanged -= InputCollection_CollectionChanged; | |
| this.OutputCollection.CollectionChanged -= OutputCollection_CollectionChanged; | |
| } | |
| } | |
| public static class ObservableCollectionWrapper | |
| { | |
| public static ObservableCollectionWrapper<TCollection, TInput, ObservableCollection<TOutput>, TOutput> Create<TCollection, TInput, TOutput>(TCollection collection, Func<TInput, TOutput> converter) | |
| where TCollection : IList<TInput>, INotifyCollectionChanged => | |
| new ObservableCollectionWrapper<TCollection, TInput, ObservableCollection<TOutput>, TOutput>(collection, new ObservableCollection<TOutput>(), converter); | |
| public static ObservableCollectionWrapper<TInputCollection, TInput, TOutputCollection, TOutput> Create<TInputCollection, TInput, TOutputCollection, TOutput>(TInputCollection inputCollection, TOutputCollection outputCollection, Func<TInput, TOutput> converter, Func<TOutput, TInput> reverseSelector) | |
| where TInputCollection : IList<TInput>, INotifyCollectionChanged | |
| where TOutputCollection : IList<TOutput>, INotifyCollectionChanged => | |
| new ObservableCollectionWrapper<TInputCollection, TInput, TOutputCollection, TOutput>(inputCollection, outputCollection, converter, reverseSelector); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment