<

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

x

Asynchronous programming with UI

Published on
4,712 Points
1,712 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
Comment
Author:Naman Goel
0 Comments

Featured Post

Why Diversity in Tech Matters

Kesha Williams, certified professional and software developer, explores the imbalance of diversity in the world of technology -- especially when it comes to hiring women. She showcases ways she's making a difference through the Colors of STEM program.

Join & Write a Comment

Overview of OneDrive and collaboration.
Learn how to collaborate with office 365 Office Online

Keep in touch with Experts Exchange

Tech news and trends delivered to your inbox every month