简体   繁体   English

C#如何实现进度条?

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

How do I implement a progress bar and backgroundworker for database calls in C#? C#如何实现数据库调用的进度条和backgroundworker?

I do have some methods that deal with large amounts of data.我确实有一些处理大量数据的方法。 They are relatively long running operations, so I want to implement a progress bar to let the user know that something is actually happening.它们是运行时间相对较长的操作,所以我想实现一个进度条让用户知道某些事情正在发生。

I thought of using progress bar or status strip label, but since there is a single UI thread, the thread where the database-dealing methods are executed, UI controls are not updated, making the progress bar or status strip label are useless to me.我想到了使用进度条或状态条 label,但由于只有一个 UI 线程,执行数据库处理方法的线程,UI 控件不更新,使得进度条或状态条 label 对我来说毫无用处。

I've already seen some examples, but they deal with for-loops, ex:我已经看过一些示例,但它们处理的是 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);
}

I'm looking for better examples.我正在寻找更好的例子。

Some people may not like it, but this is what I do: 有些人可能不喜欢它,但这就是我所做的:

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;
}

The Marquee style requires VisualStyles to be enabled, but it continuously scrolls on its own without needing to be updated. Marquee样式需要启用VisualStyles,但它会不断滚动,无需更新。 I use that for database operations that don't report their progress. 我将其用于不报告其进度的数据库操作。

If you can't know the progress you should not fake it by abusing a progress bar, instead just display some sort of busy icon like en.wikipedia.org/wiki/Throbber#Spinning_wheel Show it when starting the task and hide it when it's finished. 如果你不知道进展你不应该通过滥用进度条来伪造它,而只是显示某种繁忙的图标,如en.wikipedia.org/wiki/Throbber#Spinning_wheel在启动任务时显示它并在它的时候隐藏它完了。 That would make for a more "honest" GUI. 这将使一个更“诚实”的GUI。

When you perform operations on Background thread and you want to update UI, you can not call or set anything from background thread. 当您在后台线程上执行操作并且想要更新UI时,您无法从后台线程调用或设置任何内容。 In case of WPF you need Dispatcher.BeginInvoke and in case of WinForms you need Invoke method. 对于WPF,您需要Dispatcher.BeginInvoke,如果是WinForms,则需要Invoke方法。

WPF: 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: 的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);
    });
}

for WinForms delegate may require some casting or you may need little help there, dont remember the exact syntax now. 对于WinForms委托可能需要一些演员或你可能需要一点帮助,不记得现在的确切语法。

The idea behind reporting progress with the background worker is through sending a 'percent completed' event. 向后台工作人员报告进度的想法是通过发送'完成百分比'事件。 You are yourself responsible for determining somehow 'how much' work has been completed. 你自己负责确定已经完成了多少“工作量”。 Unfortunately this is often the most difficult part. 不幸的是,这通常是最困难的部分。

In your case, the bulk of the work is database-related. 在您的情况下,大部分工作与数据库相关。 There is to my knowledge no way to get progress information from the DB directly. 据我所知,无法直接从DB获取进度信息。 What you can try to do however, is split up the work dynamically. 然而,你可以尝试做的是动态地分解工作。 Eg, if you need to read a lot of data, a naive way to implement this could be. 例如,如果您需要阅读大量数据,可以采用一种天真的方式来实现这一点。

  • Determine how many rows are to be retrieved (SELECT COUNT(*) FROM ...) 确定要检索的行数(SELECT COUNT(*)FROM ...)
  • Divide the actual reading in smaller chunks, reporting progress every time one chunk is completed: 将实际读数除以较小的块,每次完成一个块时报告进度:

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

I have not compiled this as it is meant for a proof of concept. 我没有编译它,因为它是用于概念证明。 This is how I have implemented a Progress bar for database access in the past. 这就是我过去为数据库访问实现进度条的方法。 This example shows access to a SQLite database using the System.Data.SQLite module 此示例显示使用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);
}

This will Helpfull.Easy to implement,100% tested. 这将有助于实现,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
}

You have to execute the process from a thread, and from the thread you invoke the progress bar and change its value, maybe this example helps you您必须从线程执行流程,并从线程调用进度条并更改其值,也许这个示例可以帮助您

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