Asynchronous programming with UI

Naman GoelPrincipal Software engineer
CERTIFIED EXPERT
Published:
Updated:
Mulithreading is a wonderful concept but it is one of the most complex way to achieve a task. Microsoft had provided probably the most efficient way to handle this problem by using async calls and thread pool. This provides an easy way to handle more than one task in an efficient way. But the biggest problem is thread creation and disposal of thread in this programming scenario. A common problem we are facing with UI programming is we are executing a task which takes a long time.

And if we are trying to do the same in Main Thread of application it freezes the UI until the completion of job. For example in the given program we are trying to run infinite loop and trying the display the count on textbox

 
using System;
using System.Windows.Forms;

namespace WindowsFormsApplication2
{
    public partial class Form1 : Form
    {
        private TextBox textBox1;
        private Button btnStart;
    
        public Form1()
        {
            InitializeComponent();
        }

        private void InitializeComponent()
        {
            this.btnStart = new System.Windows.Forms.Button();
            this.textBox1 = new System.Windows.Forms.TextBox();
            this.SuspendLayout();
            // 
            // btnStart
            // 
            this.btnStart.Location = new System.Drawing.Point(134, 27);
            this.btnStart.Name = "btnStart";
            this.btnStart.Size = new System.Drawing.Size(75, 23);
            this.btnStart.TabIndex = 0;
            this.btnStart.Text = "Start";
            this.btnStart.UseVisualStyleBackColor = true;
            this.btnStart.Click += new System.EventHandler(this.btnStart_Click);
            // 
            // textBox1
            // 
            this.textBox1.Location = new System.Drawing.Point(12, 29);
            this.textBox1.Name = "textBox1";
            this.textBox1.Size = new System.Drawing.Size(100, 20);
            this.textBox1.TabIndex = 1;
            // 
            // Form1
            // 
            this.ClientSize = new System.Drawing.Size(292, 273);
            this.Controls.Add(this.textBox1);
            this.Controls.Add(this.btnStart);
            this.Name = "Form1";
            this.ResumeLayout(false);
            this.PerformLayout();

        }

        /// <summary>
        /// Updates the textbox
        /// </summary>
        /// <param name="i">Value to be updated</param>
        private void UpdateTextBox(int i)
        {
            textBox1.Text = i.ToString();
        }

        /// <summary>
        /// Start counting
        /// </summary>
        /// <param name="i">Intial value</param>
        private void Count(int i)
        {
            while (true)
            {
                i++;
                UpdateTextBox(i);
            }
        }

        private void btnStart_Click(object sender, EventArgs e)
        {
            Count(0);
        }

    }
}

Open in new window


One of the better ways to solve this is pushing the time consuming task to a different thread and update the UI with callback method.

One of best way to achieve this is by using BackgroundWorker class

 
using System;
using System.Windows.Forms;

namespace WindowsFormsApplication2
{
    public partial class Form1 : Form
    {
        private TextBox textBox1;
        private System.ComponentModel.BackgroundWorker backgroundWorker1;
        private Button btnStart;
    
        public Form1()
        {
            InitializeComponent();
        }

        private void InitializeComponent()
        {
            this.btnStart = new System.Windows.Forms.Button();
            this.textBox1 = new System.Windows.Forms.TextBox();
            this.backgroundWorker1 = new System.ComponentModel.BackgroundWorker();
            this.SuspendLayout();
            // 
            // btnStart
            // 
            this.btnStart.Location = new System.Drawing.Point(134, 27);
            this.btnStart.Name = "btnStart";
            this.btnStart.Size = new System.Drawing.Size(75, 23);
            this.btnStart.TabIndex = 0;
            this.btnStart.Text = "Start";
            this.btnStart.UseVisualStyleBackColor = true;
            this.btnStart.Click += new System.EventHandler(this.btnStart_Click);
            // 
            // textBox1
            // 
            this.textBox1.Location = new System.Drawing.Point(12, 29);
            this.textBox1.Name = "textBox1";
            this.textBox1.Size = new System.Drawing.Size(100, 20);
            this.textBox1.TabIndex = 1;
            // 
            // backgroundWorker1
            // 
            this.backgroundWorker1.DoWork += new System.ComponentModel.DoWorkEventHandler(this.backgroundWorker1_DoWork);
            // 
            // Form1
            // 
            this.ClientSize = new System.Drawing.Size(292, 273);
            this.Controls.Add(this.textBox1);
            this.Controls.Add(this.btnStart);
            this.Name = "Form1";
            this.ResumeLayout(false);
            this.PerformLayout();

        }

        /// <summary>
        /// Updates the textbox
        /// </summary>
        /// <param name="i">Value to be updated</param>
        private void UpdateTextBox(int i)
        {
            textBox1.Text = i.ToString();
        }

        /// <summary>
        /// Start counting
        /// </summary>
        /// <param name="i">Intial value</param>
        private void Count(int i)
        {
            while (true)
            {
                i++;
                this.Invoke(new Action<int>((x) => textBox1.Text = x.ToString()), i);
                
            }
        }

        private void btnStart_Click(object sender, EventArgs e)
        {
            backgroundWorker1.RunWorkerAsync();
        }

        private void backgroundWorker1_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
        {
            Count(0);
        }

    }
}

Open in new window

The other was to achieve the same is using Delegate BeginInvoke  method

 
using System;
using System.Windows.Forms;

namespace WindowsFormsApplication2
{
    public partial class Form1 : Form
    {
        private TextBox textBox1;
        private Button btnStart;
    
        public Form1()
        {
            InitializeComponent();
        }

        private void InitializeComponent()
        {
            this.btnStart = new System.Windows.Forms.Button();
            this.textBox1 = new System.Windows.Forms.TextBox();
            this.SuspendLayout();
            // 
            // btnStart
            // 
            this.btnStart.Location = new System.Drawing.Point(134, 27);
            this.btnStart.Name = "btnStart";
            this.btnStart.Size = new System.Drawing.Size(75, 23);
            this.btnStart.TabIndex = 0;
            this.btnStart.Text = "Start";
            this.btnStart.UseVisualStyleBackColor = true;
            this.btnStart.Click += new System.EventHandler(this.btnStart_Click);
            // 
            // textBox1
            // 
            this.textBox1.Location = new System.Drawing.Point(12, 29);
            this.textBox1.Name = "textBox1";
            this.textBox1.Size = new System.Drawing.Size(100, 20);
            this.textBox1.TabIndex = 1;
            // 
            // Form1
            // 
            this.ClientSize = new System.Drawing.Size(292, 273);
            this.Controls.Add(this.textBox1);
            this.Controls.Add(this.btnStart);
            this.Name = "Form1";
            this.ResumeLayout(false);
            this.PerformLayout();

        }

        private void UpdateTextBox(int i)
        {
            textBox1.Text = i.ToString();
        }

        private void Count(int i)
        {
            while (true)
            {
                i++;
                this.Invoke(new Action<int>((x) => textBox1.Text = x.ToString()), i);
            }
        }


        private void btnStart_Click(object sender, EventArgs e)
        {
            int i = 0;
            new Action<int>(Count).BeginInvoke(i, null, null);
        }
    }
}

Open in new window


And similar thing can be achieved with Task Class from .net 4.0 onwards.
 
using System;
using System.Timers;
using System.Threading;
using System.Windows.Forms;
using System.Threading.Tasks;

namespace WindowsFormsApplication2
{
    public partial class Form1 : Form
    {
        private TextBox textBox1;
        private Button btnStart;
    
        public Form1()
        {
            InitializeComponent();
        }

        private void InitializeComponent()
        {
            this.btnStart = new System.Windows.Forms.Button();
            this.textBox1 = new System.Windows.Forms.TextBox();
            this.SuspendLayout();
            // 
            // btnStart
            // 
            this.btnStart.Location = new System.Drawing.Point(134, 27);
            this.btnStart.Name = "btnStart";
            this.btnStart.Size = new System.Drawing.Size(75, 23);
            this.btnStart.TabIndex = 0;
            this.btnStart.Text = "Start";
            this.btnStart.UseVisualStyleBackColor = true;
            this.btnStart.Click += new System.EventHandler(this.btnStart_Click);
            // 
            // textBox1
            // 
            this.textBox1.Location = new System.Drawing.Point(12, 29);
            this.textBox1.Name = "textBox1";
            this.textBox1.Size = new System.Drawing.Size(100, 20);
            this.textBox1.TabIndex = 1;
            // 
            // Form1
            // 
            this.ClientSize = new System.Drawing.Size(292, 273);
            this.Controls.Add(this.textBox1);
            this.Controls.Add(this.btnStart);
            this.Name = "Form1";
            this.ResumeLayout(false);
            this.PerformLayout();

        }

        private void UpdateTextBox(int i)
        {
            textBox1.Text = i.ToString();
        }

        private void Count()
        {
            int i=0;
            while (true)
            {
                
                this.Invoke(new Action<int>((x) => textBox1.Text = x.ToString()), i++);
            }
        }


        private void btnStart_Click(object sender, EventArgs e)
        {
            Task.Factory.StartNew(new Action(Count));
        }
    }
}

Open in new window


and making it more compact:
 
using System;
using System.Timers;
using System.Threading;
using System.Windows.Forms;
using System.Threading.Tasks;

namespace WindowsFormsApplication2
{
    public partial class Form1 : Form
    {
        private TextBox textBox1;
        private Button btnStart;
    
        public Form1()
        {
            InitializeComponent();
        }

        private void InitializeComponent()
        {
            this.btnStart = new System.Windows.Forms.Button();
            this.textBox1 = new System.Windows.Forms.TextBox();
            this.SuspendLayout();
            // 
            // btnStart
            // 
            this.btnStart.Location = new System.Drawing.Point(134, 27);
            this.btnStart.Name = "btnStart";
            this.btnStart.Size = new System.Drawing.Size(75, 23);
            this.btnStart.TabIndex = 0;
            this.btnStart.Text = "Start";
            this.btnStart.UseVisualStyleBackColor = true;
            this.btnStart.Click += new System.EventHandler(this.btnStart_Click);
            // 
            // textBox1
            // 
            this.textBox1.Location = new System.Drawing.Point(12, 29);
            this.textBox1.Name = "textBox1";
            this.textBox1.Size = new System.Drawing.Size(100, 20);
            this.textBox1.TabIndex = 1;
            // 
            // Form1
            // 
            this.ClientSize = new System.Drawing.Size(292, 273);
            this.Controls.Add(this.textBox1);
            this.Controls.Add(this.btnStart);
            this.Name = "Form1";
            this.ResumeLayout(false);
            this.PerformLayout();

        }


        private void btnStart_Click(object sender, EventArgs e)
        {
            Task.Factory.StartNew((Action)delegate()
                                  {
                                      int i = 0;
                                      while (true)
                                      {
                                          this.Invoke(new Action<int>((x) => textBox1.Text = x.ToString()), i++);
                                      }
                                  });
        }
    }
}

Open in new window


Please note that in all these method Thread is taken from ThreadPool
0
1,944 Views
Naman GoelPrincipal Software engineer
CERTIFIED EXPERT

Comments (0)

Have a question about something in this article? You can receive help directly from the article author. Sign up for a free trial to get started.

Get access with a 7-day free trial.
Continue Growing Your Skills and Your Career
  • Interact with leading experts on your specific technology problems.
  • Receive the guidance of experienced professionals.
  • Learn from troubleshooting others have experienced.
  • Gain knowledge from a library of courses, all included.