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

I have to implement busy indication and progress reporting. The constraint is, that I have to use the provided Control Library, which offers a Window for progress reporting.
The following code works fine, but does not block the UI, which in some times is required.
private async void StartLongRunningTask2Sync() {
var wndHandle = Application.Current.Windows.OfType<Window>().SingleOrDefault(x => x.IsActive);
if (wndHandle == null)
{
return;
}
IntPtr windowHandle = new WindowInteropHelper(wndHandle).Handle;
var progressWindow = new ProgressBarCustomized(windowHandle)
{
Value = 0, CanCancel = true, CanRetry = false, Thumbnail = null, IsIndeterminate = true
};
progressWindow.Show();
await Task.Run(() => this.longRunningTaskComponent.DoLongRunningTask(this.taskIterations, this.iterationSleepTime));
progressWindow.Close();
}
The following code, which blocks the UI would work so far that the dialog is opened, but the long running task never gets executed until the dialog is closed again:
private async void StartLongRunningTask2Sync() {
var wndHandle = Application.Current.Windows.OfType<Window>().SingleOrDefault(x => x.IsActive);
if (wndHandle == null)
{
return;
}
IntPtr windowHandle = new WindowInteropHelper(wndHandle).Handle;
var progressWindow = new ProgressBarCustomized(windowHandle)
{
Value = 0, CanCancel = true, CanRetry = false, Thumbnail = null, IsIndeterminate = true
};
progressWindow.ShowDialog();
await Task.Run(() => this.longRunningTaskComponent.DoLongRunningTask(this.taskIterations, this.iterationSleepTime));
progressWindow.Close();
}
So I tried with this approach:
private async void StartLongRunningTask2Sync() {
var wndHandle = Application.Current.Windows.OfType<Window>().SingleOrDefault(x => x.IsActive);
if (wndHandle == null)
{
return;
}
IntPtr windowHandle = new WindowInteropHelper(wndHandle).Handle;
var progressWindow = new ProgressBarCustomized(windowHandle)
{
Value = 0, CanCancel = true, CanRetry = false, Thumbnail = null, IsIndeterminate = true
};
Task.Run(() => progressWindow.ShowDialog());
await Task.Run(() => this.longRunningTaskComponent.DoLongRunningTask(this.taskIterations, this.iterationSleepTime));
progressWindow.Close();
}
When doing this, I get the following error:
The calling thread cannot access this object because a different thread owns it.
After investigation of the custom progress window I found out, that the call "base.ShowDialog()" throws this error.
Is there a way to do what I like or do I have to do this with a totally different approach?
Best regards
UPDATE:
Yes, I have searched for this error and yes, I have tried several approaches with Dispatcher.Invoke() etc...
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. The solution should (preferably) work with the MVVM pattern and not rely on (too much) code behind.

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.

Related

WPF - Why does UI Lock Up?

If we use the task based async pattern with async and await keywords, there is no issue with the UI locking up. For example, we can spend 10 seconds loading data from the server and happily display a wait indicator at the same time. However, when running complex tasks on the UI thread, the thread locks up and the animation in the wait indicator just freezes.
Of course, one strategy could be to avoid hefty UI altogether, but this is not really an option in this case. I am loading hundreds of TreeViewItems on to the screen. This is clearly causing the UI to lock up.
I have tried putting the work on the control's Dispatcher like this, but it doesn't help:
var action = new Action(() =>
{
SchemaTree.Items.Clear();
foreach (var assemblyStructure in assemblyStructures)
{
var assemblyNode = CreateTreeNode(SchemaTree.Items, assemblyStructure.Assembly.Name.Replace("Adapt.Model.", string.Empty), assemblyStructure.Assembly, "/Icons/Schema.PNG");
foreach (var theNameSpace in assemblyStructure.Namespaces)
{
var namespaceNode = CreateTreeNode(assemblyNode.Items, theNameSpace.TheNamespace.Name, theNameSpace.TheNamespace, "/Icons/Namespace.PNG");
foreach (var classInfo in theNameSpace.Classes)
{
CreateClassInfoNode(theNameSpace, classInfo, namespaceNode);
}
}
}
});
await SchemaTree.Dispatcher.BeginInvoke(action, DispatcherPriority.Background, null);
If I move the work on to a Task with Task.Run it actually stops the UI from locking up, but obviously, I get a cross threading violation.
var action = new Action(async () =>
{
await Task.Run(() =>
{
SchemaTree.Items.Clear();
foreach (var assemblyStructure in assemblyStructures)
{
var assemblyNode = CreateTreeNode(SchemaTree.Items, assemblyStructure.Assembly.Name.Replace("Adapt.Model.", string.Empty), assemblyStructure.Assembly, "/Icons/Schema.PNG");
foreach (var theNameSpace in assemblyStructure.Namespaces)
{
var namespaceNode = CreateTreeNode(assemblyNode.Items, theNameSpace.TheNamespace.Name, theNameSpace.TheNamespace, "/Icons/Namespace.PNG");
foreach (var classInfo in theNameSpace.Classes)
{
CreateClassInfoNode(theNameSpace, classInfo, namespaceNode);
}
}
}
});
});
await SchemaTree.Dispatcher.BeginInvoke(action, DispatcherPriority.Background, null);
Any ideas on how to tell the UI to lessen the priority of this work so that the wait indicator has a chance to animate, and the whole UI doesn't lock up?
The answer to my original question is that the Dispatcher (that handles message pumping) executes everything in a first in first out sequence and is not multi threaded. That means that if you flood the Dispatcher with messages, the UI will lock up. Thankfully, the problem is easy to solve. All I need to do is call
await Dispatcher.Yield();
in the loop and the problem magically goes away. What it does is allow other UI work to execute in between the creation of the nodes.
This answer is a placeholder until more detail is added here.

How to raise a UI event from a background Thread

I've a WebCamCapture class that inherits from UserControl class and I start it in a separate thread like this
ThreadStart webCamThreadStart = () =>
{
webcam = new WebCamCapture();
webcam.FrameNumber = ((ulong)(0ul));
webcam.TimeToCapture_milliseconds = FrameNumber;
webcam.ImageCaptured += new WebCamCapture.WebCamEventHandler(webcam_ImageCaptured);
Start();
};
Thread threadnameThread = new Thread(webCamThreadStart) { IsBackground = true };
threadnameThread.Start();
The web cam starts but it never raises ImageCaptured event.
When I run the code under a Dispatcher BeginInvoke, the event gets fired,
System.Windows.Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
{
//above code
}));
But thats not my purpose, because this way the UI slows down. I want to run the webcam in a separate thread, this was my original question
[Text Input very slow in WPF Application
My question is how we can raise a UI event from a background thread.
Thanks.

async await not waiting

Tried to find something similar and read all the answers given but couldn`t find something that will explain it to me.
Here is a sample code for opening a dialog popup (WPF). I want after the ShowOverlayView turns to True, that the UI will be accessible (this is why the async-await) and the program to wait until it is finished when the user clicks "Close".
Small clarification:
ShowOverlayViewModel sets a boolean to true/false for the Visibility property of a ContentControl. Since this is the case then I have nothing to wait for "the regular way".
Currently when the view is being "visible" the MessageBox is immediately shown.
Seems like it doesn`t wait for the AutoResetEvent.
Small update: It seems to be relevant specific to the MessageBox. I tried to change the Message property after the await code line and it occurred only after the are.Set(). I would still love to know why did the MessageBox act as it did.
private void CommandAction()
{
ShowOptionsDialog();
MessageBox.Show("");
}
private async void ShowOptionsDialog()
{
var are = new AutoResetEvent(false);
var viewmodel = new DialogPopupViewModel();
viewmodel.Intialize("some title", "some message", DialogPopupViewModel.YesNoCancelButtons);
SetOverlayViewModel(viewmodel);
viewmodel.SetCloseViewAction(() =>
{
HideOverlayView();
are.Set();
});
ShowOverlayView = true;
await Task.Factory.StartNew(() =>
{
are.WaitOne();
//return viewmodel.DialogResult;
});
//return DialogResultEnum.Cancel;
}
Thanks for the help
Classic async-void bug. Research what async void does and why it's bad practice. Since ShowOptionsDialog() does not return a task that is awaited execution continues immediately. Change the return type to Task and await the result of the method call.
You can replace the event with a TaskCompletionSource<object> and say await myTcs.Task. A TCS is a more TPL-friendly event.

FromCurrentSynchronizationContext block throwing nullreferenceexception on Windows Form close

I am developing a windows form application. Following is the code segment that is executed on a button click event.
var taskExecute= Task<Datatable>.Run(() =>
{
return = GetDt(); // time consuming method
})
.ContinueWith(task=>
{
this.datagridVal.Datasource = task.Result;
}, TaskScheduler.FromCurrentSynchronizationContext());
this works fine. But If I start the thread and then immediately click on the cross(x) button of the form then the following line of code throws NullReferenceException (most probably because the form has been already disposed and so is the datagrid control)
this.datagridVal.Datasource = task.Result;
How can I modify the code so that this block of code does not throw any exception if the form is closed when the thread is running? Also please suggest if there is any better way to handle the scenario using
If the problem indeed comes from the fact that the form is disposed, then you could handle that with this.IsDisposed:
var taskExecute= Task<Datatable>.Run(() =>
{
return = GetDt(); // time consuming method
})
.ContinueWith(task=>
{
if (!this.IsDisposed)
{
this.datagridVal.Datasource = task.Result;
}
}, TaskScheduler.FromCurrentSynchronizationContext());

Disable screen while loading data

In my wpf application, I have a menu. When I click on one of the elements of the menu, I change my screen data, which is quite a long process.
I tried to disable the main window when I do such a loading, using this method :
private void SetNavigation(MainContentTypeEnum enumVal, int id, ICheckState vm)
{
var parent = Window.GetWindow(this);
var tmpCursor = parent.Cursor;
parent.Cursor = Cursors.Wait;
parent.IsEnabled = false;
BackgroundWorker bw = new BackgroundWorker();
bw.WorkerReportsProgress = true;
bw.DoWork += (o, args) =>
{
try
{
Dispatcher d = args.Argument as Dispatcher;
d.Invoke(new Action(() =>
{
Navigation.Navigator.SetContol(enumVal, id, vm);
}));
}
catch (Exception ex)
{
Debug.WriteLine(ex.ToString());
}
};
bw.RunWorkerCompleted += (o, args) =>
{
parent.IsEnabled = true;
parent.Cursor = tmpCursor;
};
bw.RunWorkerAsync(Dispatcher.CurrentDispatcher);
}
This method works on the very first call, the form is disabled, and then enabled when data is loaded. But on next calls, it doesn't work anymore, everything freezes until the operation completes. I tried setting a breakpoint, and the method is correctly hit and executed. I don't understant why it only works one time...
Have you an idea ?
Thanks in advance!
Edit: A bit of precision: this code is part of a usercontrol, which is why I call the parent using Window.GetWindow(this);
Edit2: Setting a Thread.Sleep(1000); just before invoking the dispatcher does the job. My guess is that the parent.IsEnabled instruction is not executed quickly enough... but why ?
Edit3: Having made some timings, my data retrieval is quite quick. It seems that the problem exists on the binding phase. I set the value to the bound property, and the method returns. However, the UI still frozen for a moment after that.

Resources