Как реализовать индикатор выполнения в C #? - PullRequest
20 голосов
/ 11 августа 2009

Как мне реализовать индикатор выполнения и фона для вызовов базы данных в C #?

У меня есть несколько методов, которые работают с большими объемами данных. Это относительно длительные операции, поэтому я хочу добавить индикатор выполнения, чтобы пользователь знал, что что-то на самом деле происходит.

Я подумал об использовании индикатора выполнения или метки строки состояния, но поскольку существует один поток пользовательского интерфейса, поток, в котором выполняются методы обработки базы данных, элементы управления пользовательского интерфейса не обновляются, что делает индикатор выполнения или метку строки состояния бесполезными для меня.

Я уже видел несколько примеров, но они имеют дело с циклами 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);
}

Я ищу лучшие примеры.

Ответы [ 6 ]

18 голосов
/ 11 августа 2009

Некоторым это может не понравиться, но я так и делаю:

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, но он постоянно прокручивается без необходимости обновления. Я использую это для операций с базой данных, которые не сообщают о своем прогрессе.

11 голосов
/ 07 апреля 2010

Если вы не можете узнать прогресс, вы не должны имитировать его, злоупотребляя индикатором выполнения, вместо этого просто отобразите какой-нибудь значок занятости, например en.wikipedia.org/wiki/Throbber#Spinning_wheel Показать его при запуске задачи и скрыть это когда это закончено. Это сделало бы более «честный» GUI.

6 голосов
/ 11 августа 2009

Когда вы выполняете операции в фоновом потоке и хотите обновить пользовательский интерфейс, вы не можете вызывать или устанавливать что-либо из фонового потока. В случае 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 может потребоваться некоторое приведение или вам может не потребоваться помощь, не помню точный синтаксис сейчас.

5 голосов
/ 11 августа 2009

Идея создания отчета о ходе работы с фоновым рабочим заключается в отправке события «Процент завершен». Вы сами несете ответственность за то, чтобы как-то определить, «сколько» работы было выполнено. К сожалению, это часто самая сложная часть.

В вашем случае основная часть работы связана с базой данных. Насколько мне известно, нет способа получить информацию о прогрессе непосредственно из БД. Однако вы можете попытаться динамически разделить работу. Например, если вам нужно прочитать много данных, наивный способ реализовать это может быть.

  • Определите, сколько строк нужно извлечь (ВЫБЕРИТЕ СЧЕТЧИК (*) ОТ ...)
  • Разделите фактическое чтение на более мелкие порции, сообщая о прогрессе каждый раз, когда завершается одна порция:

    for (int i = 0; i < count; i++)
    {
        bgWorker.ReportProgress((100 * i) / count);
        // ... (read data for step i)
    }
    
2 голосов
/ 07 апреля 2010

Я не скомпилировал это, поскольку оно предназначено для доказательства концепции. Вот как я реализовал индикатор Progress для доступа к базе данных в прошлом. В этом примере показан доступ к базе данных SQLite с помощью модуля System.Data.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);
}
0 голосов
/ 24 марта 2017

Это будет полезно. Легко реализовать, 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
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...