Skip to content

Instantly share code, notes, and snippets.

@JohanLarsson
Last active December 27, 2015 12:39
Show Gist options
  • Save JohanLarsson/7327483 to your computer and use it in GitHub Desktop.
Save JohanLarsson/7327483 to your computer and use it in GitHub Desktop.
public class ChildPropertyChangedEventArgs : PropertyChangedEventArgs
{
public ChildPropertyChangedEventArgs(object child, string propertyName)
: base(propertyName)
{
Child = child;
}
public ChildPropertyChangedEventArgs(object sender, PropertyChangedEventArgs e)
: base(e.PropertyName)
{
Child = sender;
}
//public ChildPropertyChangedEventArgs(EventPattern<PropertyChangedEventArgs> e)
// : base(e.EventArgs.PropertyName)
//{
// Child = e.Sender;
//}
public object Child { get; private set; }
}
public class OcNpcListener<T> : INotifyPropertyChanged where T : INotifyPropertyChanged
{
private readonly ObservableCollection<T> _collection;
private readonly string _propertyName;
private readonly Dictionary<T, int> _items = new Dictionary<T, int>(new ObjectIdentityComparer());
public OcNpcListener(ObservableCollection<T> collection, string propertyName = "")
{
_collection = collection;
_propertyName = propertyName ?? "";
AddRange(collection);
CollectionChangedEventManager.AddHandler(collection, CollectionChanged);
}
private void CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
AddRange(e.NewItems.Cast<T>());
break;
case NotifyCollectionChangedAction.Remove:
RemoveRange(e.OldItems.Cast<T>());
break;
case NotifyCollectionChangedAction.Replace:
AddRange(e.NewItems.Cast<T>());
RemoveRange(e.OldItems.Cast<T>());
break;
case NotifyCollectionChangedAction.Move:
break;
case NotifyCollectionChangedAction.Reset:
Reset();
break;
default:
throw new ArgumentOutOfRangeException();
}
}
private void AddRange(IEnumerable<T> newItems)
{
foreach (T item in newItems)
{
if (_items.ContainsKey(item))
{
_items[item]++;
}
else
{
_items.Add(item, 1);
PropertyChangedEventManager.AddHandler(item, ChildPropertyChanged, _propertyName);
}
}
}
private void RemoveRange(IEnumerable<T> oldItems)
{
foreach (T item in oldItems)
{
_items[item]--;
if (_items[item] == 0)
{
_items.Remove(item);
PropertyChangedEventManager.RemoveHandler(item, ChildPropertyChanged, _propertyName);
}
}
}
private void Reset()
{
foreach (T item in _items.Keys.ToList())
{
PropertyChangedEventManager.RemoveHandler(item, ChildPropertyChanged, _propertyName);
_items.Remove(item);
}
AddRange(_collection);
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void ChildPropertyChanged(object sender, PropertyChangedEventArgs e)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, new ChildPropertyChangedEventArgs(sender, e));
}
private class ObjectIdentityComparer : IEqualityComparer<T>
{
public bool Equals(T x, T y)
{
return object.ReferenceEquals(x, y);
}
public int GetHashCode(T obj)
{
return System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(obj);
}
}
}
public static class OcNpcListener
{
public static OcNpcListener<T> Create<T>(ObservableCollection<T> collection, string propertyName = "") where T : INotifyPropertyChanged
{
return new OcNpcListener<T>(collection, propertyName);
}
}
public class OcNpcListenerTests
{
[Test]
public void SimpleAddTest()
{
var fakeInpc = new FakeInpc();
var oc = new ObservableCollection<FakeInpc>();
var events = new List<FakeInpc>();
var listener = OcNpcListener.Create(oc);
listener.PropertyChanged += (sender, args) => events.Add((FakeInpc)((ChildPropertyChangedEventArgs)args).Child);
Assert.IsFalse(fakeInpc.HasHandler);
oc.Add(fakeInpc);
Assert.IsTrue(fakeInpc.HasHandler);
fakeInpc.Raise1();
Assert.AreEqual(fakeInpc, events.Single());
}
[Test]
public void SimpleAddPropertyChangedEventManagerTest()
{
var fakeInpc = new FakeInpc();
var oc = new ObservableCollection<FakeInpc>();
var events = new List<FakeInpc>();
var listener = OcNpcListener.Create(oc);
PropertyChangedEventManager.AddHandler(listener, (sender, args) => events.Add((FakeInpc)((ChildPropertyChangedEventArgs)args).Child), "");
Assert.IsFalse(fakeInpc.HasHandler);
oc.Add(fakeInpc);
Assert.IsTrue(fakeInpc.HasHandler);
fakeInpc.Raise1();
Assert.AreEqual(fakeInpc, events.Single());
}
[Test]
public void SimpleAddNamedTest()
{
var fakeInpc = new FakeInpc();
var oc = new ObservableCollection<FakeInpc>();
var events = new List<FakeInpc>();
var listener = OcNpcListener.Create(oc, "DummyProperty1");
listener.PropertyChanged += (sender, args) => events.Add((FakeInpc)((ChildPropertyChangedEventArgs)args).Child);
Assert.IsFalse(fakeInpc.HasHandler);
oc.Add(fakeInpc);
Assert.IsTrue(fakeInpc.HasHandler);
fakeInpc.Raise2();
Assert.IsFalse(events.Any());
fakeInpc.Raise1();
Assert.AreEqual(fakeInpc, events.Single());
}
[Test]
public void AddTwiceRemoveOnceTest()
{
var fakeInpc = new FakeInpc();
var oc = new ObservableCollection<FakeInpc>();
var events = new List<FakeInpc>();
var listener = OcNpcListener.Create(oc);//, (sender, args) => events.Add((FakeInpc)sender));
listener.PropertyChanged += (sender, args) => events.Add((FakeInpc)((ChildPropertyChangedEventArgs)args).Child);
Assert.IsFalse(fakeInpc.HasHandler);
oc.Add(fakeInpc);
oc.Add(fakeInpc);
Assert.IsTrue(fakeInpc.HasHandler);
fakeInpc.Raise1();
Assert.AreEqual(fakeInpc, events.Single());
oc.RemoveAt(1);
Assert.IsTrue(fakeInpc.HasHandler);
}
[Test]
public void RemoveTest()
{
var fakeInpc = new FakeInpc();
var oc = new ObservableCollection<FakeInpc>();
var events = new List<FakeInpc>();
var listener = OcNpcListener.Create(oc);//, (sender, args) => events.Add((FakeInpc)sender));
listener.PropertyChanged += (sender, args) => events.Add((FakeInpc)((ChildPropertyChangedEventArgs)args).Child);
Assert.IsFalse(fakeInpc.HasHandler);
oc.Add(fakeInpc);
Assert.IsTrue(fakeInpc.HasHandler);
fakeInpc.Raise1();
Assert.AreEqual(fakeInpc, events.Single());
oc.Remove(fakeInpc);
Assert.IsFalse(fakeInpc.HasHandler);
}
[Test]
public void ClearTest()
{
var fakeInpc = new FakeInpc();
var oc = new ObservableCollection<FakeInpc>();
var events = new List<FakeInpc>();
var listener = OcNpcListener.Create(oc);//, (sender, args) => events.Add((FakeInpc)sender));
listener.PropertyChanged += (sender, args) => events.Add((FakeInpc)((ChildPropertyChangedEventArgs)args).Child);
Assert.IsFalse(fakeInpc.HasHandler);
oc.Add(fakeInpc);
Assert.IsTrue(fakeInpc.HasHandler);
fakeInpc.Raise1();
Assert.AreEqual(fakeInpc, events.Single());
oc.Clear();
Assert.IsFalse(fakeInpc.HasHandler);
}
[Test]
public void ReplaceTest()
{
var fakeInpc1 = new FakeInpc();
var fakeInpc2 = new FakeInpc();
var oc = new ObservableCollection<FakeInpc>();
var events = new List<FakeInpc>();
var listener = OcNpcListener.Create(oc);//, (sender, args) => events.Add((FakeInpc)sender));
listener.PropertyChanged += (sender, args) => events.Add((FakeInpc)((ChildPropertyChangedEventArgs)args).Child);
Assert.IsFalse(fakeInpc1.HasHandler);
oc.Add(fakeInpc1);
Assert.IsTrue(fakeInpc1.HasHandler);
fakeInpc1.Raise1();
Assert.AreEqual(fakeInpc1, events.Single());
oc[0] = fakeInpc2;
Assert.IsFalse(fakeInpc1.HasHandler);
Assert.IsTrue(fakeInpc2.HasHandler);
}
}
public class FakeInpc : INotifyPropertyChanged, IEquatable<FakeInpc>
{
public int DummyProperty1 { get { return 1; } }
public int DummyProperty2 { get { return 2; } }
public void Raise1()
{
OnPropertyChanged("DummyProperty1");
}
public void Raise2()
{
OnPropertyChanged("DummyProperty2");
}
public bool HasHandler { get { return PropertyChanged != null; } }
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
public bool Equals(FakeInpc other)
{
return ReferenceEquals(this, other);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((FakeInpc)obj);
}
public override int GetHashCode()
{
return 3;
}
public static bool operator ==(FakeInpc left, FakeInpc right)
{
return Equals(left, right);
}
public static bool operator !=(FakeInpc left, FakeInpc right)
{
return !Equals(left, right);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment