WPF - Why does UI Lock Up? - wpf

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.

Related

Creating a new UI thread while calling methods on the original thread

I am in a similar situation as this poster (What's the best way to create a new UI thread and call back methods on the original thread?)
I have an API object which I would like to perform lengthy calculations on, however any properties or methods of this object must be accessed on the current thread (which is the UI thread) or else I get "Accessing disposed TPS.NET DataObject" exceptions
Is there an elegant way of accomplishing this using F# async workflows or will I be stuck managing thread dispatchers as in his solution.
For reference, here is his solution to the issue:
public class Plugin
{
public void Run(Context context)
{
// Get the application's UI thread dispatcher
var dispatcher = Dispatcher.CurrentDispatcher;
// Create a dispatcher frame to push later
var frame = new DispatcherFrame();
// Create a new UI thread (using an StaTaskScheduler)
Task.Factory.StartNew(async () =>
{
var window = new MyWindow();
// The Click event handler now uses the original
// thread's dispatcher to run the slow method
window.MyButton.Click += async (o, e) =>
await dispatcher.InvokeAsync(() => context.SlowMethod());
window.ShowDialog();
// When the window is closed, end the dispatcher frame
frame.Continue = false;
}, CancellationToken.None, TaskCreationOptions.None, new StaTaskScheduler(1));
// Prevent exiting this Run method until the frame is done
Dispatcher.PushFrame(frame);
}
}
Without know the exact details I would suggest having the Click handler on the main thread and do the following:
Copy any data needed off the UI into an F# record and passes this into an async workflow
Return immediately after putting the UI into a 'loading' state
The following code is untested but should put you on the right track:
//Get the context of the UI
let context = System.Threading.SynchronizationContext.Current
//Gather any needed data from the UI into immutable F# records
//Put the UI into a 'loading' state
async {
// Do work on a different thread
do! Async.Sleep 1000
let x = 1
// Switching back to the UI
do! Async.SwitchToContext context
//Update UI
return ()
}
|> Async.Start
This link should also provide some useful information http://tomasp.net/blog/async-non-blocking-gui.aspx/
EDIT:
If you need to go back and forth between the UI thread and a background thread to gather additional information in the async workflow you can make alternating calls between do! Async.SwitchToThreadPool() and do! Async.SwitchToContext context

WPF: Waiting for an ItemContainerGenerator to have its containers ready

I'm writing a WPF control that contains an ItemsControl. The control adds and removes items based on certain user actions. Once an item has been added, the control needs to access a FrameworkElement inside the ItemTemplate instance that was just created.
I'm using ItemContainerGenerator.ContainerFromIndex to do this. I also get a ContentPresenter back, but it is empty: it appears it takes a few milliseconds on a separate thread to instantiate the template objects.
I read that I need to use ItemContainerGenerator.Status to determine whether or not the containers are fully created, so I wrote the following method:
private async Task<TextBox> GetMainInputControl(int index)
{
// _selectedItemsEditor is the ItemsControl inside my main control that contains the items
var evt = new ManualResetEvent(false);
_selectedItemsEditor.ItemContainerGenerator.StatusChanged += (sender, args) =>
{
var status = _selectedItemsEditor.ItemContainerGenerator.Status;
if (status == GeneratorStatus.ContainersGenerated || status == GeneratorStatus.Error)
{
evt.Set();
}
};
ContentPresenter container = null;
await Task.Run(() =>
{
var status = _selectedItemsEditor.ItemContainerGenerator.Status;
if (status == GeneratorStatus.GeneratingContainers
|| status == GeneratorStatus.NotStarted)
{
evt.WaitOne();
}
container =
_selectedItemsEditor.ItemContainerGenerator.ContainerFromIndex(index) as ContentPresenter;
});
return container?.ContentTemplate.FindName("PART_ItemEditorMainInput", container) as TextBox;
}
I know that there are a few things I need to fix here, but mostly, it just doesn't work, because _selectedItemsEditor.ItemContainerGenerator.Status immediately returns GeneratorStatus.ContainersGenerated, so the code doesn't wait - but then the code container?.ContentTemplate.FindName throws an exception indicating that the container is NOT ready.
How can I make this work, or alternatively use a better way of achieving this?
That code looks like you're trying to access ui controls on a background thread. So I'm not at all surprised it doesn't work.
There are two approaches I would consider.
You could defer your code so it waits until the dispatcher ( the ui thread essentially ) has done it's stuff for whatever you just asked it to do.
Application.Current.Dispatcher.InvokeAsync(new Action(() =>
{
// Your code which is to run after the items are rendered
}), DispatcherPriority.ContextIdle);
Or
You could force the layout process so you make the items do their thing. This will potentially lock the ui up whilst it's working. If the user clicks something and his obvious intent is to wait for layout to update or there's not so much going on then this won't be a problem.
You could just call .UpdateLayout() on your control.
https://msdn.microsoft.com/en-us/library/system.windows.uielement.updatelayout(v=vs.110).aspx

WPF: How to make Calls to Dispatcher.Invoke() Synchronous?

In my MVVM based Wix Managed Bootstrapper application, while handling different events, I'm trying to show the user a view to get some input. It looks like Burn events are executing Asynchronously because using Dispatcher.Invoke(), it is skipping or passing by the view and hitting the last event, i.e not waiting for this input task to finish.
Here is the event handler which needs to finish before hitting last one:
Please note that when MessageBox.Show is popped, it waits until we close it. While debugging, I see it actually switched to MissingSourceView and loaded MissingSourceViewModel, but then while skipping it, and executes ApplyComplete();
BootstrapperApplication.ResolveSource += (sender, e) => {
System.Windows.Forms.MessageBox.Show("Inside ResolveSource");
WixBootstrapperData.CurrentDispatcher.Invoke(((Action)(() =>
{
WixBootstrapperData.CurrentViewModel = new MissingSourceViewModel(WixBootstrapperData, InfoMessage);
})));
};
BootstrapperApplication.ApplyComplete += (sender, e) =>
{
WixBootstrapperData.BootstrapperApplicationModel.FinalResult = e.Status;
WixBootstrapperData.CurrentDispatcher.Invoke((Action)(() =>
{
InfoMessage = AppResource.MsgFinished;
WixBootstrapperData.CurrentViewModel = new FinishViewModel(WixBootstrapperData, InfoMessage);
}
));
};
I guess, I should use await and async with ResolveSource(), but I face errors like:
Error CS1061 'BaseViewModel' does not contain a definition for
'GetAwaiter' and no extension method 'GetAwaiter' accepting a first
argument of type 'Task' could be found (are you missing a using
directive or an assembly reference?)
Any idea how to make it wait for finishing inside ResolveSource() and then jump to wherever it wants?
Use this, and please tell if this solves your problem.
WixBootstrapperData.CurrentDispatcher.Invoke(((Action)(() =>
{
Task.Factory.StartNew(() => {
WixBootstrapperData.CurrentViewModel = new MissingSourceViewModel(WixBootstrapperData, InfoMessage);
}).RunSynchronously();
})));

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

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.

Getting Progress Updates using HttpWebRequest and TPL (Tasks)

I would like to track the progress of a download taking place on a separate thread. I know that System.Net.WebClient has a DownloadStringAsync method, but it doesn't work directly with the new TPL types (TaskFactory, Task, etc.).
Can progress be tracked using the HttpRequest and HttpResponse classes?
What's the best class for tracking progress? The less overhead the better.
Are there times when the size of the response is unknown, aka, progress can't be tracked?
What's the best way to synchronize with the UI thread whenever progress is made?
Most examples show Tasks updating the UI only after the entire task is complete. These examples use continuations taking a UI synchronization context that avoids needing to work with a Dispatcher directly.
The idea is to show a grid view (in WPF) with all the downloads with progress bars. I am going to adding new rows and updating progress bars all the time. I'm trying to avoid turning this code into a mess.
DownloadStringAsync and the other event methods work very well with TPL in .NET 4.0 (check for EAP and TPL). In general, TPL does support event async programming through the TaskCompletionSource. The Begin/EndXXX model (APM) is supported through the Task.FromAsync method. You can find a detailed description TPL and Traditional .NET Asynchronous Programming.
The ParallelExtensionExtras library has a set of WebClient extensions methods like DownloadStringTask that return a task which completes when the appropriate event is fired.
The following code will create a Task that will complete when download finishes:
public Task<string> DownloadStringTask(WebClient client,Uri uri)
{
var tcs = new TaskCompletionSource<string>();
client.DownloadStringCompleted += (o, a) => tcs.SetResult(a.Result);
client.DownloadStringAsync(uri);
return tcs.Task;
}
As for updating the UI, you can easily use the DownloadProgressChanged event to provide feedback,eg:
using (var client = new WebClient())
{
client.DownloadProgressChanged += (o, a) => Console.WriteLine("{0}",a.ProgressPercentage);
var task = DownloadStringTask(client,new Uri("http://www.stackoverflow.com"));
var write=task.ContinueWith(t => Console.WriteLine("Got {0} chars", t.Result.Length));
write.Wait();
Console.ReadKey();
}
If you use data binding to provide the progress values to your progress bars, you can just update the progress value properties. If you update the progress bars directly (not a good idea), you will have to marshal the call to the UI thread using the progress bar's dispatcher, eg. like this
void UpdateProgress(int percent)
{
if (progressBar1.CheckAccess())
progressBar1.Value = percent;
else
{
progressBar1.Dispatcher.Invoke(new Action(()=>UpdateProgress(percent)));
}
}
....
client.DownloadProgressChanged += (o, a) => UpdateProgress(a.ProgressPercentage);

Resources