2015-10-29 17 views
7

Ich benutze Async/erwarten, um meine Daten asynchron aus der Datenbank zu laden und während des Ladevorgangs möchte ich ein Ladeformular Popup, es ist nur ein einfaches Formular mit laufenden Fortschrittsbalken, um anzuzeigen, dass es einen laufenden Prozess gibt. Nach dem Laden der Daten wird der Dialog automatisch geschlossen. Wie kann ich das erreichen? Im Folgenden finden Sie meine aktuellen Code:Async ShowDialog

protected async void LoadData() 
    { 
     ProgressForm _progress = new ProgressForm(); 
     _progress.ShowDialog() // not working 
     var data = await GetData();   
     _progress.Close(); 
    } 

Aktualisiert:

schaffte ich es, indem Sie den Code zum Laufen zu bringen:

protected async void LoadData() 
     { 
      ProgressForm _progress = new ProgressForm(); 
      _progress.BeginInvoke(new System.Action(()=>_progress.ShowDialog())); 
      var data = await GetData();   
      _progress.Close(); 
     } 

Ist dies der richtige Weg oder gibt es irgendwelche bessere Möglichkeiten?

Danke für Ihre Hilfe.

+1

Ihre aktualisierte Lösung eine mögliche Race-Bedingung hat; 'GetData' könnte theoretisch abgeschlossen sein, bevor das Dialogfeld jemals angezeigt wird; somit würde es unbegrenzt auf dem Bildschirm bleiben (oder, wahrscheinlicher, würden Sie eine "ObjectDisposedException" erhalten). –

+0

Benötigen Sie das Formular, um modal zu sein? –

Antwort

0

ShowDialog() ist ein blockierender Anruf; Die Ausführung wird nicht bis zur await-Anweisung fortgesetzt, bis das Dialogfeld vom Benutzer geschlossen wird. Verwenden Sie stattdessen Show(). Leider ist Ihr Dialogfeld nicht modal, aber es wird den Fortschritt der asynchronen Operation korrekt verfolgen.

+1

Leider muss ich aus irgendwelchen Gründen ShowDialog() verwenden. Trotzdem danke. – davidcoder

5

Es ist einfach, mit Task.Yield, wie diese umzusetzen (WinForms, der Einfachheit halber keine Ausnahmebehandlung):

namespace WinFormsApp 
{ 
    public partial class MainForm : Form 
    { 
     public MainForm() 
     { 
      InitializeComponent(); 
     } 

     async Task<int> LoadDataAsync() 
     { 
      await Task.Delay(2000); 
      return 42; 
     } 

     private async void button1_Click(object sender, EventArgs e) 
     { 
      var progressForm = new Form() { 
       Width = 300, Height = 100, Text = "Please wait... " }; 

      var progressFormTask = progressForm.ShowDialogAsync(); 

      var data = await LoadDataAsync(); 

      progressForm.Close(); 
      await progressFormTask; 

      MessageBox.Show(data.ToString()); 
     } 
    } 

    internal static class DialogExt 
    { 
     public static async Task<DialogResult> ShowDialogAsync(this Form @this) 
     { 
      await Task.Yield(); 
      if (@this.IsDisposed) 
       return DialogResult.OK; 
      return @this.ShowDialog(); 
     } 
    } 
} 

Es ist wichtig, hier zu verstehen, wie der Ausführungsablauf zu einer neuen Nachrichtenschleife verschachtelten springt über (das von der modale Dialog) und kehrt dann zur ursprünglichen Nachrichtenschleife zurück (das ist, was await progressFormTask ist).

-2

könnten Sie versuchen, die folgenden:

protected async void LoadData() 
{ 
    ProgressForm _progress = new ProgressForm(); 
    var loadDataTask = GetData(); 
    loadDataTask.ContinueWith(a => 
     this.Invoke((MethodInvoker)delegate 
     { 
      _progress.Close(); 
     })); 
    _progress.ShowDialog(); 
} 
-1

Betrachten Sie Ihre ProgressForm die Daten laden lassen. Ihre ProgressForm würde eine LoadDataForm, deren Aufgabe es ist, Daten nach dem Laden zu laden, zeigt den Fortschritt des Ladens an den Betreiber und möglicherweise dem Bediener die Möglichkeit zu geben, Laden abzubrechen:

protected void OnButtonLoadData_Clicked(object sender, ...) 
{ 
    using (var loadDataForm = new loadDataForm()) 
    { 
     loadDataForm.LoadingParameters = myLoadingParameters; 
     var dialogResult = loadDataForm.ShowDialog(this); 
     if (dialogResult = DialogResult.Ok) 
     { 
      var loadedData = loadDataForm.LoadedData; 
      ProcessData(loadedData); 
     } 
    } 
} 

auf die Ladedaten geladen form, initialisiere den Fortschrittsbalken, starte einen Task zum Laden der Daten und einen Timer, um den Fortschrittsbalken zu aktualisieren, oder lass den Ladevorgang den Fortschrittsbalken aktualisieren. Sobald das Laden abgeschlossen ist, schließt die Ladedaten-Task das Dialogfeld.

Wenn eine Stornierung erforderlich ist: Erstellen Sie eine CancellationTokenSource, rufen Sie ein Token aus dieser Quelle ab und übergeben Sie das Token als Parameter an die Aufgabe. Nach dem Closing-Ereignis die cancellationTokenSource abbrechen und warten, bis die Aufgabe abgeschlossen ist. Lassen Sie die Aufgabe das Token regelmäßig überprüfen, wenn eine Stornierung angefordert wird.

1

Hier ist ein Formular, das Task.ContinueWith verwendet und soll jede Race-Bedingung mit der Nutzung des modalen ProgressForm vermeiden:

protected async void LoadDataAsync() 
{ 
    ProgressForm _progress = new ProgressForm(); 

    // 'await' long-running method by wrapping inside Task.Run 
    await Task.Run(new Action(() => 
    { 
     // Display dialog modally 
     // - Use BeginInvoke here to avoid blocking 
     // and illegal cross threading exception 
     this.BeginInvoke(new Action(() => 
     { 
      _progress.ShowDialog(); 
     })); 

     // Begin long-running method here 
     LoadData(); 
    })).ContinueWith(new Action<Task>(task => 
    { 
     // Close modal dialog 
     // - No need to use BeginInvoke here 
     // because ContinueWith was called with TaskScheduler.FromCurrentSynchronizationContext() 
     _progress.Close(); 
    }), TaskScheduler.FromCurrentSynchronizationContext()); 
} 
Verwandte Themen