Click here to Skip to main content
15,868,016 members
Articles / Desktop Programming / WPF

Simple Example of Responsive UI in WPF

Rate me:
Please Sign up or sign in to vote.
4.62/5 (10 votes)
30 Apr 2013CPOL5 min read 53.8K   2.5K   17   6
Responsive UI

Introduction

In this article we will see how to create responsive UI in WPF. A very simple example.

Background

In our application we often face situations where we need to communicate with a service to get data. Or we need to do some I/O operation and the get the data after processing it. So what happens if the service is slow or even if the amount of data is huge it takes time to populate the data.  In the mean time the UI kind of  freezes. And we become confused whether the application is running or not. The operating system shows it "Not Responding". Where as actually your application is waiting for some data and not crashed. Here in this article we will see how we can avoid this kind of situation and get rid off confusion.

Behind the Scenes

First of all we need to understand what happens when we ask for some data or want to do any kind of operation that has some impact on UI.  Every UI in WPF runs on a thread and managed by that thread. That thread is responsible for managing and updating the UI. The point is whenever we are doing any kind of update or any kind of UI related work we need to be on that thread. Cause any other thread does not has the access to work on the UI. In most of the cases WPF does that for us automatically. That's why we don't think about that. But in certain scenarios like mentioned above we need to manage this by ourselves. So the point is UI related work runs on a thread, I/O related work should run on different thread and so on.  And if we don't follow this things may screw up.

Let's think of scenario similar to the one we are trying to work on. Suppose we have a class that returns a calculation result of any particular thing. And we need to get that result to show it in the UI. In an ideal scenario we would use a service to call that class and get the result. But just for the simplicity of this example we will call this class directly from our code behind. Same I have another class that returns a name which is also similar like the  previous one. And I have another class which is not in any service and returns a age value.

  1. So in first two cases we need to get data from services (though we are not using any). The point is that here we may have a time delay factor. Cause can be different, like:   
    1. Network congestion
    2. Busy Server
    3. Packet Loss
    4. Round Trip Time
  2. In the third case there is no time factor.
Now let's go through this scenarios in both ways (wrong and right).  

 The Wrong Way  

 First of all let's see how the UI looks like. So simple..

XML
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Left">
    <TextBlock x:Name="TBBill"/>
    <Button  Content="Get Bill" Click="Button_Click"/>
    <TextBlock x:Name="TBName"/>
    <Button Content="Get Name" Click="Button_Click_1"/>
    <TextBlock x:Name="TBAge"/>
    <Button Content="Get Age" Click="Button_Click_2"/>
</StackPanel>  

So we have a StackPanel where we can see that we have 3 textblocks and 3 buttons. Now Let's see the code behind of this XAML.

C#
/// <summary>
/// Interaction logic for Wrong.xaml
/// </summary>
public partial class Wrong : Window
{
    public Wrong()
    {
        InitializeComponent();
    }

    /// <summary>
    /// This function gets the bill. Instead of calling the service we are
    ///  calling the class directly. Time delay has been created in the class
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void Button_Click(object sender, RoutedEventArgs e)
    {
        CalBill obBill = new CalBill();
        TBBill.Text = obBill.GenerateBill().ToString();
    }

    /// <summary>
    /// This function gets the name. Instead of calling the service we are
    ///  calling the class directly. Time delay has been created in the class
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void Button_Click_1(object sender, RoutedEventArgs e)
    {
        Name obNam = new Name();
        TBName.Text = obNam.ReturnName();
    }
 
    /// <summary>
    /// Gets the age. No service scenario here as discussed above.
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void Button_Click_2(object sender, RoutedEventArgs e)
    {
        Age obAge = new Age();
        TBAge.Text = obAge.ReturnAge().ToString();
    }
}

Now run this application and click on the Get Bill or Get Name button. What is happening?? It seems that the UI has been frozen or kind of not responding. But after a certain time we can see the result appear in the text block above the button. But in the mean time we can't do anything in the UI. Now why is that? Because whatever we are doing under the button click event, we are doing it in the UI thread. So the tread becomes busy to do that work (in this case lets' just say it becomes busy to get data from the service as we said before) and therefore the user interface freezes.

Right Way

So what is the solution. Well like I said before, any kind of time consuming work should run on a different thread. And that thread should do all the waiting stuffs. And once it gets the data then it will give it back to the UI.  And for this we can use the ThreadPool. So we can use the ThreadPool to call the service or method and queue it in the Work item of ThreadPool. So that the UI thread becomes free and ThreadPool will take care of those issues like calling service and getting the data from the  service or may be wait fro the service to respond. And therefore UI does not freeze anymore and we don't get confused too. And the code for that looks like this:

C#
private void Button_Click(object sender, RoutedEventArgs e)
{
    ThreadPool.QueueUserWorkItem(GetCal);
}
C#
private void GetCal(object state)
{
  int totalbill = 0;
  CalBill obBill = new CalBill();
  totalbill = obBill.GenerateBill();
  this.Dispatcher.BeginInvoke(DispatcherPriority.Normal, 
             new Action<int>(UpdateUI), totalbill);
}

private void UpdateUI(int bill)
{
  TBBill.Text = "Total Bill: " + bill.ToString();
}

Check we are queuing the GetCal function for execution. The method executes when a thread of that ThreadPool becomes available. So now the time consuming work is handled in a different thread. And therefore the UI is free.

Remember I said on top of this article that each UI in WPF runs on a thread and managed by that thread only. Dispatcher is that thread. And in the GetCal method we called Dispatcher.BeginInvoke() method where we created a delegate to update the UI with return result. Now what would happen if don't call Dispatcher.BeginInvoke() method and call the UpdateUI method directly. Something similar like this:

C#
private void Button_Click(object sender, RoutedEventArgs e)
{
    ThreadPool.QueueUserWorkItem(GetCal);
}

private void GetCal(object state)
{
  int totalbill = 0;
  CalBill obBill = new CalBill();
  totalbill = obBill.GenerateBill();
  UpdateUI(totalbill);
}

If we run it like this we will get a error. That actually says that the current thread does not has the permission to update the UI or work on the UI. Because now we are trying to update UI from a different thread rather than the thread which has the access to do it and that is the dispatcher thread.

So now we understand that how we can keep the UI responsive. Let's now see the example based on the three scenarios stated above. First a class that returns a calculation and another class that returns a name. And both this classes take time to return data.

C#
public class CalBill
{
    // Multiple lopps with huge iteration has been created
    // intentionally just to create time delay.
    public int GenerateBill()
    {
        int totalBill = 0;

        for (int i = 0; i < 1000; i++)
        {
            for (int j = 1; j < 1001; j++)
            {
                for (int k = 1; k < 1802; k++)
                {
                    totalBill = i + j + k;
                }
            }
        }

        return totalBill;
    }
}

public class Name
{
    //Thread.sleep has been used just to delay some time.
    public string ReturnName()
    {
        string name = "John Here";
        Thread.Sleep(5000);
        return name;
    }
}

public class Age
{
    public int ReturnAge()
    {
        return 100;
    }
}

The code behind file of XAML.

C#
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    /// <summary>
    /// Queuing the GetCal method for execution which is managed by ThreadPool 
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void btnBill_Click(object sender, RoutedEventArgs e)
    {
        ThreadPool.QueueUserWorkItem(GetCal);
    }

    /// <summary>
    /// This function gets the bill. Instead of calling the service we are
    ///  calling the class directly. Time delay has been created in the class
    /// </summary>
    /// <param name="state"></param>
    private void GetCal(object state)
    {
        int totalbill = 0;
        CalBill obBill = new CalBill();
        totalbill = obBill.GenerateBill();
        
        // Calls the dispatcher thread of the UI
        this.Dispatcher.BeginInvoke(DispatcherPriority.Normal, 
                      new Action<int>(UpdateUI), totalbill);
    }

    private void UpdateUI(int bill)
    {
        TBBill.Text = "Total Bill" + bill.ToString();
    }

    /// <summary>
    /// Queuing the GetCal method for execution which is managed by ThreadPool 
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void btnName_Click(object sender, RoutedEventArgs e)
    {
        ThreadPool.QueueUserWorkItem(GetName);
    }

    /// <summary>
    /// This function gets the name. Instead of calling the service we are
    ///  calling the class directly. Time delay has been created in the class
    /// </summary>
    /// <param name="state"></param>
    private void GetName(object state)
    {
        string name = " ";
        Name obName = new Name();
        name = obName.ReturnName();

        // Calls the dispatcher thread of the UI
        this.Dispatcher.BeginInvoke(DispatcherPriority.Normal, 
                            new Action<string>(UpdateUI), name);
    }

    private void UpdateUI(string name)
    {
        TBName.Text = "Name is " + name;
    }

    private void btnAge_Click(object sender, RoutedEventArgs e)
    {
        Age obAge = new Age();
        TBAge.Text = obAge.ReturnAge().ToString();
    }

}

Now if we run this application we will see a very responsive UI. No more confusion or waiting and happy working..

There are other ways of doing it too. In the next article we will see another way of doing this.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
Bangladesh Bangladesh
All I'm trying is to be a ......

Comments and Discussions

 
GeneralSimple and neat Pin
Thava Rajan24-Jul-15 3:50
professionalThava Rajan24-Jul-15 3:50 
QuestionBackgroundWorker? Pin
FatCatProgrammer11-Jun-15 4:45
FatCatProgrammer11-Jun-15 4:45 
GeneralMy vote of 5 Pin
VEMS12-Aug-13 9:50
VEMS12-Aug-13 9:50 
Questionasync Pin
MrDeej4-May-13 6:57
MrDeej4-May-13 6:57 
AnswerRe: async Pin
Saiyed Alam4-May-13 9:37
Saiyed Alam4-May-13 9:37 
GeneralMy vote of 5 Pin
Md. Rashim Uddin1-May-13 18:23
Md. Rashim Uddin1-May-13 18:23 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.