1. 程式人生 > >C#Winform進度條的使用

C#Winform進度條的使用

一個好的進度條能有效的改善使用者體驗。當然,前提是進度條做得好才行,做得差作用就正好相反了,可能造成程式假死、無法關閉等。

下面是一個真例項子,我自己寫程式碼的經驗(由於我使用進度條都是自己琢磨,所以和網上的例子不太一樣)。

以下是我一年前寫的進度條,當時剛學C#,所以寫出的進度條窗體效果非常差,正好做一個反例。

        public int current = 0;//當前值
        public int max = 100;//最大值
        
        public ProgressForm()
        {

            InitializeComponent();

        }
        //迴圈跑進度條
        public void pand()
        {
            int i, old = 0;
            do
            {


                if (current != old)
                {
                    for (i = 0; i < 10; i++)
                    {
                        this.progressBar1.Value += 1;
                        this.progressBar1.Update();
                        Application.DoEvents();
                    }
                    old = current;
                }
                this.progressBar1.Update();
                Application.DoEvents();
                if (current == 0)
                {
                    this.label1.Text = "正在寫入論文資訊,請稍等...";
                }
                else
                {
                    this.label1.Text = "正在寫入論文正文,請稍等...";
                }
            }
            while (current != max);
            while (this.progressBar1.Value != this.progressBar1.Maximum)
            {
                this.progressBar1.Value += 1;
                this.progressBar1.Update();
                Application.DoEvents();

            }
        }
        //窗體顯示時的事件
        private void ProgressForm_Shown(object sender, EventArgs e)
        {
            this.progressBar1.Maximum = max * 10;
            this.progressBar1.Value = 0;
            pand();
            this.Close();
        }

好吧,程式碼非常繁瑣,我稍微解釋一下這段程式碼以及當時我的想法:

我定義了兩個公共變數:max存後臺需要完成任務任務數,current存當前完成了第幾項任務。然後pand()方法負責迴圈檢測current的值,然後給進度條增加值,當current等於max時停止迴圈關閉進度條窗體,代表任務完成。

                        while (this.progressBar1.Value != this.progressBar1.Maximum)
            {
                this.progressBar1.Value += 1;
                this.progressBar1.Update();
                Application.DoEvents();

            }

這段程式碼的作用是讓進度條跑的時候不跳格,類似this.progressBar1.PerformStep();方法(當時我並不知道進度條有這個方法)。下面說一下這段程式碼存在的問題,首先使用無限迴圈會造成程式假死現象(就是跑進度的時候無法關閉進度條窗體),這非常影響使用者體驗,其次是程式碼繁瑣,明明是非常簡單的事,寫得那麼繁瑣,既影響閱讀又影響效能,最後是進度條的畫面和Value不同步(即每次進度條的Value已經加完了,進度條還沒跑完),導致使用者看到的效果是進度還沒跑完,進度條就關了。

下面看一下我修改後的進度條窗體程式碼:

        private int _current = 0;
        /// <summary>
        /// 當前值
        /// </summary>
        public int Current
        {
            get { return _current; }
            set
            {
                _current = value;
                AddValue();
            }
        }
        private int _max = 100;
        /// <summary>
        /// 最大值
        /// </summary>
        public int Max
        {
            get { return _max; }
            set 
            { 
                _max = value;
                this.progressBar1.Maximum = (_max - 1) * 10;
                this.progressBar1.Value = 0;
            }
        }
        MainForm father;
        public ProgressForm(MainForm main)
        {
            father = main;
            InitializeComponent();
            this.label1.Text = "正在寫入論文資訊,請稍等...";
        }

        /// <summary>
        /// 給進度條加值的方法
        /// </summary>
        private void AddValue()
        {
            if (_current > 0)
            {
                this.label1.Text = "正在寫入論文正文,請稍等...";
            }
            this.progressBar1.PerformStep();
            if (this._current * 10 > this.progressBar1.Maximum)
            {
                this.DialogResult = System.Windows.Forms.DialogResult.OK;
                this.Close();

            }

        }
        /// <summary>
        /// 取消按鈕的方法
        /// </summary>
        private void Cancel()
        {
            father.CreateThesisIsRuning = false;
            progressBar1.Style = ProgressBarStyle.Marquee;
            label1.Text = "取消中,請稍等...";
            while (true)
            {
                if (!father.CreateThesisTh.IsAlive)
                    break;
                Application.DoEvents();
            }
        }
        /// <summary>
        /// 取消按鈕事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void buttonX1_Click(object sender, EventArgs e)
        {
            Cancel();
        }

同樣是需要當前值和最大值變數,不同的是我把這兩個欄位封裝起來了,AddValue()方法負責給進度條增加值,Cancel()方法負責執行使用者點選取消按鈕的操作。

在設定Current值時執行AddValue()方法,AddValue()方法中使用了this.progressBar1.PerformStep(),這個方法的作用是按照設定好的this.progressBar1.Step值給進度條增加值,比如Step=10,呼叫這個方法就相當於給進度條新增10次值,每次增加1,就不會造成進度條跳躍格數過大,提升使用者體驗。

使用者在點選取消按鈕時,就設定後臺程序CreateThesisIsRuning=false,使程序停止,由於程序正常可能停止需要一定的時間,所以用一個迴圈去檢測CreateThesisTh.IsAlive的值判斷程序是否結束,此時還要修改進度條型別為ProgressBarStyle.Marquee,Application.DoEvents()可以避免迴圈卡死。

然後針對進度不同步,在設定Max值得時候,this.progressBar1.Maximum = (_max - 1) * 10,將進度條的最大值設定成max最大值-10,然後最後判斷進度條是否結束時使用this._current * 10 > this.progressBar1.Maximum去判斷,這樣就可以使進度條畫面,原理是進度條畫面更新比較慢,所以減小進度條的最大值,讓進度條先跑完再執行完任務,造成進度條在等任務,雖然還是不同步,但是在使用者體驗上效果好多了。

下面看一下效果:

跑條中.png

圖1 任務進行中

取消中.png

圖2 任務取消中

以上是我自己想的一個進度條方案,如有雷同,純屬巧合,歡迎大家指正。