yadyn/WPF-Task-Dialog

Sample: progress dialog with async operation

deakjahn opened this issue · 0 comments

The current test samples show nothing about how to use the progress dialog version to do contemporary style async processing with progress (it's basically a modal dialog doing nothing until it returns), so you might want to add something like this to your samples:

A helper class we will use to report status:

public class ProgressArgs {
  public bool DoStart;
  public bool DoEnd;
  public int Percent;
  public string Message;

  public ProgressArgs() {
    DoStart = DoEnd = false;
    Percent = 0;
    Message = string.Empty;
  }

  public ProgressArgs(int percent, string message) {
    DoStart = DoEnd = false;
    Percent = percent;
    Message = message;
  }
}

We can start our async operation like this:

IProgress<ProgressArgs> Progressor;
CancellationTokenSource Canceller;
IActiveTaskDialog ProgressDialog;

Canceller = new CancellationTokenSource();
Progressor = new Progress<ProgressArgs>((args) => {
  if (args.DoStart) {
    TaskDialog.Show(new TaskDialogOptions {
      Owner = ...,
      Title = "...",
      MainInstruction = "...",
      Content = " ",
      CustomButtons = new string[] { "&Cancel", "Close" },
      AllowDialogCancellation = true,
      ShowProgressBar = true,
      EnableCallbackTimer = true,
      Callback = ProgressCallback,
      CallbackData = Canceller,
    });
  }
  else if (args.DoEnd)
    ProgressDialog?.ClickCustomButton(1);
  else {
    ProgressDialog?.SetProgressBarPosition(args.Percent);
    ProgressDialog?.SetContent(args.Message);
  }
});

//Cursor = Cursors.Wait;
Progressor.Report(new ProgressArgs { DoStart = true });
if (!await OperationAsync(Progressor, Canceller)) {
  // canceled
}
Progressor.Report(new ProgressArgs { DoEnd = true });
//Cursor = Cursors.Arrow;

where the callback function is:

private bool ProgressCallback(IActiveTaskDialog dialog, VistaTaskDialogNotificationArgs args, object callbackData) {
  if (args.Notification == VistaTaskDialogNotification.Created) {
    ProgressDialog = dialog;
    dialog.SetProgressBarRange(0, 100);
  }
  else if (args.Notification == VistaTaskDialogNotification.ButtonClicked && args.ButtonIndex == 0) {
    if (callbackData is CancellationTokenSource canceller)
      canceller.Cancel(); 
  }

  return false;
}

We pass the Progressor to OperationAsync() so that it can report back:

public override async Task<bool> OperationAsync(IProgress<ProgressArgs> Progressor, CancellationTokenSource Canceller) {
  return await Task.Run(() => {
    ...
    Progressor.Report(new ProgressArgs(0, "..."));
    // a tight loop will not allow the UI to be refreshed, this gives it some chance to breathe
    // Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, new Action(delegate { }));
    ...
    if (Canceller.IsCancellationRequested)
      return false;
    ...
    Progressor.Report(new ProgressArgs(50, "..."));
    ...
    return true;
  }, Canceller.Token);
}

Note that one of the main reasons to use the Progress object is that it handles the transition between non-UI background tasks and the UI automatically. Not using it (or needing to do more UI stuff than just progress reporting) would require to start the async operation with Task.Factory.StartNew() and to grab the synchronization context ourselves.