Skip to content

Instantly share code, notes, and snippets.

@mfakane
Last active September 29, 2015 17:41
Show Gist options
  • Select an option

  • Save mfakane/8d665c29a9aa9ce81375 to your computer and use it in GitHub Desktop.

Select an option

Save mfakane/8d665c29a9aa9ce81375 to your computer and use it in GitHub Desktop.
M→VM 用コレクションラッパ
// 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