2016-03-30 6 views
0

Ich habe eine C# WinForms-Anwendung mit einem Tab-Steuerelement und mehrere Registerkarten. Eine der Registerkarten enthält ein Datenraster-Steuerelement - es enthält nur etwa 10 Elemente, aber die Daten werden durch Abfragen mehrerer Server aufgefüllt und laden sich daher langsam auf.Warte Nachricht beim Auffüllen eines DataGrid-Steuer

Wenn ich meine Anwendung ausführen und die Registerkarte mit dem DataGrid-Steuerelement auswählen, scheint die Anwendung zu hängen, während sie versucht, alle Server abzufragen und das Raster zu füllen.

Anstatt aufzuhängen, möchte ich, dass die Anwendung reagiert und eine "Bitte warten ..." - Meldung anzeigt, die verschwindet, nachdem das Datagrid aufgefüllt wurde.

Was ich versucht habe, einen Hintergrund Arbeiter als solcher zu tun zu schaffen ist:

if (tabctrl.SelectedTab == tabctrl.TabPages["tabServices"]) 
{ 

    this.dgrdServices.RowPrePaint += new DataGridViewRowPrePaintEventHandler(dgrdServices_RowPrePaint); 
    this.dgrdServices.CellContentClick += new DataGridViewCellEventHandler(dgrdServices_CellClick); 

    BackgroundWorker bw = new BackgroundWorker(); 
    lblLoading.Visible = true; 
    bw.RunWorkerAsync(); 
    bw.DoWork += new DoWorkEventHandler(bw_DoWork); 
    bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted); 


} 

private void bw_DoWork(object sender, DoWorkEventArgs e) 
{ 

    PopulateServicesDataGrid(); 
} 

private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
{ 
    lblLoading.Visible = false; 
} 

private void PopulateServicesDataGrid() 
{ 
    int x = 0; 

    foreach (Service Service in Globals.Services) 
    { 
     // Add a row to the datagrid for each service 
     this.dgrdServices.Rows.Add(); 

     // Update the current service status 
     Service.Status = Service.Query(Service.Server, Service.Name); 
     if (Service.Status == "running") 
     { 
      this.dgrdServices.Rows[x].Cells[0].Value = Properties.Resources.green_dot; 
      this.dgrdServices.Rows[x].Cells[4].Value = Properties.Resources.stop_enabled; 
     } 
     else 
     { 
      this.dgrdServices.Rows[x].Cells[0].Value = Properties.Resources.grey_dot; 
      this.dgrdServices.Rows[x].Cells[4].Value = Properties.Resources.start_enabled; 
     } 

     this.dgrdServices.Rows[x].Cells[1].Value = Service.Server.ToUpper(); 
     this.dgrdServices.Rows[x].Cells[2].Value = Service.FreindlyName; 
     this.dgrdServices.Rows[x].Cells[3].Value = Service.Status; 
     this.dgrdServices.Rows[x].Cells[5].Value = "Uninstall"; 
     this.dgrdServices.Rows[x].Cells[6].Value = Service.Name; 
     x++; 
    } 
} 

PopulateServicesDataGrid() enthält Code, der iteriert durch einige Objekte und Abfragen mehrere verschiedene Server für den Service-Status.

Wenn ich versuche, obiges auszuführen, wird das Raster jedoch nicht ausgefüllt. Wenn ich keinen Hintergrund-Worker verwende und PopulateServicesDataGrid direkt aufruft, funktioniert es (obwohl die App hängt).

Warum funktioniert der Hintergrundarbeiter/Datagrid nicht?

+1

Sie müssen im e-Parameter nach Fehlern im RunWorkerCompleted-Ereignis suchen. Das Ausführen von RunWorkerAsync() vor dem Anhängen der Ereignisse sieht einfach komisch aus. Wenn Sie Ihr PopulateServicesDataGrid nicht posten, helfen Sie uns nicht weiter. – LarsTech

+0

Der Hintergrund-Worker ist ein separater Prozess und wird nicht im Raster angezeigt. Die Abfragen an den Server sollten im Hintergrund-Worker sein, wo der Hintergrundarbeiter eine DataTable zurückgibt. Füllen Sie anschließend das DataGrid mit der zurückgegebenen DataTable aus. – jdweng

+0

PopulateServiceDataGrid veröffentlicht. – Brad

Antwort

1

In Ihrem PopulateServicesDataGrid stelle ich mir vor, dass Sie mit einem UI-Steuerelement interagieren, was nicht funktioniert, weil der Hintergrund-Worker in einem anderen Thread als Ihrem UI-Kontext arbeitet. Sie müssen einen Mechanismus entwickeln, der die Arbeit so ausführt, dass die Informationen zurückgegeben werden, die Sie in das Grid und dann wieder in Ihren Kontext für Benutzeroberflächen-Threads einfügen möchten (RunWorkerCompleted). Füllen Sie das Raster mit den Informationen aus, mit denen Sie kommen Arbeite.

Jedes Mal, wenn Sie einen Hintergrund-Worker verwenden, müssen Sie Ihre Interaktionen mit den UI-Steuerelementen aufteilen und nachdem der Hintergrundarbeiter die Interaktion mit Ihrer Benutzeroberfläche fortgesetzt hat.

Sie hängen die Ereignisse auch nach dem Aufruf von RunWorkerAsync an, verknüpfen Sie zuerst Ihre Ereignisse und dann RunWorkerAsync.

bearbeiten reflektieren Kommentar mit einem Beispiel:

Raute Beispiel dafür, wie Sie dies tun könnten, basierend auf dem Code, den ich sehe.

private void bw_DoWork(object sender, DoWorkEventArgs e) 
{ 
    QueryServices() 
} 

private void QueryServices() 
{ 
    foreach (Service Service in Globals.Services) 
    { 
     Service.Status = Service.Query(Service.Server, Service.Name); 
    } 
} 

    private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
    { 
     PopulateServicesDataGrid(); 
     lblLoading.Visible = false; 
    } 

    private void PopulateServicesDataGrid() 
    { 
     //Do everything else you are doing originally in this method minus the Service.Query calls. 
    } 
+0

Moved RunWorkerAsync zum Beenden - danke! Irgendwelche Vorschläge, wie Sie die Informationen an das Grid zurückgeben können? Ich habe Multithreading noch nicht gemacht – Brad

+0

@Brad Es sieht so aus, als ob Sie bereits eine Sammlung in Globals.Services haben, es ist eine Art von seltsamen Art zu bevölkern, aber das ist, was Sie arbeiten müssen. Sie können Ihre Foreach fortführen und den Abfragebefehl auf jedem einzelnen durchführen. Übernehmen Sie in dem abgeschlossenen Ereignis diese und führen Sie die UI-basierte Logik aus, die Sie bereits in der Methode haben. Dies setzt voraus, dass Service.Status seinen Status behält, nachdem der Worker fertiggestellt wurde, wobei ich nicht 100% des aktuell angezeigten Codes angegeben habe. – davidallyoung

+0

Entschuldigung, ich habe deine Antwort etwa ein Dutzend Mal gelesen. Ich bin mir nicht sicher, ob ich dem folge, was du vorschlägst. Könnten Sie das ausarbeiten oder klären? Vielleicht mit einem Beispiel? Es tut uns leid! – Brad

1

Methode bw_DoWork läuft in einem anderen Thread von ThreadPool. Der Zugriff auf WinForms-Objekte von anderen Threads erfordert eine Synchronisierung. Der beste Weg dies zu tun - verwenden Sie AsyncOperationManager. Sie sollten AsyncOperation im GUI-Thread erstellen und in PopulateServicesDataGrid verwenden, um Ergebnisse zu senden oder zu veröffentlichen.

Eine andere Möglichkeit - DataGrid durch vorbereitete Daten in bw_RunWorkerComplete aktualisieren - es ist bereits von BackgroundWorker Komponente synchronisiert.

Modernerer Weg, um das gleiche zu tun - Async-Aufgaben zu verwenden, aber es erfordert grundlegende Kenntnisse der TPL.

Verwandte Themen