pmunin
3/22/2017 - 6:10 PM

Event Accumulator class

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; } }
    }
}