Event Accumulator class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace EventAccumulatorUtils
{
/// <summary>
/// Helper that allows to accumulate and temporary ignore events (e.g. PropertyChanged, Dirty,...)
/// Has 2 main methods: BeginAccumulate, BeginIgnore
/// </summary>
public class EventAccumulator
{
public EventAccumulator()
{
}
public EventAccumulator(Action<EventAccumulator> onInvoke)
{
OnInvoke(onInvoke);
}
Action<EventAccumulator> OnAccumulatorFlush;
public IDisposable BeginAccumulate()
{
return new DisposeAction(a => {
Interlocked.Increment(ref accumulate_Stack);
a.OnDispose = () => {
Interlocked.Decrement(ref accumulate_Stack);
if (accumulate_Stack == 0)
InvokeOrAccumulateOrIgnore(false);
};
});
}
public IDisposable BeginIgnore()
{
return new DisposeAction(a => {
Interlocked.Increment(ref ignore_Stack);
a.OnDispose = () => {
Interlocked.Decrement(ref ignore_Stack);
};
});
}
public object AccumulatedData = null;
int accumulated_Count = 0;
public int Accumulated_Count => accumulated_Count;
int accumulate_Stack = 0;
int ignore_Stack = 0;
public void Reset()
{
accumulated_Count = 0;
accumulate_Stack = 0;
ignore_Stack = 0;
AccumulatedData = null;
}
public bool ShouldIgnore()
{
return ignore_Stack > 0;
}
/// <summary>
/// Configure invocation
/// </summary>
/// <param name="onAccumulatorFlush"></param>
public void OnInvoke(Action<EventAccumulator> onAccumulatorFlush)
{
OnAccumulatorFlush = onAccumulatorFlush;
}
/// <summary>
/// Depending on the state (BeginAccumulate, BeginIgnore) it can either:
/// Do nothing (if BeginIgnored started),
/// or Append to accumulator (if BeginAccumulate started)
/// or Invoke immediately if (neither BeginIgnore nor BeginAccumulate are started)
/// </summary>
public void InvokeOrAccumulateOrIgnore(bool incrementAccumulatedCount=true)
{
if (ignore_Stack > 0)
return;
if(incrementAccumulatedCount)
Interlocked.Increment(ref accumulated_Count);
if (accumulate_Stack > 0)
return;
if(accumulated_Count>0)
OnAccumulatorFlush?.Invoke(this);
Reset();
}
}
public class EventAccumulator<T>:EventAccumulator
{
public EventAccumulator()
{
}
public EventAccumulator(Action<EventAccumulator<T>> onInvoke)
{
OnInvoke(e => onInvoke((EventAccumulator<T>)e));
}
public new T AccumulatedData { get { return (T)base.AccumulatedData; } set { base.AccumulatedData = value; } }
}
}