2017-11-24 7 views
0

Ich muss meine API aufrufen und warten PictureBox (wait.gif) bis API vollständig und die Daten zurückgeben, um mein benutzerdefiniertes Steuerelement in Tablepanellayout zu binden.C# Windows-Anwendung Bildbox anzeigen, bis Hintergrundprozess beendet

Ich kann BackgroundWorker Klasse hier nicht verwenden, weil es Cross-Thread-Problem mit meinem Untersteuerelement in benutzerdefinierten Steuerelement hat.

Hier habe ich nur eine Idee, das zu tun, ist untergeordnete Thread aus dem Haupt-Thread aufrufen, aber bis es vollständig ausgeführt werden zeigen Sie die PictureBox (wait.gif) und blockieren den Restcode ausgeführt werden.

Kann mir jemand vorschlagen, wie man es genau macht oder bitte geben Sie ein Code-Schnipsel zum Beispiel.

+3

So haben Sie einige seltsame Problem in Ihrem Code, die Sie von der Verwendung eines sehr einfachen Hintergrund-Worker verhindert. Dann entscheiden Sie, dass Sie anstatt es zu reparieren einen Hacky-Hack erstellen, der den Hauptanwendungs-Thread blockiert: z. B. eine Menge potentieller Fehler und seltsame, schwer zu behebende Fehler. Ist es nicht besser, diesen Fehler in Ihrem "Untersteuerelement in benutzerdefiniertem Steuerelement" zu beheben? – rs232

+0

Geben Sie Ihren Code ein. Niemand kann erraten, was mit * deinem * Code falsch ist. Die BGW hat keine Cross-Thread-Probleme. Es soll * Threadübergreifende Probleme verhindern *, indem Fortschrittsereignisse im UI-Thread ausgelöst werden. Wenn Sie Cross-Thread-Probleme haben, ist * Ihr * Code fehlgeschlagen. Höchstwahrscheinlich versuchen Sie, die Benutzeroberfläche innerhalb des 'DoWork'-Ereignisses zu ändern, anstatt ein Fortschrittsereignis zu erhöhen und die Benutzeroberfläche dort zu aktualisieren. –

+0

Alles, was Sie tun müssen, ist den Hintergrund vor dem Aufruf von' RunWorkerAsync' zu ändern und dann im [ RunWorkerCompleted] (https://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.runworkercompleted%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396) Ereignis, das ausgelöst wird der UI-Thread.Wenn Sie verhindern möchten, dass Benutzer die UI verwenden, deaktivieren Sie * einfach * das Formular oder verwenden Sie einen modalen Begrüßungsbildschirm. Nichts mit Threading in diesem Fall zu tun –

Antwort

3

Es gibt zwei Artikel, die mir viel Verständnis Asynchron-await geholfen:

Für WinForms Sie wie folgt vor:

  • Wenn Ihre Datenabfrage-Funktion Asynchron-Versionen, die Daten zu holen hat, diese Funktion zu verwenden. Denken Sie an Funktionen wie SQLConnection.openAsync, Dapper.QueryAsync etc
  • Wenn Ihre Daten querier keine Asynchron-Versionen hat es machen Asynchron mit Task.Run
  • Jede Funktion, die Asynchron-Funktionen aufruft, sollte async selbst
  • Jede Asynchron-Funktion deklariert werden gibt Task anstelle von void und Task<TResult> statt TResult zurück.
  • Die einzige Ausnahme ist der Event-Handler: Diese Funktion gibt void statt
  • void zurück, die Aufrufe an andere asynchrone Funktionen erwartet werden sollen, bevor Ihre asynchronen zurückgibt.

.

// The event handler: make async. The only one that still returns void 
async void OnButton1_clicked(object sender, ...) 
{ 
    // start fetching the data. Don't await yet, you'll have other things to do 
    Task<MyData> fetchDataTask = FetchData(...); 

    // meanwhile: show the user that you are busy: 
    this.ShowBusy(true); // show picture box? 

    // if needed do other things you can do before the data is fetched 
    this.ClearTable(); 

    // once you have nothing meaningful to do, await for your data 
    MyData fetchedData = await fetchDataTask; 
    this.ProcessData(fetchedData); 

    // finished: 
    this.ShowBusy(false); // remove picture box 
} 

Async-Version der Funktion, die die Daten geholt:

async Task<IQueryable<MyData>> FetchDataAsync(myParams) 
{ 
    using (SqlConnection dbConnection = new SqlConnection(...) 
    { 
     // open the connection, don't wait yet: 
     Task taskOpen = sqlCommand.OpenAsync(); 

     // continue while opening: 
     using (var sqlCommand = new SqlCommand(...)) 
     { 
      cmd.Parameters.AddWithValue(...); 

      // before executing the query: wait until OpenAsync finished: 
      await taskOpen; 

      // read the data. If nothing to do: await, otherwise use Task similar to Open 
      SqlDataReader dataReader = await cmd.ExecuteReaderAsync(); 
      foreach (var row in dataReader) 
      { 
       ... (some Await with GetFieldValueAsync 
      } 
     } 
    } 
} 

Ich bin nicht wirklich vertraut mit dem Lesen SQL-Daten auf ein so niedriges Niveau, ziehe ich Entity Framework und adrett, so könnte es ein Fehler in meinem SqlReader-Kram sein. Vielleicht kann jemand das korrigieren. Trotzdem wirst du das Wesentliche verstehen.

Wenn Sie es so schreiben, wird Ihr Programm ziemlich reaktionsbereit sein: Immer wenn die Prozedur auf etwas warten muss, wird die Kontrolle an den Aufrufer der Funktion zurückgegeben, der die Verarbeitung fortsetzen kann, bis er eine Erwartung erfüllt, wenn die Kontrolle ist zurückgegeben an den Anrufer, der die Verarbeitung fortfährt usw.

Beachten Sie, dass dies nicht hilft, wenn Ihr Programm nicht auf etwas wartet. Wenn Ihr Hauptthread einige Sekunden schwere Berechnungen durchführt, reagiert Ihr Programm nicht. Erwägen Sie, eine asynchrone Funktion zu erstellen, die dies für Ihre Task verwendet.Führen Sie

Wenn Sie so programmiert haben, haben alle Threads, die Ihre Funktionen ausführen, den gleichen Kontext: es wird so sein, als wäre nur der UI-Thread beteiligt. Keine Notwendigkeit für Mutexe, Semaphoren, InvokeRequired usw.

Verwandte Themen