My UI is asynchronous (Part 1)

Hi, it has been a long time since my last post. I enjoyed some time travelling in Canada and Alaska.

So now I’m back to business and today we’ll talk about UI and asynchrony.

I’m mainly working with applications written in WPF. What is worse, from a client perspective, than an application which get frozen while performing a task ?  As developers we’re used to wait :-)…so why client shouldn’t do the same ?. Actually, for a normal user point of view the application is not reliable, not stable and it gives him a bad feeling about the software. So…why don’t use asynchrony ?

During the execution of long operation you keep your UI responsive and usable. Even better, you can show the progress in real time to the user…

So let’s start basic..we first need a method which will simulate a long running operation. Thanks to the Thread.Sleep() method I will simulate a 3 seconds long operation.

 Func<string> LongRunningTask = () =>
        {
            Thread.Sleep(new TimeSpan(0, 0, 3));
            return DateTime.Now.ToString("hh:mm:ss");
        };

The UI

For testing purposes I will just create a button and a textblock on my WPF interface. When the user click the button it will update the textblock with the result of the long running task.

Here’s the basic code for the UI :

 <Grid>
        <StackPanel Orientation="Vertical">
            <Button x:Name="btnClick" Content="Click" Height="50" Width="200"   Click="btnClick_Click"/>
            <TextBlock x:Name="lblResult" />
        </StackPanel>
    </Grid>

And now the code-behind of the control :

       private void btnClick_Click(object sender, RoutedEventArgs e)
        {
            ActionWihtoutAsynchrony();
        }

        private void ActionWihtoutAsynchrony()
        {
            this.lblResult.Text = LongRunningTask();
        }

So we’re ready to test and what happens is that the UI will be frozen for 3 seconds, which is totally unacceptable from a client’s perspective. You can’t even move the window.

 

Go Asynchronous !

We have many ways to implement asynchony in our application. We’ll skip the classic Thread class and work with Tasks. The goal is now to execute the long running operation in a different thread and let the UI responsive during the process. Here the code will call when the click event is raised :

private void Asynchony1()
{
     Task.Factory.StartNew(() =>
     {
       this.lblResult.Text = LongRunningTask();
     });
}

It looks easy, isn’t it ? Actually, just try it before you continue reading…

Yeah…it doesn’t show anything and it seems to not throw an exception either. But the UI is alive while processing, first goal completed but the client is still quite disappointed !

Actually it happens because the UI is running in a special thread dedicated to its, that’s how WPF works. Only this thread is allowed to access (and so modify) an UI element like our textblock. So, we need to update our code to process the UI modification inside the UI thread. There’s multiple ways to do it, we’ll first use the Dispatcher. This dispatcher mechanism manage the work for a thread. Here’s our updated method :

 private void Asynchony1()
        {
            Task.Factory.StartNew(() =>
            {
                var message = this.LongRunningTask();
                Application.Current.Dispatcher.Invoke(new Action(() =>
                {
                    this.lblResult.Text = message;
                }));
            });
        }

Ok, it works great now. You’ll probably wonder why the Invoke and BeginInvoke method exist in the Dispatcher class. Actually the first one is synchronous while the other is asynchronous. You can even set a priority for the task execution, by default the priority is normal. In the next example the code will execute only when the application’s idle :

 private void Asynchony1()
        {
            Task.Factory.StartNew(() =>
            {
                var message = this.LongRunningTask();
                Application.Current.Dispatcher.Invoke(new Action(() =>
                {
                    this.lblResult.Text = message;
                }), System.Windows.Threading.DispatcherPriority.SystemIdle);
            });
        }

As we have seen, putting some asynchrony in our code is not really a big issue, and we’ll see in the next post other ways of doing it.

Leave a Reply