UI Updates – Part 1

Updating the UI is a problem and it is tricky. There are very few simple rules to follow. Let’s see Here we’ll see  some smart ways to perform UI updates on Console applications, Windows Forms, WPF, ASP.NET, Silverlight, Windows Phone and Windows Store apps.

Console Applications

Do they have UI ? No. But we’ll check how different threads perform updates on Console applications. Look at the following code. It has a main thread and another thread (worker thread) perform some time consuming task and updates the UI. Main thread shows a rotating bar till the worker thread completes.

   1: class Program

   2:     {

   3:         static void Main(string[] args)

   4:         {

   5:             Console.WriteLine("Application Start");

   6:             Thread worker = new Thread(PerformTask);

   7:  

   8:             worker.Start();

   9:  

  10:             ShowConsoleWaitAnimation(worker);

  11:  

  12:             Console.WriteLine("Application End");

  13:             Console.ReadKey();

  14:  

  15:         }

  16:  

  17:  

  18:         private static void PerformTask()

  19:         {

  20:             // job time 4 - 10 seconds

  21:             int time = new Random().Next(4,11);

  22:             Thread.Sleep(1000 * time);

  23:             Console.WriteLine("Hello from worker - ran for {0} seconds",time);

  24:         }

  25:  

  26:  

  27:         private static void ShowConsoleWaitAnimation(Thread thread)

  28:         {

  29:             while (thread.IsAlive)

  30:             {

  31:                 Console.Write("Processing - /\r");

  32:                 Thread.Sleep(100);

  33:                 Console.Write("Processing - -\r");

  34:                 Thread.Sleep(100);

  35:                 Console.Write("Processing - \\\r");

  36:                 Thread.Sleep(100);

  37:                 Console.Write("Processing - |\r");

  38:                 Thread.Sleep(100);

  39:             }

  40:         }

  41:     }

 

Windows Forms

Look at the below code. In a simple Windows Forms application, for a button click event, a time consuming task runs in a separate thread and tries to update a Label control.

   1: private void BtnStartWork_Click(object sender, EventArgs e)

   2: {

   3:     Thread worker = new Thread(PerformTask);

   4:     worker.Start();

   5: }

   6:  

   7: private void PerformTask()

   8: {

   9:     // job time 4 - 10 seconds

  10:     int time = new Random().Next(4, 11);

  11:     Thread.Sleep(1000 * time);

  12:     LblStatus.Text = String.Format("Work completed - ran for {0} seconds", time);

  13: } 

 

When you run the above code, you will get this famous Exception. Because the Label control is created in UI thread and our worker thread tries to reach it. Cross thread referencing cannot happen as this is a violation of operating system thread handling policies. ( Do not mistake the cross thread communication and cross thread referencing, cross thread communication can happen among threads)

image

There are plenty of solutions for this problem, but the ready made one is to use BackgroundWorker class. The below code shows how you can use BackgroundWorker class and perform UI updates and also shows a progress bar on the UI. Events are coded inline to reduce the number of methods for this small demo.

   1: public partial class Form1 : Form

   2: {       

   3:        private BackgroundWorker _bgWorker = new BackgroundWorker();

   4:        private string _result = String.Empty;

   5:  

   6:        public Form1()

   7:        {

   8:            InitializeComponent();

   9:        }

  10:  

  11:        private void BtnStartWork_Click(object sender, EventArgs e)

  12:        {

  13:            _bgWorker.WorkerReportsProgress = true;

  14:  

  15:            _bgWorker.DoWork += (s, ev) =>

  16:                {

  17:                    _result = PerformTask();

  18:                };

  19:            

  20:            _bgWorker.ProgressChanged += (s, ev) =>

  21:                {

  22:                    workerProgressBar.Value = ev.ProgressPercentage * 100;

  23:                };

  24:  

  25:            _bgWorker.RunWorkerCompleted += (s, ev) =>

  26:                {

  27:                    LblStatus.Text = _result;

  28:                };

  29:  

  30:            _bgWorker.RunWorkerAsync();

  31:        }

  32:  

  33:  

  34:        private string PerformTask()

  35:        {

  36:            // job time 4 - 10 seconds

  37:            int time = new Random().Next(4, 11);

  38:            int fullTime = time;

  39:  

  40:            while (time >= 0)

  41:            {

  42:                Thread.Sleep(1000);

  43:                _bgWorker.ReportProgress(1 - (time / fullTime));

  44:                time--;

  45:            }

  46:  

  47:            return String.Format("Work completed - ran for {0} seconds", fullTime);

  48:         }

  49:           

  50: } 

 

WPF

In WPF we can use the BackgroundWorker, but also there’s a well refined solution in the WPF is to use the Dispatcher object. WPF model has Dispatcher object common across all the controls. So we can use this to update the UI.

Little modification in the PerformTask() and make it return the result.

   1: private string PerformTask()

   2: {

   3:     // job time 4 - 10 seconds

   4:     int time = new Random().Next(4, 11);

   5:     int fullTime = time;

   6:  

   7:     Thread.Sleep(time);      

   8:  

   9:     return String.Format("Work completed - ran for {0} seconds", fullTime);

  10: } 

Button click event.

   1: private void BtnWork_Click(object sender, RoutedEventArgs e)

   2: {

   3:     Thread thread = new Thread(() =>

   4:         {

   5:             string result = PerformTask();

   6:             Dispatcher.Invoke(() =>

   7:                 {

   8:                     TxtStatus.Text = result;

   9:                 });

  10:         });

  11:     thread.Start();

  12: }

Part 2 – will discuss about UI updates in ASP.NET and Silverlight.

Advertisement