※台中搬家公司費用怎麼算?
擁有20年純熟搬遷經驗,提供免費估價且流程透明更是5星評價的搬家公司
前言
程序開發過程中,難免會有的業務邏輯,或者算法之類產生讓人能夠感知的耗時操作,例如循環中對複雜邏輯處理;獲取數據庫百萬乃至千萬級數據;http請求的時候等……
用戶在使用UI操作並不知道程序的內部處理,從而誤操作導致程序無響應,關閉程序等待影響體驗的情況,因此,在等待過程中提供友好的等待提示是有必要的,接下來
我們一起封裝一個自定義進度條控件!
主要使用技術(C#相關)
- BackgroundWoker異步模型
- ProgressBar控件
- 泛型
- 定時器 System.Timers.Timer
自定義控件開發
項目解決方案
- BackgroundworkerEx : 自定義進度條控件工程
- Test : 調用BackgroundworkerEx的工程(只是展示如何調用)
處理控件樣式
- 
新建一個ProgressbarEx名稱的 用戶控件 
- 
添加Labal控件(lblTips),用於展示進度條显示的信息狀態 
- 
添加一個PictureBox控件(PicStop),充當關閉按鈕,用於獲取用戶點擊事件,觸發關閉/終止進度條 
- 
添加進度條ProgressBar控件(MainProgressBar) 
- 
處理代碼如下: 
- 進度條樣式為”不斷循環”,並且速度為50
- 該自定義用戶控件不展示在任務欄中
- 圖片控件被點擊事件——>設置當前屬性IsStop=true,指示過程終止;
- TipMessage屬性,用於設置進度條的信息
- SetProgressValue(int value)設置進度條的- Value屬性,使得在- ProgressBarStyle.Marquee樣式中動畫平滑
- MouseDown/- MouseUp/- MouseMove這三個事件是用於拖動無邊框的用戶控件(代碼就不貼了)
public ProgressbarEx()
{
    InitializeComponent();
    MainProgressBar.Style = ProgressBarStyle.Marquee;
    MainProgressBar.MarqueeAnimationSpeed = 50;
    this.ShowInTaskbar = false;
    PicStop.Click += (s, eve) =>
    {
    IsStop = true;
    };
    this.MouseDown += CusProgressForm_MouseDown;
    this.MouseUp += CusProgressForm_MouseUp;
    this.MouseMove += CusProgressForm_MouseMove;
}
/// <summary>
/// Need Stop ?
/// </summary>
public bool IsStop { get; private set; } = false;
/// <summary>
/// TipMessage
/// </summary>
public string TipMessage { get; set; }
/// <summary>
/// TipMessage
/// </summary>
public string TipMessage
{
    get
    {
    return lblTips.Text;
    }
    set
    {
    lblTips.Text = value;
    }
}
/// <summary>
/// Set ProgressBar value ,which makes ProgressBar smooth
/// </summary>
/// <param name="value"></param>
public void SetProgressValue(int value)
{
    if (MainProgressBar.Value == 100) MainProgressBar.Value = 0;
    MainProgressBar.Value += value;
}
到現在,這個自定義進度條控件的樣式基本完成了.
功能邏輯處理
運行前所需
- 定義BackgroundWorkerEx<T>泛型類,並且繼承於IDisposable- 釋放資源;
 
 		/// <summary>
        /// Dispose
        /// </summary>
        public void Dispose()
        {
            try
            {
                DoWork = null;
                RunWorkCompleted = null;
                WorkStoped = null;
                _mWorkerThread = null;
                _mWorker.Dispose();
                _mWorker = null;
                _mTimer = null;
            }
            catch (Exception){}
        }
- T用與異步處理的時候,傳遞- T類型
- 因為我們是通過.Net 的 BackgroundWorker異步模型來做的,所以我們理所當然定義相關的事件:- 異步開始
- 異步完成
- 加上我們自定義擴展的異步停止
- ……報告進度事件在此進度條樣式中並不需要
 
我們先定義這四個事件所用到的參數,因為在BackgroundWorkerEx<T>泛型類中,我們還是使用BackgroundWorker來處理異步過程,因此我們定義的參數泛型類需要繼承原來的參數類型,並且在傳輸傳遞中,將原生BackgroundWorker的Argument,Result屬性轉成全局的泛型T,這樣我們在外部調用的時候,拿到的返回結果就是我們傳入到BackgroundWorkerEx<T>泛型類中的T類型,而不需要使用as進行轉換; 注:因為原生沒有停止相關事件,所以自定義異步停止的事件參數使用的是DoWorkEventArgs<T>
    public class DoWorkEventArgs<T> : DoWorkEventArgs
    {
        public new T Argument { get; set; }
        public new T Result { get; set; }
        public DoWorkEventArgs(object argument) : base(argument)
        {
            Argument = (T)argument;
        }
    }
    public class RunWorkerCompletedEventArgs<T> : RunWorkerCompletedEventArgs
    {
        public new T Result { get; set; }
        public RunWorkerCompletedEventArgs(object result, Exception error, bool cancelled) : base(result, error, cancelled)
        {
            Result = (T)result;
        }
    }
接着我們需要去定義事件,參數使用以上定義的泛型類
	public delegate void DoWorkEventHandler(DoWorkEventArgs<T> Argument);
        /// <summary>
        /// StartAsync
        /// </summary>
        public event DoWorkEventHandler DoWork;
        public delegate void StopEventHandler(DoWorkEventArgs<T> Argument);
        /// <summary>
        /// StopAsync
        /// </summary>
        public event StopEventHandler WorkStoped;
        public delegate void RunWorkCompletedEventHandler(RunWorkerCompletedEventArgs<T> Argument);
        /// <summary>
        /// FinishAsync
        /// </summary>
        public event RunWorkCompletedEventHandler RunWorkCompleted;
- 定義全局的字段
- private BackgroundWorker _mWorker = null;異步操作必要;
- private T _mWorkArg = default(T);操作傳遞進來的參數類並且返回到外部
- private Timer _mTimer;定時器檢測自定義進度條控件屬性- IsStop是否為- true,並且動態修改進度條消息
- private Thread _mWorkerThread = null;異步操作在該線程中,終止時調用- About()拋出- ThreadAbortException異常,用於標記當前是停止而不是完成狀態
- private int _miWorkerStartDateSecond = 0;異步消耗時間(非必要)
- private int _miShowProgressCount = 0;動態显示”.”的個數(非必要)
- private ProgressbarEx _mfrmProgressForm = null;自定義進度條控件實例
 
        /// <summary>
        /// .Net  BackgroundWorker
        /// </summary>
        private BackgroundWorker _mWorker = null;
        /// <summary>
        /// Whole Para
        /// </summary>
        private T _mWorkArg = default(T);
        /// <summary>
        /// Timer
        /// </summary>
        private Timer _mTimer = null;
        /// <summary>
        /// WorkingThread
        /// </summary>
        private Thread _mWorkerThread = null;
        /// <summary>
        /// Async time sec
        /// </summary>
        private int _miWorkerStartDateSecond = 0;
        /// <summary>
        /// Async time dot
        /// </summary>
        private int _miShowProgressCount = 0;
        /// <summary>
        /// ProgressbarEx
        /// </summary
        private ProgressbarEx _mfrmProgressForm = null;
- 定義全局屬性
- IsBusy返回- _mWorker的工作忙碌是否
- ProgressTip自定義進度條控件显示內容
	/// <summary>
        /// Express Busy
        /// </summary>
        public bool IsBusy
        {
            get
            {
                if (_mWorker != null)
                {
                    return _mWorker.IsBusy;
                }
                return false;
            }
        }
        /// <summary>
        /// 進度條提示 默認: 正在加載數據,請稍後[{0}]{1}
        /// </summary>
        public string ProgressTip { get; set; } = "Elapsed Time[{0}]{1}";
**到現在,我們已經將必要的字段,屬性,樣式都處理完成!!! ** 接下來我們就要實現方法
※台中搬家遵守搬運三大原則,讓您的家具不再被破壞!
台中搬家公司推薦超過30年經驗,首選台中大展搬家
方法實現
- 
異步工作事件,用法與 BackgroundWorker一致,- 
如果調用處沒有註冊 DoWork事件,則直接返回
- 
將接受到的參數創建成泛型參數類 
- 
開線程,將異步操作放在該線程中操作,注意設置線程的 IsBackground=true,防止主進程意外退出,線程還在處理
- 
循環直到線程結束 
- 
e.Result = Argument.Result;將結果賦予Result,在停止或者完成事件中可以獲取到結果
 
- 
		/// <summary>
        /// Working
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Worker_DoWork(object sender, DoWorkEventArgs e)
        {
            if (DoWork == null)
            {
                e.Cancel = true;
                return;
            }
            DoWorkEventArgs<T> Argument = new DoWorkEventArgs<T>(e.Argument);
            try
            {
                if (_mWorkerThread != null && _mWorkerThread.IsAlive)
                {
                    _mWorkerThread.Abort();
                }
            }
            catch (Exception)
            {
                Thread.Sleep(50);
            }
            _mWorkerThread = new Thread(a =>
            {
                try
                {
                    DoWork?.Invoke(a as DoWorkEventArgs<T>);
                }
                catch (Exception)
                {
                }
            });
            _mWorkerThread.IsBackground = true;
            _mWorkerThread.Start(Argument);
            //Maybe cpu do not start thread
            Thread.Sleep(20);
            //Wait.....
            while (_mWorkerThread.IsAlive)
            {
                Thread.Sleep(50);
            }
            e.Result = Argument.Result;
        }
- 
異步完成/停止 當線程停止拋出異常(catch但是不處理)/線程完成時會進入異步完成事件 - 完成后,將自定義進度條控件實例關閉,釋放
 
- 將全局的BackgroundWorker實例_mWorker相關事件取消註冊,並且檢查線程情況
- 感覺線程情況,如果線程狀態為ThreadState.Aborted意味着線程被停止了,調用停止事件,否則調用完成事件
	  /// <summary>
      /// Completed
      /// </summary>
      /// <param name="sender"></param>
      /// <param name="e"></param>
      private void Worker_RunWorkCompleted(object sender, RunWorkerCompletedEventArgs e)
      {
          try
          {
              if (_mfrmProgressForm != null)
              {
                _mfrmProgressForm.Close();
                  _mfrmProgressForm.Dispose();
                _mfrmProgressForm = null;
              }
                if (_mWorker != null)
                {
                    _mWorker.DoWork -= Worker_DoWork;
                    _mWorker.RunWorkerCompleted -= Worker_RunWorkCompleted;
                    try
                    {
                        if (_mWorkerThread != null && _mWorkerThread.IsAlive) _mWorkerThread.Abort();
                    }
                    catch (Exception) { }
                }
              //In timer, When stop progress will make thread throw AbortException
              if (_mWorkerThread != null && _mWorkerThread.ThreadState == ThreadState.Aborted)
            {
                  WorkStoped?.Invoke(new DoWorkEventArgs<T>(_mWorkArg));
              }
              else
              {
                  RunWorkCompleted?.Invoke(new RunWorkerCompletedEventArgs<T>(e.Result, e.Error, e.Cancelled));
              }
          }
          catch (Exception ex)
          {
              throw ex;
          }
      }
- 
線程開始 - 檢查消息提醒內容 , {0}{1}同於显示異步耗時和”..”的個數
- 在定時器執行方法中,檢查_mfrmProgressForm.IsStop是否為true,這個屬性標誌是否被停止;true則拋出異常
- _mfrmProgressForm不為- Null則不斷修改當前的內容提醒,友好化,實際可以按需處理
 /// <summary> /// Timer Start /// </summary> private void StartTimer() { //Check user ProgressTip if (!ProgressTip.Contains("{0}")) { ProgressTip += "...Elapsed Time{0}{1}"; } if (_mTimer != null) return; //On one sec _mTimer = new Timer(1000); _mTimer.Elapsed += (s, e) => { //progress and it's stop flag (picture stop)|| this stop flag if (_mfrmProgressForm != null && _mfrmProgressForm.IsStop) { if (_mWorker != null) { try { if (_mWorkerThread != null && _mWorkerThread.IsAlive) { if (_mTimer != null && _mTimer.Enabled) { _mTimer.Stop(); _mTimer = null; } _mWorkerThread.Abort(); } } catch (Exception) { } } } if (_mfrmProgressForm != null) { //Callback _mfrmProgressForm.Invoke(new Action<DateTime>(elapsedtime => { DateTime sTime = elapsedtime; //worked time _miWorkerStartDateSecond++; if (_mfrmProgressForm != null) { _mfrmProgressForm.SetProgressValue(_miWorkerStartDateSecond); } //.....count _miShowProgressCount++; if (_miShowProgressCount > 6) { _miShowProgressCount = 1; } string[] strs = new string[_miShowProgressCount]; string ProgressStr = string.Join(".", strs); string ProgressText = string.Format(ProgressTip, _miWorkerStartDateSecond, ProgressStr); if (_mfrmProgressForm != null) { _mfrmProgressForm.TipMessage = ProgressText; } }), e.SignalTime); } }; if (!_mTimer.Enabled) { _mTimer.Start(); } }
- 
最後一步:異步開始 與 BackgroundWorker用法一致,只是在最後開始了定時器和進度條控件而已/// <summary> /// Start AsyncWorl /// </summary> /// <param name="Para"></param> public void AsyncStart(T Para) { //if workeven is null ,express user do not regist event if (DoWork == null) { return; } _miWorkerStartDateSecond = 0; _miShowProgressCount = 0; //init if (_mWorker != null && _mWorker.IsBusy) { _mWorker.CancelAsync(); _mWorker = null; } _mWorker = new BackgroundWorker(); //create progressbar _mfrmProgressForm = new ProgressbarEx(); //add event _mWorker.DoWork += Worker_DoWork; _mWorker.RunWorkerCompleted += Worker_RunWorkCompleted; _mWorker.WorkerReportsProgress = true; _mWorker.WorkerSupportsCancellation = true; //Set Whole Para _mWorkArg = Para; _mWorker.RunWorkerAsync(Para); //Start timer StartTimer(); _mfrmProgressForm.StartPosition = FormStartPosition.CenterParent; _mfrmProgressForm.ShowDialog(); }
到這裏,整個的進度條控件已經完成了!
調用
- 定義一個參數類
	/// <summary>
    /// Para Class
    /// </summary>
    public class ParaArg
    {
        public DataTable Data { get; set; }
        public string Msg { get; set; }
        public Exception Ex { get; set; }
    }
- 定義全局的幫助類BackgroundWorkerEx<ParaArg> workHelper = null;
- 調用
				if (workHelper != null || (workHelper != null && workHelper.IsBusy))
                {
                    workHelper.Dispose();
                    workHelper = null;
                }
                if (workHelper == null)
                {
                    workHelper = new BackgroundWorkerEx<ParaArg>();
                }
                workHelper.DoWork += (eve) =>
                {
                    ParaArg args = eve.Argument;
                    try
                    { 
                        //ToDo  like Thread.Sleep(20000);
                        Thread.Sleep(10000);
                        args.Msg = "...this is bussiness code result";
                        throw new Exception("");
                    }
                    catch (Exception ex)
                    {
                        args.Ex = ex;
                    }
                    finally
                    {
                        eve.Result = args;
                    }
                };
                workHelper.RunWorkCompleted += (eve) =>
                {
                    if (eve.Error != null)
                    {
                        //get .net backgroundworker exception;
                        //handle this exception;
                        //return ?
                    }
                    //get your para result
                    ParaArg x = eve.Result;
                 
                    if (x.Ex != null)
                    {
                        //get your bussiness exception;
                        //handle this exception;
                        //return ?
                    }
                    //finially get your need;
                    //MayBe to do some UI hanlde and bussiness logical
                    string sReusltMsg = x.Msg;
                };
                workHelper.WorkStoped += (eve) =>
                { 
                    //if stoped ! it means no error;
                    //just get what you want; 
                    ParaArg x = eve.Result as ParaArg;
                    btnBegin.Enabled = true;
                };
                //參數
                ParaArg arg = new ParaArg()
                {
                    Msg = "Msg"
                };
                workHelper.AsyncStart(arg);
最後
其實不管是封裝的過程,還是調用,可以說完全就是BackgroundWorker的方式,所以很多BackgroundWorker相關的地方我都沒有很詳細的去說明;只要看看這個異步模型,就能夠很好理解!大家有空也可以實現以下,有問題也可以詳細,我比較喜歡交流技術~~~
還有一點就是這個解決方案已經放上Github上了,歡迎大家拉下來用
- GitHub地址 歡迎star/fork
本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
※台中搬家公司費用怎麼算?
擁有20年純熟搬遷經驗,提供免費估價且流程透明更是5星評價的搬家公司
