<

Asynchronous programming with UI

Published on
4,843 Points
1,843 Views
Last Modified:
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
Author:Naman Goel
Ask questions about what you read
If you have a question about something within an article, you can receive help directly from the article author. Experts Exchange article authors are available to answer questions and further the discussion.
Get 7 days free