wake-up-neo.net

Wie implementiere ich eine Fortschrittsleiste in C #?

Wie implementiere ich eine Fortschrittsleiste und einen Hintergrund-Worker für Datenbankaufrufe in C #?

Ich habe einige Methoden, die große Datenmengen behandeln. Sie laufen relativ lange, daher möchte ich eine Fortschrittsleiste implementieren, um den Benutzer darüber zu informieren, dass tatsächlich etwas passiert.

Ich dachte an die Verwendung von Fortschrittsbalken oder Statusstreifenbezeichnungen. Da es jedoch einen einzelnen UI-Thread gibt, den Thread, in dem die Datenbank handelnden Methoden ausgeführt werden, die UI-Steuerelemente nicht aktualisiert werden, sind die Fortschrittsbalken oder Statusstreifenbezeichnungen für mich unbrauchbar.

Ich habe bereits einige Beispiele gesehen, die sich aber mit For-Loops befassen, zB:

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

Ich suche nach besseren Beispielen.

20
go-goo-go

Einige Leute mögen es vielleicht nicht, aber das mache ich:

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

Für den Marquee-Stil muss VisualStyles aktiviert sein, es wird jedoch fortlaufend automatisch gescrollt, ohne dass es aktualisiert werden muss. Ich verwende das für Datenbankoperationen, die ihren Fortschritt nicht melden.

18
Kyle Gagnet

Wenn Sie den Fortschritt nicht kennen, sollten Sie ihn nicht durch einen Missbrauch einer Fortschrittsleiste täuschen, sondern zeigen Sie ein belebtes Symbol wie en.wikipedia.org/wiki/Throbber#Spinning_wheel an. Zeigen Sie es beim Start der Aufgabe an und blenden Sie es aus, wenn es läuft fertig. Das würde für eine "ehrlichere" GUI sorgen.

11
user282727

Wenn Sie Vorgänge für einen Hintergrund-Thread ausführen und die Benutzeroberfläche aktualisieren möchten, können Sie nichts vom Hintergrund-Thread aufrufen oder festlegen. Für WPF benötigen Sie Dispatcher.BeginInvoke und für WinForms die Invoke-Methode.

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

für WinForms-Delegate ist möglicherweise ein Casting erforderlich, oder Sie benötigen dort möglicherweise wenig Hilfe. Erinnern Sie sich jetzt nicht an die genaue Syntax.

6
Akash Kava

Hinter dem Bericht über den Fortschritt des Hintergrundarbeiters steht das Senden eines Ereignisses "Prozent abgeschlossen". Sie sind selbst dafür verantwortlich, irgendwie zu bestimmen, wie viel Arbeit abgeschlossen wurde. Leider ist dies oft der schwierigste Teil.

In Ihrem Fall ist der Hauptteil der Arbeit datenbankbezogen. Meines Wissens gibt es keine Möglichkeit, Fortschrittsinformationen direkt von der DB zu erhalten. Sie können jedoch versuchen, die Arbeit dynamisch aufzuteilen. Wenn Sie beispielsweise sehr viele Daten lesen müssen, könnte dies eine naive Methode sein. 

  • Bestimmen Sie, wie viele Zeilen abgerufen werden sollen (SELECT COUNT (*) FROM ...)
  • Teilen Sie den tatsächlichen Messwert in kleinere Abschnitte auf und melden Sie den Fortschritt jedes Mal, wenn ein Abschnitt abgeschlossen ist:

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

Ich habe dies nicht erstellt, da es für einen Proof of Concept gedacht ist. So habe ich in der Vergangenheit eine Fortschrittsleiste für den Datenbankzugriff implementiert. Dieses Beispiel zeigt den Zugriff auf eine SQLite-Datenbank mit dem Modul 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);
}
2
galford13x

Dies wird hilfreich sein. Einfach zu implementieren, 100% getestet.

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
}