Home WPF MainWindow freezes even in Asynchronous state
Reply: 3

WPF MainWindow freezes even in Asynchronous state

Damian Radinoiu
1#
Damian Radinoiu Published in 2018-01-11 08:52:48Z

What I'm trying to do is perform a heavy task triggered by a button event on the MainWindow, but still be able to drag the window freely. I've tried both the async/await pattern and creating new threads. However, threads will be nonblocking, MainWindow still freezes. Here's the code:

uiTIN.Click += async (o, e) =>
{
    var _ = await Task.Run(() => job());
};

That's the button callback and here is the func:

    private int job()
    {
        this.Dispatcher.Invoke(() =>
        { 
         //Other function calls here omitted
        });
     return 0;
    }

EDIT: The workaround was to use BackgroundWorker and I have also decorated dependent UI code snippets in Dispatcher Invoke function

appa yip yip
2#
appa yip yip Reply to 2018-01-11 09:43:25Z

From Microsoft's doccumentation on Dispatcher (emphasis mine):

In WPF, a DispatcherObject can only be accessed by the Dispatcher it is associated with. For example, a background thread cannot update the contents of a Button that is associated with the Dispatcher on the UI thread. In order for the background thread to access the Content property of the Button, the background thread must delegate the work to the Dispatcher associated with the UI thread. This is accomplished by using either Invoke or BeginInvoke. Invoke is synchronous and BeginInvoke is asynchronous. The operation is added to the queue of the Dispatcher at the specified DispatcherPriority.

So basically what you're doing is call an asynchronous method, and then forcing it to run on the UI thread, which accomplishes nothing.

In your //Other function calls here omitted, I'm asuming that you need to access some part of the UI, if that's not the case, all you have to do is remove the Dispatcher.Invoke from your method.

If my assumptions are right, then you must figure out a way of splitting your function, so that the part that isn't UI related run in a Background thread and only what needs to run on the UI Thread actually do.

My suggestion is to use a Background Worker. Here's how it'd look:

uiTIN.Click += (o, e) =>
{
    job();
};

... and then ...

private int job()
{
    BackgroundWorker worker = new BackgroundWorker();

    worker.DoWork += (s, e) =>
    {
        // Part of other function calls here omitted that don't need to run on the UI thread

        Dispatcher.Invoke(() =>
        {
            // Part of other function calls here omitted that must run on the UI thread
        });
    };

    worker.RunWorkerAsync();

    return 0;
}
Gordon
3#
Gordon Reply to 2018-01-11 09:05:36Z

The normal practice is that you have to return from buttons onClick event callback as soon as you can in order to avoid blocking the main thread(or some refer to UI thread). If the main thread is blocked the application will look like frozen. This is a fundamental design of any GUI application to synchronize UI flow.

You start an async task in callback but you also wait for the task to finish before returning. You should start a BackgroundWorker in the onClick event then return.

Stefano d'Antonio
4#
Stefano d'Antonio Reply to 2018-01-11 10:48:09Z

It has been explained quite well already why your code was blocking the UI thread (queuing your work on the Dispatcher). But I would not recommend the usage of the BackgroundWorker, I would rather fix your code with Task.Run for several reasons all explained in this article: https://blog.stephencleary.com/2013/09/taskrun-vs-backgroundworker-conclusion.html

You need to login account before you can post.

About| Privacy statement| Terms of Service| Advertising| Contact us| Help| Sitemap|
Processed in 0.322 second(s) , Gzip On .

© 2016 Powered by mzan.com design MATCHINFO