CSharp · Console
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Runtime.CompilerServices;
public class EventAggregator
{
public static EventAggregator<TSource, TAccumulate> Create<TSource, TAccumulate>(
INotifyCollectionChanged collection,
TAccumulate seed,
Func<TAccumulate, TSource, TAccumulate> execute,
Func<TAccumulate, TSource, TAccumulate> undo,
Func<TAccumulate, bool> validate = null,
ISet<string> propertySet = null
) where TSource : class, INotifyPropertyChanging, INotifyPropertyChanged =>
new EventAggregator<TSource, TAccumulate>(collection, seed, execute, undo, validate, propertySet);
}
public class EventAggregator<TSource, TAccumulate> where TSource : class, INotifyPropertyChanging, INotifyPropertyChanged
{
public TAccumulate Value { get; private set; }
TAccumulate seed;
Func<TAccumulate, TSource, TAccumulate> execute;
Func<TAccumulate, TSource, TAccumulate> undo;
Func<TAccumulate, bool> validate;
ISet<string> propertySet;
public EventAggregator(
INotifyCollectionChanged collection,
TAccumulate seed,
Func<TAccumulate, TSource, TAccumulate> execute,
Func<TAccumulate, TSource, TAccumulate> undo,
Func<TAccumulate, bool> validate = null,
ISet<string> propertySet = null
)
{
collection.CollectionChanged += this.CollectionChanged;
this.Value = seed;
this.seed = seed;
this.execute = execute;
this.undo = undo;
this.validate = validate;
this.propertySet = propertySet;
}
void AddItems(IList items)
{
foreach (TSource item in items) {
var tempValue = execute(Value, item);
if (validate?.Invoke(tempValue) == false) {
throw new Exception("AddItems validation error");
}
Value = tempValue;
item.PropertyChanging += PropertyChanging;
item.PropertyChanged += PropertyChanged;
}
}
void RemoveItems(IList items)
{
foreach (TSource item in items) {
var tempValue = undo(Value, item);
if (validate?.Invoke(tempValue) == false) {
throw new Exception("RemoveItems validation error");
}
Value = tempValue;
item.PropertyChanged -= PropertyChanged;
item.PropertyChanging -= PropertyChanging;
}
}
void CollectionChanged(object sender, NotifyCollectionChangedEventArgs args)
{
switch (args.Action) {
case NotifyCollectionChangedAction.Add:
AddItems(args.NewItems);
break;
case NotifyCollectionChangedAction.Move:
break;
case NotifyCollectionChangedAction.Remove:
RemoveItems(args.OldItems);
break;
case NotifyCollectionChangedAction.Replace:
RemoveItems(args.OldItems);
AddItems(args.NewItems);
break;
case NotifyCollectionChangedAction.Reset:
Value = seed;
break;
default:
throw new NotImplementedException();
}
}
void PropertyChanging(object sender, PropertyChangingEventArgs e)
{
if (propertySet?.Contains(e.PropertyName) != true && propertySet != null) {
return;
}
var item = sender as TSource;
if (item == null) {
throw new ArgumentException("sender");
}
Value = undo(Value, item);
}
void PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (propertySet?.Contains(e.PropertyName) != true && propertySet != null) {
return;
}
var item = sender as TSource;
if (item == null) {
throw new ArgumentException("sender");
}
var tempValue = execute(Value, item);
if (validate?.Invoke(tempValue) == false) {
throw new Exception("PropertyChanged validation error");
}
Value = tempValue;
}
}
public class Item : INotifyPropertyChanging, INotifyPropertyChanged
{
private int h;
public int H {
get => this.h;
set
{
if (value != this.h) {
NotifyPropertyChanging();
this.h = value;
NotifyPropertyChanged();
}
}
}
public event PropertyChangingEventHandler PropertyChanging;
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanging([CallerMemberName] String propertyName = "")
{
PropertyChanging?.Invoke(this, new PropertyChangingEventArgs(propertyName));
}
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public override string ToString()
{
return $"Item {{ H = {this.H} }}";
}
}
public class Program
{
public static void Main()
{
display("Init collection: ");
var coll = new ObservableCollection<Item>();
var sumAggr = EventAggregator.Create(
coll,
seed: 0,
execute: (int accum, Item item) => accum + item.H,
undo: (int accum, Item item) => accum - item.H,
validate: accum => accum < 5,
propertySet: new HashSet<string>() { nameof(Item.H) }
);
display($"Value: {sumAggr.Value}");
var logAggr = EventAggregator.Create(
coll,
seed: new List<string>(),
execute: (List<string> accum, Item item) => { accum.Add($"Added item: {item}"); return accum; },
undo: (List<string> accum, Item item) => { accum.Add($"Removed item: {item}"); return accum; }
);
display("Add item: ");
var item0 = new Item() { H = 2 };
coll.Add(item0);
display(coll);
display($"Value: {sumAggr.Value}");
display("Add item: ");
coll.Add(new Item() { H = 1 });
display(coll);
display($"Value: {sumAggr.Value}");
display("Change item: ");
item0.H = -1;
display(coll);
display($"Value: {sumAggr.Value}");
display("Remove item: ");
coll.Remove(item0);
display(coll);
display($"Value: {sumAggr.Value}");
display("Log: ");
display(logAggr.Value);
display("Add item: ");
coll.Add(new Item() { H = 10 });
display(coll);
display($"Value: {sumAggr.Value}");
}
public static void display(string str)
{
Console.WriteLine(str);
}
public static void display(IEnumerable list)
{
FiddleHelper.WriteTable(list);
}
}