using System;
using System.Threading;
namespace System.Windows.Forms {
public class AsyncWorker {
private int _ReportNext;
private bool _CancelNotified;
private volatile bool _CancelRequested ;
public bool CancelRequested { get { return _CancelRequested; } }
public void Cancel() { _CancelRequested = true; }
public int ReportInterval { get; set; }
public event EventHandler IsRunningChanged = delegate { };
public bool IsRunning { get; private set; }
private void ToggleIsRunning() {
IsRunning = !IsRunning;
IsRunningChanged(this, EventArgs.Empty);
}
private AsyncCallback _ToggleIsRunningCallback;
public AsyncWorker() {
ReportInterval = 300;
_ToggleIsRunningCallback = ar => InvokeGui(new Action(ToggleIsRunning));
}
private bool TryInvokeGui(Delegate action, bool reportProgress, params object[] args) {
if(_CancelRequested) {
if(_CancelNotified) throw this.BugException(
"after cancelation you should notify Gui no longer.");
_CancelNotified = true;
return false;
}
if(reportProgress) {
var ticks = Environment.TickCount;
if(ticks < _ReportNext) return true;
InvokeGui(action, args);
//set the next reportingtime, aligned to ReportInterval
_ReportNext = ticks + ReportInterval - (ticks - _ReportNext) % ReportInterval;
} else InvokeGui(action, args);
return true;
}
private void InvokeGui(Delegate action, params object[] args) {
var target = action.Target as Control;
if(Application.OpenForms.Count == 0 || (target != null && target.IsDisposed)) Cancel();
else Application.OpenForms[0].BeginInvoke(action, args);
}
/// <summary>
/// if canceled, returns false. if called once more, throws BugException: "after cancelation
/// you should notify Gui no longer.". Use the return-value to avoid that.
/// </summary>
public bool ReportProgress(Action psAction) { return TryInvokeGui(psAction, true); }
public bool ReportProgress<T1>(Action<T1> psAction, T1 arg1) {
return TryInvokeGui(psAction, true, arg1);
}
public bool ReportProgress<T1, T2>(Action<T1, T2> psAction, T1 arg1, T2 arg2) {
return TryInvokeGui(psAction, true, arg1, arg2);
}
public bool ReportProgress<T1, T2, T3>(Action<T1, T2, T3> psAction, T1 arg1, T2 arg2, T3 arg3) {
return TryInvokeGui(psAction, true, arg1, arg2, arg3);
}
public bool ReportProgress<T1, T2, T3, T4>(
Action<T1, T2, T3, T4> psAction, T1 arg1, T2 arg2, T3 arg3, T4 arg4) {
return TryInvokeGui(psAction, true, arg1, arg2, arg3, arg4);
}
/// <summary>
/// if canceled, returns false. if called once more, throws BugException: "after cancelation
/// you should notify Gui no longer.". Use the return-value to avoid that.
/// </summary>
public bool NotifyGui(Action syncAction) { return TryInvokeGui(syncAction, false); }
public bool NotifyGui<T1>(Action<T1> syncAction, T1 arg1) {
return TryInvokeGui(syncAction, false, arg1);
}
public bool NotifyGui<T1, T2>(Action<T1, T2> syncAction, T1 arg1, T2 arg2) {
return TryInvokeGui(syncAction, false, arg1, arg2);
}
public bool NotifyGui<T1, T2, T3>(Action<T1, T2, T3> syncAction, T1 arg1, T2 arg2, T3 arg3) {
return TryInvokeGui(syncAction, false, arg1, arg2, arg3);
}
public bool NotifyGui<T1, T2, T3, T4>(
Action<T1, T2, T3, T4> syncAction, T1 arg1, T2 arg2, T3 arg3, T4 arg4) {
return TryInvokeGui(syncAction, false, arg1, arg2, arg3, arg4);
}
private AsyncCallback GetCallback(AsyncCallback endInvoke) {
if(IsRunning) throw this.BugException("I'm already running");
_CancelRequested = _CancelNotified = false;
ToggleIsRunning();
_ReportNext = Environment.TickCount;
return (AsyncCallback)Delegate.Combine(endInvoke, _ToggleIsRunningCallback);
}
public void RunAsync(Action asyncAction) {
asyncAction.BeginInvoke( GetCallback( asyncAction.EndInvoke), null);
}
public void RunAsync<T>(Action<T> asyncAction, T arg) {
asyncAction.BeginInvoke(arg, GetCallback( asyncAction.EndInvoke), null);
}
public void RunAsync<T1, T2>(Action<T1, T2> asyncAction, T1 arg1, T2 arg2) {
asyncAction.BeginInvoke(arg1, arg2, GetCallback( asyncAction.EndInvoke), null);
}
public void RunAsync<T1, T2, T3>(Action<T1, T2, T3> asyncAction, T1 arg1, T2 arg2, T3 arg3) {
asyncAction.BeginInvoke(arg1, arg2,
arg3,GetCallback( asyncAction.EndInvoke), null);
}
public void RunAsync<T1, T2, T3, T4>(
Action<T1, T2, T3, T4> asyncAction, T1 arg1, T2 arg2, T3 arg3, T4 arg4) {
asyncAction.BeginInvoke(arg1, arg2,arg3,arg4,
GetCallback( asyncAction.EndInvoke), null);
}
}// class AsyncWorker
public static class AsyncWorkerX {
public static void RunAsync<T1, T2, T3>(this Action<T1, T2, T3> action, T1 arg1, T2 arg2, T3 arg3) {
// setting action.EndInvoke() as Callback-Argument makes shure, .EndInvoke() will be called
action.BeginInvoke(arg1, arg2, arg3, action.EndInvoke, null);
}
public static void RunAsync<T1, T2>(this Action<T1, T2> action, T1 arg1, T2 arg2) {
action.BeginInvoke(arg1, arg2, action.EndInvoke, null);
}
public static void RunAsync<T1>(this Action<T1> action, T1 arg1) {
action.BeginInvoke(arg1, action.EndInvoke, null);
}
public static void RunAsync(this Action action) {
action.BeginInvoke(action.EndInvoke, null);
}
private static bool InvokeGui(Delegate action, params object[] args) {
if(Application.OpenForms.Count == 0) return true;
if(Application.OpenForms[0].InvokeRequired) {
Application.OpenForms[0].BeginInvoke(action, args);
return true;
}
return false;
}
public static void NotifyGui<T1, T2, T3>(this Action<T1, T2, T3> action, T1 arg1, T2 arg2, T3 arg3) {
//if no Invoking required execute action directly
if(!InvokeGui(action, arg1, arg2, arg3)) action(arg1, arg2, arg3);
}
public static void NotifyGui<T1, T2>(this Action<T1, T2> action, T1 arg1, T2 arg2) {
if(!InvokeGui(action, arg1, arg2)) action(arg1, arg2);
}
public static void NotifyGui<T1>(this Action<T1> action, T1 arg1) {
if(!InvokeGui(action, arg1)) action(arg1);
}
public static void NotifyGui(this Action action) {
if(!InvokeGui(action)) action();
}
public static void RaiseGui<T1>(this EventHandler<T1> action, object sender, T1 arg1) where T1 : EventArgs {
if(!InvokeGui(action, sender, arg1)) action(sender, arg1);
}
public static void RaiseGui(this EventHandler action, object sender) {
if(!InvokeGui(action, sender, EventArgs.Empty)) action(sender, EventArgs.Empty);
}
}// class AsyncWorkerX
}