Skip to content

值得您信賴的旅遊品牌 | 團體旅遊、自由行的專家‎

機場接送

Menu
  • 首頁
  • 裝潢設計
  • 旅遊天地
  • 環保清潔
Menu

C# 根據BackgroundWorker異步模型和ProgressBar控件,自定義進度條控件_台中搬家

Posted on 2021-08-052021-08-05 by admin

※台中搬家公司費用怎麼算?

擁有20年純熟搬遷經驗,提供免費估價且流程透明更是5星評價的搬家公司

前言

程序開發過程中,難免會有的業務邏輯,或者算法之類產生讓人能夠感知的耗時操作,例如循環中對複雜邏輯處理;獲取數據庫百萬乃至千萬級數據;http請求的時候等……
用戶在使用UI操作並不知道程序的內部處理,從而誤操作導致程序無響應,關閉程序等待影響體驗的情況,因此,在等待過程中提供友好的等待提示是有必要的,接下來
我們一起封裝一個自定義進度條控件!

主要使用技術(C#相關)

  • BackgroundWoker異步模型
  • ProgressBar控件
  • 泛型
  • 定時器 System.Timers.Timer

自定義控件開發

項目解決方案

  • BackgroundworkerEx : 自定義進度條控件工程
  • Test : 調用BackgroundworkerEx的工程(只是展示如何調用)

處理控件樣式

  • 新建一個ProgressbarEx名稱的 用戶控件

  • 添加Labal控件(lblTips),用於展示進度條显示的信息狀態

  • 添加一個PictureBox控件(PicStop),充當關閉按鈕,用於獲取用戶點擊事件,觸發關閉/終止進度條

  • 添加進度條ProgressBar控件(MainProgressBar)

  • 處理代碼如下:

  1. 進度條樣式為”不斷循環”,並且速度為50
  2. 該自定義用戶控件不展示在任務欄中
  3. 圖片控件被點擊事件——>設置當前屬性IsStop=true,指示過程終止;
  4. TipMessage屬性,用於設置進度條的信息
  5. SetProgressValue(int value) 設置進度條的Value屬性,使得在ProgressBarStyle.Marquee樣式中動畫平滑
  6. 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
    1. 釋放資源;
 		/// <summary>
        /// Dispose
        /// </summary>
        public void Dispose()
        {
            try
            {
                DoWork = null;
                RunWorkCompleted = null;
                WorkStoped = null;

                _mWorkerThread = null;
                _mWorker.Dispose();
                _mWorker = null;
                _mTimer = null;
            }
            catch (Exception){}
        }
  1. T用與異步處理的時候,傳遞T類型
  • 因為我們是通過.Net 的 BackgroundWorker異步模型來做的,所以我們理所當然定義相關的事件:
    1. 異步開始
    2. 異步完成
    3. 加上我們自定義擴展的異步停止
    4. ……報告進度事件在此進度條樣式中並不需要

我們先定義這四個事件所用到的參數,因為在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;
  • 定義全局的字段
    1. private BackgroundWorker _mWorker = null;異步操作必要;
    2. private T _mWorkArg = default(T);操作傳遞進來的參數類並且返回到外部
    3. private Timer _mTimer; 定時器檢測自定義進度條控件屬性IsStop是否為true,並且動態修改進度條消息
    4. private Thread _mWorkerThread = null;異步操作在該線程中,終止時調用About()拋出ThreadAbortException異常,用於標記當前是停止而不是完成狀態
    5. private int _miWorkerStartDateSecond = 0; 異步消耗時間(非必要)
    6. private int _miShowProgressCount = 0; 動態显示”.”的個數(非必要)
    7. 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;

  • 定義全局屬性
  1. IsBusy 返回_mWorker的工作忙碌是否
  2. 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一致,

    1. 如果調用處沒有註冊DoWork事件,則直接返回

    2. 將接受到的參數創建成泛型參數類

    3. 開線程,將異步操作放在該線程中操作,注意設置線程的IsBackground=true,防止主進程意外退出,線程還在處理

    4. 循環直到線程結束

    5. 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但是不處理)/線程完成時會進入異步完成事件

    1. 完成后,將自定義進度條控件實例關閉,釋放
  1. 將全局的BackgroundWorker實例_mWorker相關事件取消註冊,並且檢查線程情況
  2. 感覺線程情況,如果線程狀態為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;
          }
      }
  • 線程開始

    1. 檢查消息提醒內容 , {0}{1}同於显示異步耗時和”..”的個數
    2. 在定時器執行方法中,檢查_mfrmProgressForm.IsStop是否為true,這個屬性標誌是否被停止;true則拋出異常
    3. _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星評價的搬家公司

好站推薦

  • 健康醫療 減重知識專區
  • 婚紗世界 婚紗攝影寫真網
  • 成人話題 未滿18請勿進入
  • 流行時尚 時下流行愛美情報
  • 理財資訊 當舖借貸信用卡各式理財方法
  • 生活情報 各行各業情報資訊
  • 科技資訊 工業電子3C產品
  • 網路資訊 新奇趣味爆笑內容
  • 美食分享 全台各式名產 伴手禮
  • 裝潢設計 買屋賣屋裝修一羅框
  • 視覺設計 T恤、團體服、制服、polo衫

近期文章

  • 美擬降大陸鋼品關稅 中鋼回應了
  • 搶標地上權展店 業者複製「全聯模式」
  • 擁雙利多 巧新營運吃補丸
  • 亞德客、氣立 全年營收拚攻頂
  • 交屋入帳衝刺 營建F4業績看旺

標籤

USB CONNECTOR  到府月嫂 南投搬家公司費用 古典家具推薦 台中室內設計 台中搬家 台中搬家公司 台中電動車 台北網頁設計 台東伴手禮 台東名產 地板施工 大圖輸出 如何寫文案 婚禮錄影 宜蘭民宿 家具工廠推薦 家具訂製工廠推薦 家具訂製推薦 實木地板 床墊 復刻家具推薦 新竹婚宴會館 木地板 木質地板 柚木地板 桃園機場接送 桃園自助婚紗 沙發修理 沙發換皮 海島型木地板 潭子電動車 牛軋糖 租車 網站設計 網頁設計 網頁設計公司 貨運 超耐磨木地板 銷售文案 隱形鐵窗 電動車 馬賽克拼貼 馬賽克磁磚 馬賽克磚

彙整

  • 2022 年 5 月
  • 2022 年 4 月
  • 2022 年 3 月
  • 2022 年 2 月
  • 2022 年 1 月
  • 2021 年 12 月
  • 2021 年 11 月
  • 2021 年 10 月
  • 2021 年 9 月
  • 2021 年 8 月
  • 2021 年 7 月
  • 2021 年 6 月
  • 2021 年 5 月
  • 2021 年 4 月
  • 2021 年 3 月
  • 2021 年 2 月
  • 2021 年 1 月
  • 2020 年 12 月
  • 2020 年 11 月
  • 2020 年 10 月
  • 2020 年 9 月
  • 2020 年 8 月
  • 2020 年 7 月
  • 2020 年 6 月
  • 2020 年 5 月
  • 2020 年 4 月
  • 2020 年 3 月
  • 2020 年 2 月
  • 2020 年 1 月
  • 2019 年 12 月
  • 2019 年 11 月
  • 2019 年 10 月
  • 2019 年 9 月
  • 2019 年 8 月
  • 2019 年 7 月
  • 2019 年 6 月
  • 2019 年 5 月
  • 2019 年 4 月
  • 2019 年 3 月
  • 2019 年 2 月
  • 2019 年 1 月
  • 2018 年 12 月
©2022 值得您信賴的旅遊品牌 | 團體旅遊、自由行的專家‎ | Built using WordPress and Responsive Blogily theme by Superb