Extended my DispatcherUtil class today, with
SwitchBasedDelay!
Example of calling the new method, note we use a fixed Guid in this case to "group" calls into a logical switch:
DispatcherUtil.SwitchbasedDelayedInvokeAction(Guid.Parse(@"{4E101F98-31F3-4E19-B18B-2820AEA60A1B}"), () =>
{
PublishEvent<ProcedureFreeTypeTextChangedEvent, ProcedureFreeTypeTextChangedEventArg>(
new ProcedureFreeTypeTextChangedEventArg
{
FreshId = Context.CurrentOperationalUnit.FreshId,
ProcedureCode = Model.ProcedureTypeFreeText,
OperationId = Model.OperationId
});
}, 2000);
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Windows.Threading;
namespace SomeAcme.SomePackage
{
/// <summary>
/// Contains helper methods for dispatcher operations
/// </summary>
public static class DispatcherUtil
{
private static readonly List<DelayedAction> ActionsRegistered = new List<DelayedAction>();
private static readonly Dictionary<Guid?, bool> SwitchDelays = new Dictionary<Guid?, bool>();
/// <summary>
/// Executes an action passed into this method by a timeout measured in millisecond in a switch-based manner.
/// </summary>
/// <param name="keyToken">A key token (Guid) to identity the switch (basic grouping)</param>
/// <param name="executeAction">Action to execute</param>
/// <param name="timeOut">The timeout to wait before executing (in milliseconds)</param>
/// <param name="priority">Priority of the dispatcher operation</param>
/// <returns></returns>
public static bool SwitchbasedDelayedInvokeAction(Guid keyToken, Action executeAction, int timeOut,
DispatcherPriority priority = DispatcherPriority.Background)
{
if (SwitchDelays.ContainsKey(keyToken) && SwitchDelays[keyToken])
return false; //do not execute, already in progress
SwitchDelays[keyToken] = true;
DelayedInvokeAction(executeAction, timeOut, priority, keyToken);
return true; //delayed action sent off
}
/// <summary>
/// Executes an action passed into this method by a timeout measured in milliseconds
/// </summary>
/// <param name="executeAction">Action to execute</param>
/// <param name="timeOut">The timeout to wait before executing (in milliseconds)</param>
/// <param name="priority"></param>
/// /// <param name="keyToken">A key token to identity the switch (basic grouing). Will be used as a tag on the DispatcherTimer</param>
public static bool DelayedInvokeAction(Action executeAction, int timeOut, DispatcherPriority priority = DispatcherPriority.Background, Guid? keyToken = null)
{
var delayedAction = new DelayedAction(executeAction, timeOut, keyToken);
ActionsRegistered.Add(delayedAction);
DispatcherTimer dtimer = new DispatcherTimer(priority);
dtimer.Interval += new TimeSpan(0, 0, 0, 0, timeOut);
dtimer.Tag = delayedAction.ExecuteGuid;
dtimer.Tick += DelayedInvokeTimerTick;
dtimer.IsEnabled = true;
dtimer.Start();
return true;
}
private static void DelayedInvokeTimerTick(object sender, EventArgs e)
{
var dtimer = sender as DispatcherTimer;
if (dtimer != null)
{
dtimer.IsEnabled = false;
dtimer.Stop();
dtimer.Tick -= DelayedInvokeTimerTick; //unsubscribe
Guid targetActionGuid = (Guid)dtimer.Tag;
DelayedAction delayedAction = ActionsRegistered.Single(a => a.ExecuteGuid == targetActionGuid);
delayedAction.ActionToExecute(); //now execute the action
ActionsRegistered.Remove(delayedAction);
if (dtimer.Tag != null)
{
Guid? keyToken = dtimer.Tag as Guid?;
if (SwitchDelays.ContainsKey(keyToken))
{
SwitchDelays.Remove(keyToken); //remove the switch
} //if
} //if
// ReSharper disable once RedundantAssignment
dtimer = null; //ensure free up dispatcher timer - do not starve threading resources
} //if
}
/// <summary>
/// Invokes an action on the current dispatcher, used to execute operations on the GUI thread
/// </summary>
/// <param name="executeAction">The action to execute, pass in e.g. delegate { --code lines goes here } </param>
/// <param name="dispatcherPriority">The priority to give the action on the thread (signal to the WPF messaging queue). Default is background.</param>
/// <returns>Returns true when the action was dispatched</returns>
/// <remarks>Default priority is DispatcherPriority.Background</remarks>
public static bool InvokeAction(Action executeAction, DispatcherPriority dispatcherPriority = DispatcherPriority.Background)
{
Dispatcher.CurrentDispatcher.Invoke(new Action(() =>
{
executeAction();
}), dispatcherPriority);
return true;
}
/// <summary>
/// Asynchronously invokes an action on the current dispatcher, used to execute operations on the GUI thread
/// </summary>
/// <param name="executeAction">The action to execute, pass in e.g. delegate { --code lines goes here } </param>
/// <param name="dispatcherPriority">The priority to give the action on the thread (signal to the WPF messaging queue). Default is background.</param>
/// <returns>Returns true when the action was dispatched</returns>
/// <remarks>Default priority is DispatcherPriority.Background</remarks>
public static bool BeginInvokeAction(Action executeAction, DispatcherPriority dispatcherPriority = DispatcherPriority.Background)
{
Dispatcher.CurrentDispatcher.BeginInvoke(new Action(() =>
{
executeAction();
}), dispatcherPriority);
return true;
}
public static void AsyncWorkAndUiThreadUpdate<T>(Dispatcher currentDispatcher, Func<T> threadWork, Action<T> guiUpdate)
{
// ReSharper disable once UnusedAnonymousMethodSignature
ThreadPool.QueueUserWorkItem(delegate(object state)
{
T resultAfterThreadWork = threadWork();
// ReSharper disable once UnusedAnonymousMethodSignature
// ReSharper disable once UnusedAnonymousMethodSignature
currentDispatcher.BeginInvoke(DispatcherPriority.Normal, new Action<T>(delegate {
guiUpdate(resultAfterThreadWork);
}), resultAfterThreadWork);
});
}
}
}