簡體   English   中英

C#如何實現進度條?

[英]How do I implement a progress bar in C#?

C#如何實現數據庫調用的進度條和backgroundworker?

我確實有一些處理大量數據的方法。 它們是運行時間相對較長的操作,所以我想實現一個進度條讓用戶知道某些事情正在發生。

我想到了使用進度條或狀態條 label,但由於只有一個 UI 線程,執行數據庫處理方法的線程,UI 控件不更新,使得進度條或狀態條 label 對我來說毫無用處。

我已經看過一些示例,但它們處理的是 for 循環,例如:

for(int i = 0; i < count; i++)
{ 
    System.Threading.Thread.Sleep(70);
    // ... do analysis ...
    bgWorker.ReportProgress((100 * i) / count);
}

private void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    progressBar.Value = Math.Min(e.ProgressPercentage, 100);
}

我正在尋找更好的例子。

有些人可能不喜歡它,但這就是我所做的:

private void StartBackgroundWork() {
    if (Application.RenderWithVisualStyles)
        progressBar.Style = ProgressBarStyle.Marquee;
    else {
        progressBar.Style = ProgressBarStyle.Continuous;
        progressBar.Maximum = 100;
        progressBar.Value = 0;
        timer.Enabled = true;
    }
    backgroundWorker.RunWorkerAsync();
}

private void timer_Tick(object sender, EventArgs e) {
    if (progressBar.Value < progressBar.Maximum)
        progressBar.Increment(5);
    else
        progressBar.Value = progressBar.Minimum;
}

Marquee樣式需要啟用VisualStyles,但它會不斷滾動,無需更新。 我將其用於不報告其進度的數據庫操作。

如果你不知道進展你不應該通過濫用進度條來偽造它,而只是顯示某種繁忙的圖標,如en.wikipedia.org/wiki/Throbber#Spinning_wheel在啟動任務時顯示它並在它的時候隱藏它完了。 這將使一個更“誠實”的GUI。

當您在后台線程上執行操作並且想要更新UI時,您無法從后台線程調用或設置任何內容。 對於WPF,您需要Dispatcher.BeginInvoke,如果是WinForms,則需要Invoke方法。

WPF:

// assuming "this" is the window containing your progress bar..
// following code runs in background worker thread...
for(int i=0;i<count;i++)
{
    DoSomething();
    this.Dispatcher.BeginInvoke((Action)delegate(){
         this.progressBar.Value = (int)((100*i)/count);
    });
}

的WinForms:

// assuming "this" is the window containing your progress bar..
// following code runs in background worker thread...
for(int i=0;i<count;i++)
{
    DoSomething();
    this.Invoke(delegate(){
         this.progressBar.Value = (int)((100*i)/count);
    });
}

對於WinForms委托可能需要一些演員或你可能需要一點幫助,不記得現在的確切語法。

向后台工作人員報告進度的想法是通過發送'完成百分比'事件。 你自己負責確定已經完成了多少“工作量”。 不幸的是,這通常是最困難的部分。

在您的情況下,大部分工作與數據庫相關。 據我所知,無法直接從DB獲取進度信息。 然而,你可以嘗試做的是動態地分解工作。 例如,如果您需要閱讀大量數據,可以采用一種天真的方式來實現這一點。

  • 確定要檢索的行數(SELECT COUNT(*)FROM ...)
  • 將實際讀數除以較小的塊,每次完成一個塊時報告進度:

     for (int i = 0; i < count; i++) { bgWorker.ReportProgress((100 * i) / count); // ... (read data for step i) } 

我沒有編譯它,因為它是用於概念證明。 這就是我過去為數據庫訪問實現進度條的方法。 此示例顯示使用System.Data.SQLite模塊訪問SQLite數據庫

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{   
    // Get the BackgroundWorker that raised this event.
    BackgroundWorker worker = sender as BackgroundWorker;
    using(SQLiteConnection cnn = new SQLiteConnection("Data Source=MyDatabase.db"))
    {
        cnn.Open();
        int TotalQuerySize = GetQueryCount("Query", cnn); // This needs to be implemented and is not shown in example
        using (SQLiteCommand cmd = cnn.CreateCommand())
        {
            cmd.CommandText = "Query is here";
            using(SQLiteDataReader reader = cmd.ExecuteReader())
            {
                int i = 0;
                while(reader.Read())
                {
                    // Access the database data using the reader[].  Each .Read() provides the next Row
                    if(worker.WorkerReportsProgress) worker.ReportProgress(++i * 100/ TotalQuerySize);
                }
            }
        }
    }
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    this.progressBar1.Value = e.ProgressPercentage;
}

private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    // Notify someone that the database access is finished.  Do stuff to clean up if needed
    // This could be a good time to hide, clear or do somthign to the progress bar
}

public void AcessMySQLiteDatabase()
{
    BackgroundWorker backgroundWorker1 = new BackgroundWorker();
    backgroundWorker1.DoWork += 
        new DoWorkEventHandler(backgroundWorker1_DoWork);
    backgroundWorker1.RunWorkerCompleted += 
        new RunWorkerCompletedEventHandler(
    backgroundWorker1_RunWorkerCompleted);
    backgroundWorker1.ProgressChanged += 
        new ProgressChangedEventHandler(
    backgroundWorker1_ProgressChanged);
}

這將有助於實現,100%經過測試。

for(int i=1;i<linecount;i++)
{
 progressBar1.Value = i * progressBar1.Maximum / linecount;  //show process bar counts
 LabelTotal.Text = i.ToString() + " of " + linecount; //show number of count in lable
 int presentage = (i * 100) / linecount;
 LabelPresentage.Text = presentage.ToString() + " %"; //show precentage in lable
 Application.DoEvents(); keep form active in every loop
}

您必須從線程執行流程,並從線程調用進度條並更改其值,也許這個示例可以幫助您

public void main()
    {
        int count = 20;
        progressbar.Maximum = count;
        progressbar.Value = 0;

        new Thread(() => Work(progressbar, count)).Start();
    }
    public static void Work(ProgressBar progressbar, int count)
    {
        for (int i = 0; i <= count; i++)
        {
            Thread.Sleep(70);
            // ... do analysis ...
            Application.Current.Dispatcher.Invoke(new Action(() =>
            {
                progressbar.Value = i;
            }));
            
            
    }
    }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM