WPF - Task.Run(() => window.ShowDialog) fails

邮差的信 提交于 2019-12-03 08:50:52

So the "real" question: How can I show a blocking Window when a long running task is running and closing it after the long running task has finished and, eventually, inform the window about the progress of the action.

You've already got most of the pieces; you just need to put them together.

How can I show a blocking Window

All UI should go on a single GUI thread. This isn't strictly necessary, but it's a great simplifier and works for the vast, vast majority of applications. A "blocking Window" is known in the UI world as a "modal dialog", and you show one by calling ShowDialog.

// Start the long-running operation
var task = LongRunningOperationAsync();

// Show the dialog
progressWindow.ShowDialog();

// Retrieve results / propagate exceptions
var results = await task;

closing it after the long running task has finished

For this, you need to wire up the completion of the task to close the window. This is pretty straightforward to do using async/await:

async Task DoOperationAsync(ProgressWindow progressWindow)
{
  try
  {
    await LongRunningOperationAsync();
  }
  finally
  {
    progressWindow.Close();
  }
}

// Start the long-running operation
var task = DoOperationAsync(progressWindow);

// Show the dialog
progressWindow.ShowDialog();

// Retrieve results / propagate exceptions
var results = await task;

inform the window about the progress of the action

Assuming your operation is using the standard IProgress<T> interface for reporting progress:

async Task DoOperationAsync(Window progressWindow, IProgress<int> progress)
{
  try
  {
    await LongRunningOperationAsync(progress);
  }
  finally
  {
    progressWindow.Close();
  }
}

var progressWindowVM = ...;
var progress = new Progress<int>(value =>
{
  progressWindowVM.Progress = value;
});

var task = DoOperationAsync(progressWindow, progress);
progressWindow.ShowDialog();
var results = await task;

Another common use case to consider is the cancelling of the operation if the user closes the progress dialog themselves. Again, this is straightfoward if your operation is already using the standard CancellationToken:

async Task DoOperationAsync(Window progressWindow, CancellationToken token, IProgress<int> progress)
{
  try
  {
    await LongRunningOperationAsync(token, progress);
  }
  catch (OperationCanceledException) { }
  finally
  {
    progressWindow.Close();
  }
}

var progressWindowVM = ...;
var progress = new Progress<int>(value =>
{
  progressWindowVM.Progress = value;
});

var cts = new CancellationTokenSource();
progressWindow.Closed += (_, __) => cts.Cancel();

var task = DoOperationAsync(progressWindow, cts.Token, progress);
progressWindow.ShowDialog();
var results = await task;

The solution should (preferably) work with the MVVM pattern and not rely on (too much) code behind.

MVVM works great within a single window. As soon as you start trying to data-bind window-level actions and attributes, a lot of it falls apart. This is not due to MVVM being a poor pattern, but rather just that a lot of MVVM frameworks do not handle this well.

The example code above only uses data binding to report progress to the progress dialog. If your MVVM framework can data-bind the showing/hiding of a modal window, then you could use my NotifyTaskCompletion type to drive that. Also, some frameworks have a more elegant (MVVM) way to handle Window.Closed, but the details depend on your framework.

The calling thread cannot access this object because a different thread owns it.

This is a very common error and if you had searched online, you would have found a very simple explanation.

You cannot manipulate UI objects on a non UI thread.

The solution is simple. Don't attempt to open a dialog Window on a non UI thread.

Perhaps if you can clarify what your actual question is (by editing your question, not by commenting), then I can help further?

I think I have found a nearly working solution here: Create MVVM Background Tasks with Progress Reporting

The only thing I have to get around with is the deactivation of the main window when showing the dialog.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!