【C#】分享帶等待窗體的任務執行器一枚,
適用環境:.net 2.0+的Winform項目。
先解釋一下我所謂的【帶等待窗體的任務執行器】是個什麼鬼,就是可以用該類執行任意耗時方法(下文將把被執行的方法稱為任務或任務方法),執行期間會顯示一個模式等待窗體,讓用戶知道任務正在得到執行,程序並沒有卡死。先看一下效果:
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017012018245291.png)
功能:
- 等待窗體可以使用執行器自帶的默認窗體(就上圖的樣子),嫌丑你也可以使用自己精心設計的窗體,甚至基於Devpress、C1等第三方漂亮窗體打造也是完全可以的
- 在任務中可以更新等待窗體上的Label、ProgressBar之類的控件以提供進度反饋。懶得反饋的話,就默認“請稍候...”+Marquee式滾動
- 如果任務允許被終止,用戶可以通過某些操作終止任務執行(例如點擊上圖中的【取消】按鈕);如果不允許,你可以把取消按鈕隱藏了,或者在任務中不響應用戶的終止請求就好
- 任務的執行結果(包括ref/out參數)、是否出現異常、是否被取消等情況都可以得到
原理:
- 調用任務所屬委托的BeginInvoke,讓任務在後台線程執行,隨即在UI線程(通常就是主線程)調用等待窗體的ShowDialog彈出模式窗體,讓用戶知道任務正在執行的同時阻止用戶進行其他操作。由於任務和等待窗體分別在不同的線程跑,所以等待窗體不會被卡住
- 任務執行期間可以通過執行器提供的一組屬性和方法操作等待窗體上的控件,這組屬性和方法內部是通過調用等待窗體的Invoke或BeginInovke對控件進行操作,實現跨線程訪問控件
- 任務執行期間用戶可以通過點擊等待窗體上的【取消】按鈕(如果你讓它顯示的話)或點擊右上角關閉按鈕發出終止任務的請求(等待窗體會攔截關閉操作),其結果是執行器的UserCancelling屬性會置為true,所以在任務中可以訪問該屬性得知用戶是否請求了取消操作,如果你同意終止的話,需設置執行器的Cancelled=true,並隨即return出任務方法
- 任務執行完後(無論成功、異常、取消)會自動進入異步回調方法,回調方法中會首先訪問Cancelled獲知任務是否已取消,如果已取消,則直接return出回調方法;如果未取消,則調用任務所屬委托的EndInvoke得到任務執行結果或異常。最後不管取消與否,finally塊中會設置等待窗體的DialogResult屬性,以確保關閉等待窗體
- 等待窗體關閉後,執行器會繼續執行ShowDialog後面的語句。如果任務已取消,則拋出特定異常報告調用者;如果任務存在異常,則拋出該異常;以上情況都不存在的話,返回任務結果
如述,功能簡單,實現容易,我只是把這種需求通用化了一下,讓還沒有類似輪子的朋友可以拿去就用。另外還有個基於BackgroundWorker的實現方式,我可能會在下一篇文章分享。
先看一下大致的使用示例:
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017012018245242.gif)
![]()
//WaitUI就是執行器
private void button1_Click(object sender, EventArgs es)
{
//可檢測執行器是否正在執行另一個任務。其實基本不可能出現IsBusy=true,因為執行器工作時,用戶做不了其它事
//老實說這個IsBusy要不要公開我還糾結了一下,因為公開了沒什麼用,但也沒什麼壞處,因為setter是private的
//Whatever~最後我還是選擇公開,可能~因為愛情
//if (WaitUI.IsBusy) { return; }
try
{
//WaitUI.RunXXX方法用於執行任務
//該方法的返回值就是任務的返回值
//任務拋出的異常會通過RunXXX方法拋出
//WaitUI.RunAction(Foo, 33, 66); //執行無返回值的方法
int r = WaitUI.RunFunc(Foo, 33, 66); //執行有返回值的方法
//object r = WaitUI.RunDelegate(new Func<int, int, int>(Foo), 33, 66);//執行委托
//WaitUI.RunAction(new MyWaitForm(), Foo);//指定自定義等待窗體執行任務,幾個RunXXX方法都有可指定自定義窗體的重載
MessageBoxEx.Show("任務完成。" + r);
}
catch (WorkCancelledException)//任務被取消是通過拋出該異常來報告
{
MessageBoxEx.Show("任務已取消!");
}
catch (Exception ex)//任務拋出的異常
{
MessageBoxEx.Show("任務出現異常!" + ex.Message);
}
}
//耗時任務。因為該方法會在後台線程執行,所以方法中不可以有訪問控件的代碼
int Foo(int a, int b)
{
//可以通過執行器的一系列公開屬性和方法間接操作等待窗體的UI元素
WaitUI.CancelControlVisible = true;//設置取消任務的控件的可見性,即是否允許用戶取消任務(默認是false:不可見)
WaitUI.BarStyle = ProgressBarStyle.Continuous;//設置滾動條樣式(默認是Marquee:循環梭動式)
WaitUI.BarMaximum = 100; //設置滾動條值上限(默認是100)
WaitUI.BarMinimum = 0; //設置滾動條值下限(默認是0)
WaitUI.BarStep = 1; //設置滾動條步進幅度(默認是10)
WaitUI.BarVisible = true; //設置滾動條是否可見(默認是true:可見)
int i;
for (i = a; i < b; i++)
{
if (WaitUI.UserCancelling)//響應用戶的取消請求
{
WaitUI.Cancelled = true;//告訴執行器任務已取消
return 0;
}
//可以拋個異常試試
//if (i == 43) { throw new NotSupportedException("異常測試"); }
//可以隨時再次操作等待窗體的各種UI元素
//if (i % 10 == 0) { WaitUI.CancelControlVisible = false; } //隱藏取消控件
//else if (i % 5 == 0) { WaitUI.CancelControlVisible = true; }//顯示取消控件
WaitUI.WorkMessage = "正在XXOO,已完成 " + i + " 下..."; //更新進度描述
WaitUI.BarValue = i;//更新進度值
//WaitUI.BarPerformStep();//步進進度條
Thread.Sleep(100);
}
return i;
}
使用示例
看完示例,熟悉套路的你可能都已經能洞悉實現細節了,不過作為方案分享文章,我還是照章講一下使用說明先,後面再掰扯實現說明,先看類圖:
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017012018245274.png)
使用說明:
方案源碼:
WaitUI.cs包含class WaitUI和3個異常類WaitFormNullException、WorkIsBusyException、WorkCancelledException
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017012018245242.gif)
![]()
using System;
using System.Reflection;
using System.Windows.Forms;
namespace AhDung.WinForm
{
/// <summary>
/// 執行任務並顯示等候窗體
/// </summary>
public static class WaitUI
{
static IWaitForm waitForm; //等待窗體
static object result; //任務返回結果
static Exception exception;//任務執行異常
static object[] parmsInput; //調用者傳入的參數
static ParameterInfo[] parmsMethod;//任務所需的參數
static bool isCallBackCompleted; //指示回調方法是否已執行完畢
/// <summary>
/// 指示用戶是否已請求取消任務
/// </summary>
public static bool UserCancelling
{
get;
private set;
}
/// <summary>
/// 指示任務是否已取消
/// </summary>
public static bool Cancelled
{
private get;
set;
}
/// <summary>
/// 指示任務是否正在執行中
/// </summary>
public static bool IsBusy
{
get;
private set;
}
#region 一系列操作等候窗體UI的屬性/方法
/// <summary>
/// 獲取或設置進度描述
/// </summary>
public static string WorkMessage
{
get
{
if (waitForm.InvokeRequired)
{
return waitForm.Invoke(new Func<object>(() => waitForm.WorkMessage)) as string;
}
return waitForm.WorkMessage;
}
set
{
if (waitForm == null) { return; }
if (waitForm.InvokeRequired)
{
waitForm.BeginInvoke(new Action(() => waitForm.WorkMessage = value));
return;
}
waitForm.WorkMessage = value;
}
}
/// <summary>
/// 獲取或設置進度條可見性
/// </summary>
public static bool BarVisible
{
get
{
if (waitForm.InvokeRequired)
{
return Convert.ToBoolean(waitForm.Invoke(new Func<object>(() => waitForm.BarVisible)));
}
return waitForm.BarVisible;
}
set
{
if (waitForm == null) { return; }
if (waitForm.InvokeRequired)
{
waitForm.BeginInvoke(new Action(() => waitForm.BarVisible = value));
return;
}
waitForm.BarVisible = value;
}
}
/// <summary>
/// 獲取或設置進度條動畫樣式
/// </summary>
public static ProgressBarStyle BarStyle
{
get
{
if (waitForm.InvokeRequired)
{
return (ProgressBarStyle)(waitForm.Invoke(new Func<object>(() => waitForm.BarStyle)));
}
return waitForm.BarStyle;
}
set
{
if (waitForm == null) { return; }
if (waitForm.InvokeRequired)
{
waitForm.BeginInvoke(new Action(() => waitForm.BarStyle = value));
return;
}
waitForm.BarStyle = value;
}
}
/// <summary>
/// 獲取或設置進度值
/// </summary>
public static int BarValue
{
get
{
if (waitForm.InvokeRequired)
{
return Convert.ToInt32(waitForm.Invoke(new Func<object>(() => waitForm.BarValue)));
}
return waitForm.BarValue;
}
set
{
if (waitForm == null) { return; }
if (waitForm.InvokeRequired)
{
waitForm.BeginInvoke(new Action(() => waitForm.BarValue = value));
return;
}
waitForm.BarValue = value;
}
}
/// <summary>
/// 獲取或設置進度條步進值
/// </summary>
public static int BarStep
{
get
{
if (waitForm.InvokeRequired)
{
return Convert.ToInt32(waitForm.Invoke(new Func<object>(() => waitForm.BarStep)));
}
return waitForm.BarStep;
}
set
{
if (waitForm == null) { return; }
if (waitForm.InvokeRequired)
{
waitForm.BeginInvoke(new Action(() => waitForm.BarStep = value));
return;
}
waitForm.BarStep = value;
}
}
/// <summary>
/// 使進度條步進
/// </summary>
public static void BarPerformStep()
{
if (waitForm == null) { return; }
if (waitForm.InvokeRequired)
{
waitForm.BeginInvoke(new Action(() => waitForm.BarPerformStep()));
return;
}
waitForm.BarPerformStep();
}
/// <summary>
/// 獲取或設置進度條上限值
/// </summary>
public static int BarMaximum
{
get
{
if (waitForm.InvokeRequired)
{
return Convert.ToInt32(waitForm.Invoke(new Func<object>(() => waitForm.BarMaximum)));
}
return waitForm.BarMaximum;
}
set
{
if (waitForm == null) { return; }
if (waitForm.InvokeRequired)
{
waitForm.BeginInvoke(new Action(() => waitForm.BarMaximum = value));
return;
}
waitForm.BarMaximum = value;
}
}
/// <summary>
/// 獲取或設置進度條下限值
/// </summary>
public static int BarMinimum
{
get
{
if (waitForm.InvokeRequired)
{
return Convert.ToInt32(waitForm.Invoke(new Func<object>(() => waitForm.BarMinimum)));
}
return waitForm.BarMinimum;
}
set
{
if (waitForm == null) { return; }
if (waitForm.InvokeRequired)
{
waitForm.BeginInvoke(new Action(() => waitForm.BarMinimum = value));
return;
}
waitForm.BarMinimum = value;
}
}
/// <summary>
/// 獲取或設置取消任務的控件的可見性
/// </summary>
public static bool CancelControlVisible
{
get
{
if (waitForm.InvokeRequired)
{
return Convert.ToBoolean(waitForm.Invoke(new Func<object>(() => waitForm.CancelControlVisible)));
}
return waitForm.CancelControlVisible;
}
set
{
if (waitForm == null) { return; }
if (waitForm.InvokeRequired)
{
waitForm.BeginInvoke(new Action(() => waitForm.CancelControlVisible = value));
return;
}
waitForm.CancelControlVisible = value;
}
}
#endregion
#region 公共方法:無返回值+默認窗體
/// <summary>
/// 執行方法並顯示默認等候窗體
/// </summary>
public static void RunAction(Action method)
{
RunDelegate(method);
}
/// <summary>
/// 執行方法並顯示默認等候窗體
/// </summary>
public static void RunAction<T>(Action<T> method, T arg)
{
RunDelegate(method, arg);
}
/// <summary>
/// 執行方法並顯示默認等候窗體
/// </summary>
public static void RunAction<T1, T2>(Action<T1, T2> method, T1 arg1, T2 arg2)
{
RunDelegate(method, arg1, arg2);
}
/// <summary>
/// 執行方法並顯示默認等候窗體
/// </summary>
public static void RunAction<T1, T2, T3>(Action<T1, T2, T3> method, T1 arg1, T2 arg2, T3 arg3)
{
RunDelegate(method, arg1, arg2, arg3);
}
/// <summary>
/// 執行方法並顯示默認等候窗體
/// </summary>
public static void RunAction<T1, T2, T3, T4>(Action<T1, T2, T3, T4> method, T1 arg1, T2 arg2, T3 arg3, T4 arg4)
{
RunDelegate(method, arg1, arg2, arg3, arg4);
}
/// <summary>
/// 執行方法並顯示默認等候窗體
/// </summary>
public static void RunAction<T1, T2, T3, T4, T5>(Action<T1, T2, T3, T4, T5> method, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5)
{
RunDelegate(method, arg1, arg2, arg3, arg4, arg5);
}
/// <summary>
/// 執行方法並顯示默認等候窗體
/// </summary>
public static void RunAction<T1, T2, T3, T4, T5, T6>(Action<T1, T2, T3, T4, T5, T6> method, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6)
{
RunDelegate(method, arg1, arg2, arg3, arg4, arg5, arg6);
}
/// <summary>
/// 執行方法並顯示默認等候窗體
/// </summary>
public static void RunAction<T1, T2, T3, T4, T5, T6, T7>(Action<T1, T2, T3, T4, T5, T6, T7> method, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7)
{
RunDelegate(method, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
}
/// <summary>
/// 執行方法並顯示默認等候窗體
/// </summary>
public static void RunAction<T1, T2, T3, T4, T5, T6, T7, T8>(Action<T1, T2, T3, T4, T5, T6, T7, T8> method, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8)
{
RunDelegate(method, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8);
}
#endregion
#region 公共方法:無返回值+自定義窗體
/// <summary>
/// 執行方法並顯示自定義等候窗體
/// </summary>
public static void RunAction(IWaitForm fmWait, Action method)
{
RunDelegate(fmWait, method);
}
/// <summary>
/// 執行方法並顯示自定義等候窗體
/// </summary>
public static void RunAction<T>(IWaitForm fmWait, Action<T> method, T arg)
{
RunDelegate(fmWait, method, arg);
}
/// <summary>
/// 執行方法並顯示自定義等候窗體
/// </summary>
public static void RunAction<T1, T2>(IWaitForm fmWait, Action<T1, T2> method, T1 arg1, T2 arg2)
{
RunDelegate(fmWait, method, arg1, arg2);
}
/// <summary>
/// 執行方法並顯示自定義等候窗體
/// </summary>
public static void RunAction<T1, T2, T3>(IWaitForm fmWait, Action<T1, T2, T3> method, T1 arg1, T2 arg2, T3 arg3)
{
RunDelegate(fmWait, method, arg1, arg2, arg3);
}
/// <summary>
/// 執行方法並顯示自定義等候窗體
/// </summary>
public static void RunAction<T1, T2, T3, T4>(IWaitForm fmWait, Action<T1, T2, T3, T4> method, T1 arg1, T2 arg2, T3 arg3, T4 arg4)
{
RunDelegate(fmWait, method, arg1, arg2, arg3, arg4);
}
/// <summary>
/// 執行方法並顯示自定義等候窗體
/// </summary>
public static void RunAction<T1, T2, T3, T4, T5>(IWaitForm fmWait, Action<T1, T2, T3, T4, T5> method, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5)
{
RunDelegate(fmWait, method, arg1, arg2, arg3, arg4, arg5);
}
/// <summary>
/// 執行方法並顯示自定義等候窗體
/// </summary>
public static void RunAction<T1, T2, T3, T4, T5, T6>(IWaitForm fmWait, Action<T1, T2, T3, T4, T5, T6> method, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6)
{
RunDelegate(fmWait, method, arg1, arg2, arg3, arg4, arg5, arg6);
}
/// <summary>
/// 執行方法並顯示自定義等候窗體
/// </summary>
public static void RunAction<T1, T2, T3, T4, T5, T6, T7>(IWaitForm fmWait, Action<T1, T2, T3, T4, T5, T6, T7> method, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7)
{
RunDelegate(fmWait, method, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
}
/// <summary>
/// 執行方法並顯示自定義等候窗體
/// </summary>
public static void RunAction<T1, T2, T3, T4, T5, T6, T7, T8>(IWaitForm fmWait, Action<T1, T2, T3, T4, T5, T6, T7, T8> method, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8)
{
RunDelegate(fmWait, method, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8);
}
#endregion
#region 公共方法:有返回值+默認窗體
/// <summary>
/// 執行方法並顯示默認等候窗體
/// </summary>
public static TResult RunFunc<TResult>(Func<TResult> method)
{
return (TResult)RunDelegate(method);
}
/// <summary>
/// 執行方法並顯示默認等候窗體
/// </summary>
public static TResult RunFunc<T, TResult>(Func<T, TResult> method, T arg)
{
return (TResult)RunDelegate(method, arg);
}
/// <summary>
/// 執行方法並顯示默認等候窗體
/// </summary>
public static TResult RunFunc<T1, T2, TResult>(Func<T1, T2, TResult> method, T1 arg1, T2 arg2)
{
return (TResult)RunDelegate(method, arg1, arg2);
}
/// <summary>
/// 執行方法並顯示默認等候窗體
/// </summary>
public static TResult RunFunc<T1, T2, T3, TResult>(Func<T1, T2, T3, TResult> method, T1 arg1, T2 arg2, T3 arg3)
{
return (TResult)RunDelegate(method, arg1, arg2, arg3);
}
/// <summary>
/// 執行方法並顯示默認等候窗體
/// </summary>
public static TResult RunFunc<T1, T2, T3, T4, TResult>(Func<T1, T2, T3, T4, TResult> method, T1 arg1, T2 arg2, T3 arg3, T4 arg4)
{
return (TResult)RunDelegate(method, arg1, arg2, arg3, arg4);
}
/// <summary>
/// 執行方法並顯示默認等候窗體
/// </summary>
public static TResult RunFunc<T1, T2, T3, T4, T5, TResult>(Func<T1, T2, T3, T4, T5, TResult> method, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5)
{
return (TResult)RunDelegate(method, arg1, arg2, arg3, arg4, arg5);
}
/// <summary>
/// 執行方法並顯示默認等候窗體
/// </summary>
public static TResult RunFunc<T1, T2, T3, T4, T5, T6, TResult>(Func<T1, T2, T3, T4, T5, T6, TResult> method, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6)
{
return (TResult)RunDelegate(method, arg1, arg2, arg3, arg4, arg5, arg6);
}
/// <summary>
/// 執行方法並顯示默認等候窗體
/// </summary>
public static TResult RunFunc<T1, T2, T3, T4, T5, T6, T7, TResult>(Func<T1, T2, T3, T4, T5, T6, T7, TResult> method, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7)
{
return (TResult)RunDelegate(method, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
}
/// <summary>
/// 執行方法並顯示默認等候窗體
/// </summary>
public static TResult RunFunc<T1, T2, T3, T4, T5, T6, T7, T8, TResult>(Func<T1, T2, T3, T4, T5, T6, T7, T8, TResult> method, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8)
{
return (TResult)RunDelegate(method, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8);
}
#endregion
#region 公共方法:有返回值+自定義窗體
/// <summary>
/// 執行方法並顯示自定義等候窗體
/// </summary>
public static TResult RunFunc<TResult>(IWaitForm fmWait, Func<TResult> method)
{
return (TResult)RunDelegate(fmWait, method);
}
/// <summary>
/// 執行方法並顯示自定義等候窗體
/// </summary>
public static TResult RunFunc<T, TResult>(IWaitForm fmWait, Func<T, TResult> method, T arg)
{
return (TResult)RunDelegate(fmWait, method, arg);
}
/// <summary>
/// 執行方法並顯示自定義等候窗體
/// </summary>
public static TResult RunFunc<T1, T2, TResult>(IWaitForm fmWait, Func<T1, T2, TResult> method, T1 arg1, T2 arg2)
{
return (TResult)RunDelegate(fmWait, method, arg1, arg2);
}
/// <summary>
/// 執行方法並顯示自定義等候窗體
/// </summary>
public static TResult RunFunc<T1, T2, T3, TResult>(IWaitForm fmWait, Func<T1, T2, T3, TResult> method, T1 arg1, T2 arg2, T3 arg3)
{
return (TResult)RunDelegate(fmWait, method, arg1, arg2, arg3);
}
/// <summary>
/// 執行方法並顯示自定義等候窗體
/// </summary>
public static TResult RunFunc<T1, T2, T3, T4, TResult>(IWaitForm fmWait, Func<T1, T2, T3, T4, TResult> method, T1 arg1, T2 arg2, T3 arg3, T4 arg4)
{
return (TResult)RunDelegate(fmWait, method, arg1, arg2, arg3, arg4);
}
/// <summary>
/// 執行方法並顯示自定義等候窗體
/// </summary>
public static TResult RunFunc<T1, T2, T3, T4, T5, TResult>(IWaitForm fmWait, Func<T1, T2, T3, T4, T5, TResult> method, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5)
{
return (TResult)RunDelegate(fmWait, method, arg1, arg2, arg3, arg4, arg5);
}
/// <summary>
/// 執行方法並顯示自定義等候窗體
/// </summary>
public static TResult RunFunc<T1, T2, T3, T4, T5, T6, TResult>(IWaitForm fmWait, Func<T1, T2, T3, T4, T5, T6, TResult> method, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6)
{
return (TResult)RunDelegate(fmWait, method, arg1, arg2, arg3, arg4, arg5, arg6);
}
/// <summary>
/// 執行方法並顯示自定義等候窗體
/// </summary>
public static TResult RunFunc<T1, T2, T3, T4, T5, T6, T7, TResult>(IWaitForm fmWait, Func<T1, T2, T3, T4, T5, T6, T7, TResult> method, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7)
{
return (TResult)RunDelegate(fmWait, method, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
}
/// <summary>
/// 執行方法並顯示自定義等候窗體
/// </summary>
public static TResult RunFunc<T1, T2, T3, T4, T5, T6, T7, T8, TResult>(IWaitForm fmWait, Func<T1, T2, T3, T4, T5, T6, T7, T8, TResult> method, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8)
{
return (TResult)RunDelegate(fmWait, method, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8);
}
#endregion
/// <summary>
/// 執行委托並顯示默認等候窗體
/// </summary>
public static object RunDelegate(Delegate del, params object[] args)
{
return RunDelegate(new WaitForm(), del, args);
}
/// <summary>
/// 執行委托並顯示自定義等候窗體
/// </summary>
public static object RunDelegate(IWaitForm fmWait, Delegate del, params object[] args)
{
if (IsBusy) { throw new WorkIsBusyException(); }
if (fmWait == null) { throw new WaitFormNullException(); }
if (del == null || del.GetInvocationList().Length != 1) { throw new ApplicationException("委托不能為空,且只能綁定1個方法!"); }
if (args == null) { throw new ArgumentNullException("args"); }
MethodInfo beginInvoke = del.GetType().GetMethod("BeginInvoke");
object[] parmsBeginInvoke = new object[beginInvoke.GetParameters().Length];
if (args.Length > parmsBeginInvoke.Length - 2)
{
throw new ArgumentException("提供的參數超過了方法所需的參數!");
}
parmsMethod = del.Method.GetParameters();//假定GetParameters總是返回按參數Position排序的數組,如果將來有問題,要查驗這個假設
parmsInput = args;
try
{
//賦值BeginInvoke參數
parmsInput.CopyTo(parmsBeginInvoke, 0); //塞入傳入的參數
for (int i = parmsInput.Length; i < parmsMethod.Length; i++) //對未傳入的參數賦予默認值
{
ParameterInfo p = parmsMethod[i];
object pVal;
if ((pVal = p.DefaultValue) == DBNull.Value) //若參數不具有默認值則拋異常
{ throw new ArgumentException(string.Format("方法所需的參數{0}沒有定義默認值,必須傳入!", p.Name)); }
parmsBeginInvoke[i] = pVal;
}
parmsBeginInvoke[parmsBeginInvoke.Length - 2] = new AsyncCallback(Callback);//倒數第2個參數
parmsBeginInvoke[parmsBeginInvoke.Length - 1] = del; //倒數第1個參數
//重置狀態
IsBusy = true;
Cancelled = false;
exception = null;
isCallBackCompleted = false;
waitForm = fmWait;
fmWait.UserCancelling += WaitForm_UserCancelling;//注冊用戶取消任務事件
beginInvoke.Invoke(del, parmsBeginInvoke);
if (!isCallBackCompleted)//這裡要判斷一下,極端情況下有可能還沒等ShowDialog,回調就執行完了
{
fmWait.ShowDialog(); //務必確保ShowDialog不會拋異常
}
//返回
if (Cancelled) { throw new WorkCancelledException(); }
if (exception != null) { throw exception; }
return result;
}
finally
{
Release();
UserCancelling = false;
IsBusy = false;
}
}
/// <summary>
/// 回調方法
/// </summary>
private static void Callback(IAsyncResult ar)
{
try
{
if (Cancelled) { return; } //若任務取消就不必EndInvoke了
MethodInfo endInvoke = ar.AsyncState.GetType().GetMethod("EndInvoke");
object[] parmsEndInvoke = new object[endInvoke.GetParameters().Length];
if (parmsEndInvoke.Length != 1)//若方法存在ref或out參數,賦值給endInvoke參數
{
int i = 0;
foreach (ParameterInfo p in parmsMethod)
{
if (p.ParameterType.IsByRef) { parmsEndInvoke[i++] = parmsInput[p.Position]; }
}
}
parmsEndInvoke[parmsEndInvoke.Length - 1] = ar;
result = endInvoke.Invoke(ar.AsyncState, parmsEndInvoke);
if (parmsEndInvoke.Length != 1)//從endInvoke參數取出值返給輸入參數
{
int i = 0;
foreach (ParameterInfo p in parmsMethod)
{
if (p.ParameterType.IsByRef) { parmsInput[p.Position] = parmsEndInvoke[i++]; }
}
}
}
catch (TargetInvocationException ex)
{
exception = ex.InnerException;
}
catch (Exception ex)
{
exception = ex;
}
finally
{
//這裡不能用waitForm.Hide()關閉等待窗體,因為回調方法仍然是用後台線程執行,不能操作UI
waitForm.DialogResult = DialogResult.OK;
isCallBackCompleted = true;
}
}
/// <summary>
/// 用戶請求取消任務時
/// </summary>
private static void WaitForm_UserCancelling(object sender, EventArgs e)
{
UserCancelling = true;
}
/// <summary>
/// 釋放資源
/// </summary>
private static void Release()
{
parmsInput = null;//這裡不會影響調用者傳入的object[]實例,因為不是ref進來的
parmsMethod = null;
IDisposable disp;
if ((disp = waitForm as IDisposable) != null) { disp.Dispose(); }
}
}
/// <summary>
/// 等候窗體為空
/// </summary>
public class WaitFormNullException : ApplicationException
{
public WaitFormNullException() : base("等待窗體不能為null!") { }
}
/// <summary>
/// 任務正在執行
/// </summary>
public class WorkIsBusyException : InvalidOperationException
{
public WorkIsBusyException() : base("任務正在執行!") { }
}
/// <summary>
/// 任務已被取消
/// </summary>
public class WorkCancelledException : ApplicationException
{
public WorkCancelledException() : base("任務已被取消!") { }
}
}
WaitUI.cs
WaitForm.cs包含interface IWaitForm和class WaitForm,其中WaitForm為了實現屏蔽關閉按鈕,使用了WinFormHelper.cs
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017012018245242.gif)
![]()
using System;
using System.Windows.Forms;
namespace AhDung.WinForm
{
/// <summary>
/// 等待窗體
/// </summary>
///<remarks>IWaitForm的默認實現</remarks>
public class WaitForm : Form, IWaitForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private readonly System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.lbMsg = new System.Windows.Forms.Label();
this.bar = new System.Windows.Forms.ProgressBar();
this.btnCancel = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// lbMsg
//
this.lbMsg.Location = new System.Drawing.Point(10, 20);
this.lbMsg.Name = "lbMsg";
this.lbMsg.Size = new System.Drawing.Size(386, 55);
this.lbMsg.TabIndex = 0;
this.lbMsg.Text = "正在處理,請稍候...";
//
// bar
//
this.bar.Location = new System.Drawing.Point(12, 78);
this.bar.Name = "bar";
this.bar.Step = 1;
this.bar.Size = new System.Drawing.Size(384, 16);
this.bar.Style = System.Windows.Forms.ProgressBarStyle.Marquee;
this.bar.TabIndex = 1;
//
// btnCancel
//
this.btnCancel.Location = new System.Drawing.Point(321, 109);
this.btnCancel.Name = "btnCancel";
this.btnCancel.Size = new System.Drawing.Size(75, 23);
this.btnCancel.TabIndex = 2;
this.btnCancel.Text = "取消";
this.btnCancel.UseVisualStyleBackColor = true;
this.btnCancel.Visible = false;
//
// FmWaitForDesign
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(408, 155);
this.Controls.Add(this.btnCancel);
this.Controls.Add(this.bar);
this.Controls.Add(this.lbMsg);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
this.MaximizeBox = false;
this.Name = "FmWaitForDesign";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "請稍候...";
this.ResumeLayout(false);
}
#endregion
System.Windows.Forms.Label lbMsg;
System.Windows.Forms.Button btnCancel;
System.Windows.Forms.ProgressBar bar;
public WaitForm()
{
InitializeComponent();
btnCancel.Click += btnCancel_Click;
this.FormClosing += WaitForm_FormClosing;
}
#region 將【取消】按鈕點擊、窗體關閉等行為視為觸發【取消任務】事件
private void WaitForm_FormClosing(object sender, FormClosingEventArgs e)
{
if (e.CloseReason == CloseReason.UserClosing)
{
e.Cancel = true;
this.OnUserCancelling();
}
}
private void btnCancel_Click(object sender, EventArgs e)
{
this.btnCancel.Enabled = false;
this.OnUserCancelling();
}
protected virtual void OnUserCancelling()
{
if (UserCancelling != null) { UserCancelling(this, EventArgs.Empty); }
}
//屏蔽窗體關閉按鈕
protected override void OnVisibleChanged(EventArgs e)
{
base.OnVisibleChanged(e);
if (this.Visible) { AhDung.WinForm.WinFormHelper.DisableCloseButton(this); }
}
#endregion
#region 實現接口
public string WorkMessage
{
get { return lbMsg.Text; }
set { lbMsg.Text = value; }
}
public bool BarVisible
{
get { return bar.Visible; }
set { bar.Visible = value; }
}
public ProgressBarStyle BarStyle
{
get { return bar.Style; }
set { bar.Style = value; }
}
public int BarValue
{
get { return bar.Value; }
set { bar.Value = value; }
}
public int BarStep
{
get { return bar.Step; }
set { bar.Step = value; }
}
public void BarPerformStep()
{
bar.PerformStep();
}
public bool CancelControlVisible
{
get { return btnCancel.Visible; }
set { btnCancel.Visible = value; }
}
public int BarMaximum
{
get { return bar.Maximum; }
set { bar.Maximum = value; }
}
public int BarMinimum
{
get { return bar.Minimum; }
set { bar.Minimum = value; }
}
public event EventHandler UserCancelling;
#endregion
}
/// <summary>
/// 等待窗體規范
/// </summary>
public interface IWaitForm
{
#region 用於操作等待窗體UI表現的屬性和方法,實現時不用操心線程問題,讓客戶端(任務執行器)去操心
/// <summary>
/// 獲取或設置進度描述
/// </summary>
/// <remarks>建議默認值為“請稍候...”之類的字眼</remarks>
string WorkMessage { get; set; }
/// <summary>
/// 獲取或設置進度條的可見性
/// </summary>
/// <remarks>建議默認值為true</remarks>
bool BarVisible { get; set; }
/// <summary>
/// 獲取或設置進度條的動畫樣式
/// </summary>
/// <remarks>建議默認值為Marquee</remarks>
ProgressBarStyle BarStyle { get; set; }
/// <summary>
/// 獲取或設置進度條的值
/// </summary>
/// <remarks>建議默認值為0</remarks>
int BarValue { get; set; }
/// <summary>
/// 獲取或設置進度條的步進幅度
/// </summary>
int BarStep { get; set; }
/// <summary>
/// 使進度條步進
/// </summary>
void BarPerformStep();
/// <summary>
/// 獲取或設置取消任務的控件的可見性
/// </summary>
/// <remarks>建議默認值為false</remarks>
bool CancelControlVisible { get; set; }
/// <summary>
/// 獲取或設置進度條的值上限
/// </summary>
/// <remarks>建議默認值為100</remarks>
int BarMaximum { get; set; }
/// <summary>
/// 獲取或設置進度條的值下限
/// </summary>
/// <remarks>建議默認值為0</remarks>
int BarMinimum { get; set; }
#endregion
/// <summary>
/// 顯示模式等待窗體
/// </summary>
/// <remarks>建議使用Form類的默認實現</remarks>
DialogResult ShowDialog();
#region Invoke相關,供客戶端在後台線程中操作窗體UI
/// <summary>
/// 指示是否需要使用Invoke操作窗體控件
/// </summary>
/// <remarks>建議使用Form類的默認實現</remarks>
bool InvokeRequired { get; }
/// <summary>
/// 窗體Invoke方法
/// </summary>
/// <remarks>建議使用Form類的默認實現</remarks>
object Invoke(Delegate method);
/// <summary>
/// 窗體BeginInvoke方法
/// </summary>
/// <remarks>建議使用Form類的默認實現</remarks>
IAsyncResult BeginInvoke(Delegate method);
#endregion
#region 用於隱藏等待窗體,調用時二選一即可
//之所以要同實現DialogResult屬性和Hide方法,是因為在客戶端中需要隱藏窗體時,執行隱藏操作的線程是否UI線程要區別對待,
//是UI線程則選用Hide()方法,否則選用DialogResult屬性
/// <summary>
/// 獲取或設置窗體對話框結果
/// </summary>
/// <remarks>建議使用Form類的默認實現</remarks>
DialogResult DialogResult { get; set; }
/// <summary>
/// 隱藏等待窗體
/// </summary>
/// <remarks>建議使用Form類的默認實現</remarks>
void Hide();
#endregion
/// <summary>
/// 當用戶請求取消任務時
/// </summary>
/// <remarks>應在用戶交互取消控件、關閉窗體時觸發該事件</remarks>
event EventHandler UserCancelling;
}
}
WaitForm.cs
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017012018245242.gif)
![]()
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace AhDung.WinForm
{
public static class WinFormHelper
{
[DllImport("User32.dll ")]
private static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert);
[DllImport("User32.dll ")]
private static extern int RemoveMenu(IntPtr hMenu, int nPosition, int wFlags);
const int MF_REMOVE = 0x1000;
//const int SC_RESTORE = 0xF120; //還原
//const int SC_MOVE = 0xF010; //移動
//const int SC_SIZE = 0xF000; //大小
//const int SC_MINIMIZE = 0xF020; //最小化
//const int SC_MAXIMIZE = 0xF030; //最大化
const int SC_CLOSE = 0xF060; //關閉
/// <summary>
/// 屏蔽窗體關閉功能
/// </summary>
public static void DisableCloseButton(IWin32Window form)
{
IntPtr hMenu = GetSystemMenu(form.Handle, false);
RemoveMenu(hMenu, SC_CLOSE, MF_REMOVE);
}
}
}
WinFormHelper.cs
可憐的.net 2.0~3.5沒有足夠的內置委托,所以你可能還需要Delegates.cs
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017012018245242.gif)
![]()
namespace System
{
//無返回委托
public delegate void Action();
//public delegate void Action<in T>(T arg);//這個2.0是有的
public delegate void Action<in T1, in T2>(T1 arg1, T2 arg2);
public delegate void Action<in T1, in T2, in T3>(T1 arg1, T2 arg2, T3 arg3);
public delegate void Action<in T1, in T2, in T3, in T4>(T1 arg1, T2 arg2, T3 arg3, T4 arg4);
public delegate void Action<in T1, in T2, in T3, in T4, in T5>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5);
public delegate void Action<in T1, in T2, in T3, in T4, in T5, in T6>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6);
public delegate void Action<in T1, in T2, in T3, in T4, in T5, in T6, in T7>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7);
public delegate void Action<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8);
//public delegate void Action<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9);
//public delegate void Action<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, in T10>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10);
//public delegate void Action<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, in T10, in T11>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11);
//public delegate void Action<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, in T10, in T11, in T12>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12);
//public delegate void Action<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, in T10, in T11, in T12, in T13>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13);
//public delegate void Action<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, in T10, in T11, in T12, in T13, in T14>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14);
//public delegate void Action<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, in T10, in T11, in T12, in T13, in T14, in T15>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15);
//public delegate void Action<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, in T10, in T11, in T12, in T13, in T14, in T15, in T16>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15, T16 arg16);
//有返回委托
public delegate TResult Func<out TResult>();
public delegate TResult Func<in T, out TResult>(T arg);
public delegate TResult Func<in T1, in T2, out TResult>(T1 arg1, T2 arg2);
public delegate TResult Func<in T1, in T2, in T3, out TResult>(T1 arg1, T2 arg2, T3 arg3);
public delegate TResult Func<in T1, in T2, in T3, in T4, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4);
public delegate TResult Func<in T1, in T2, in T3, in T4, in T5, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5);
public delegate TResult Func<in T1, in T2, in T3, in T4, in T5, in T6, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6);
public delegate TResult Func<in T1, in T2, in T3, in T4, in T5, in T6, in T7, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7);
public delegate TResult Func<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8);
//public delegate TResult Func<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9);
//public delegate TResult Func<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, in T10, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10);
//public delegate TResult Func<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, in T10, in T11, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11);
//public delegate TResult Func<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, in T10, in T11, in T12, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12);
//public delegate TResult Func<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, in T10, in T11, in T12, in T13, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13);
//public delegate TResult Func<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, in T10, in T11, in T12, in T13, in T14, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14);
//public delegate TResult Func<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, in T10, in T11, in T12, in T13, in T14, in T15, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15);
//public delegate TResult Func<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, in T10, in T11, in T12, in T13, in T14, in T15, in T16, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15, T16 arg16);
}
Delegates.cs
--------------華麗麗的分隔線--------------
下面的內容獻給閒的蛋疼的童鞋,又或者你想鄙視、教育我這裡不該怎樣怎樣,那裡應該怎樣怎樣的話,請看完再繼續。
設計說明:
- 為什麼要把WaitUI弄成靜態類。new一個執行器執行任務,完了銷毀執行器不是很自然的事嗎,弄成靜態類就不怕資源占用、狀態管理不好造成BUG嗎。的確我有考慮過弄成實例類,但思考之後還是決定靜態之,原因有二:①使用簡單。這是從考慮做這個東西之初到實施過程中都始終優先考慮的原則,new一下是費不了多少事,但new完以後是不是得設置某些屬性,得Run,得釋放,像這樣:
using(WaitUI w = new WaitUI())
{
w.CancelControlVisible = true;
w.RunAction(Foo);
}
怎麼都不如一句WaitUI.RunAction(Foo)來的簡單;②不必改造任務方法。想象一下,實例類的話,任務中想更新等待窗體,是不是得獲得執行器實例的引用,或是某個包裝了執行器實例的類的實例,怎麼獲得,自然是通過任務方法的參數傳進去,像這樣:
int Foo(int a, int b, WaitUI w)
{
w.WorkMessage = "";
...
其結果就是必然要改造任務方法,而我的目的一是讓用戶拿去就能用,結果還要改這改那的我都閒害臊;二是讓任務方法既可以套上執行器執行,也可以不帶套照常執行,所以WorkMessage那些屬性都是寫成綠色無公害的,不管有沒有等待窗體,都不會拋異常,目的就是即便任務方法中增加了這些語句,也可以在不帶套的情況下照常執行
- 為什麼要弄個IWaitForm這樣的胖子接口。的確這個地方我承認弄成基類比較合適,除了不至於弄出個胖接口之外,更重要的是可以做一些基礎實現,不至於什麼都交給自定義等待窗體編寫者去實現,省事都是其次,關鍵是能做一些必要的控制,比如UserCancelling事件,要求用戶在點擊取消按鈕和關閉窗體時觸發,但編寫者只在其中一種操作時觸發或根本不觸發那也沒辦法,一句話,過分靈活不是好事。而為什麼我仍然選擇接口,也恰恰是因為要保證靈活,就是要允許編寫者從其它第三方Form繼承,設計美觀的等待窗體,如果設計為基類,那就堵死了這種可能,等於我在靈活性和健壯性之間選擇了前者。當然編寫者可以把WaitFormBase的基類改為目標第三方Form,還有Label、ProgressBar、Button都改為三方控件,反正源碼都在,愛怎麼玩怎麼玩
- WaitForm重寫了Form.OnVisibleChanged方法,是為了屏蔽右上角關閉按鈕,不屏蔽也是可以的,但必須在FormClosing事件中阻止窗體關閉,同時觸發UserCancelling事件,我兩樣的做了,也建議自定義等待窗體編寫者做足全套,因為不屏蔽關閉按鈕的話,用戶點了卻關不掉,感覺怪怪的。另外說說為什麼要弄成事件,而不是弄成一個bool屬性,當用戶取消時置該屬性為true,完了讓WaitUI.UserCancelling直接訪問該屬性,為什麼?原因是這個IWaitForm,我不希望它專供WaitUI使用,其它執行器或類似方案也可以用,那其他方案請求取消任務的操作未必是通過給UserCancelling類似的屬性做標記,人家有可能是執行一個方法,比如BackgroundWorker就是調用CancelAsync()(雖然它內部也是設置標記),那IWaitForm如果是設置某個屬性,就等於要執行器主動來獲取標記,而不是主動通知執行器,顯然對BackgroundWorker這樣的就不好使了,總不能單獨弄個timer循環獲取標記。弄成事件就靈活多了,等待窗體只負責在適當的時候觸發這個事件,至於執行器如何響應這個事件,自行處理,WaitUI可以設置UserCancelling,而BackgroundWorker可以CancelAsync(),各找各媽。另外,WaitForm作為IWaitForm的默認實現,它可以作為自定義等待窗體的實現參考
- 關於WaitUI.IsBusy,這貨請參看使用示例中的注釋,作為公開成員可有可無
最後真心希望路過大蝦對方案不妥之處指點一二,在此謝過。
-文畢-