none
How to show the iteration count/number on a label on a form in C#?

    Question

  • Hi All,

    I am writing a program which has to do a lot of iterations using a foreach loop.
    I want to show the count of the iteration on a form.
    Can you please suggest a way.

    For example let's say that I have a method in a paticular class like this.

    Public class Myclass
    {
    List<MyObject> newdata= new List<MyObject>();
    Public List<MyObject> getObjects()
    {

    List<MyObject> alldata= new List<MyObject>();
    alldata=GetRecordsFromSQL(); // Gets all the records from the SQL server

    foreach(MyObject object in alldata)
    {
    //do some operation
    //get the count of the iteration
    newdata.Add(object);

    }

    Return newdata;
    }
    }
    So in my form I have a button to run this process. So when I click the button it run the following method.

    MyClass class= new MyClass();
    class.getObjects();

    I want to show the count of the iteration on a lable. But I do now know how to do this.
    Everytime an object is processed the lable on the form should be updated with the the number of the object being processed.
    For example, lets say that I have 20,000 records in the original data(the List named alldata in my program) Then when the the button is clicked and when the object 1 is being processed, it should show in my lable as
    1/20,000. When the foreach iteration does the second object the label should change from 1/20,000 to 2/20,000. Like wise for all the operations it should be updated with the current object number being processed.

    Can someone please show me a way to do this?

    Thank you very much.




    • Edited by Clive2334 Monday, October 22, 2012 6:12 AM
    Monday, October 22, 2012 6:10 AM

Answers

  • Hi Clive,

    The first thing to understand how all this works, is to understand why your solution isn't working. 

    If the code below is what you are using:

    Public class Myclass
    {
    	List<MyObject> newdata= new List<MyObject>();
    	public int count_iteration= 0; 
    	Public List<MyObject> getObjects()
    	{
    
    		List<MyObject> alldata= new List<MyObject>();
    		alldata=GetRecordsFromSQL(); // Gets all the records from the SQL server
    
    		foreach(MyObject object in alldata)
    		{
    			//do some operation
    			newdata.Add(object);
    		}
    
    		Return newdata;
    	}
    }
    
    public partial class Form1 : Form
    {
            public Form1()
            {
                InitializeComponent();
            }
    
    	private void button1_Click(object sender, EventArgs e)
    	{
    		Myclass class= new Myclass();
    		class.getObjects();
            }
    }


    Then my explanation is that the reason why you aren't getting is because the class.getObjects() is executed in the same thread as in which the GUI is shown. So what you need to do is to run the code class.getObjects() in another thread. By doing so the GUI will not be blocked and your count will be updated. That's the reason why everyone here is suggesting to use the Timer Object or the Background worker. Here is an exmpale with two separate classes to show you how you can do it. 

    public partial class Form1 : Form
    	{
    		MyClass @class;
    		Timer timer;
    
    		public Form1()
    		{
    			InitializeComponent();
    			timer = new Timer();
    			timer.Interval = 1000; //this is in milli seconds. 
    			//The tick event is then fired in every 1000 ms.
    			timer.Tick += new EventHandler(timer_Tick);
    		}
    
    		void timer_Tick(object sender, EventArgs e)
    		{
    			//label is updated every second, because interval is set to 1000ms.
    			label1.Text = @class.CurrentIteration.ToString();
    			if (@class.IsStopped)
    				timer.Stop();
    		}
    
    		private void button1_Click(object sender, EventArgs e)
    		{
    			@class = new MyClass();
    			var t = new System.Threading.Thread(new System.Threading.ThreadStart(@class.ExtensionOperation));
    			t.Start(); //Run operation in different thread.s
    			timer.Start();
    		}
    
    		private void Form1_FormClosing(object sender, FormClosingEventArgs e)
    		{
    			//Override closing to stop the thread, otherwise the program will continue to run.
    			@class.Stop();
    			timer.Stop();
    		}
    	}
    
    	public class MyClass
    	{
    		public long CurrentIteration { get; set; }
    
    		public bool IsStopped { get; set; }
    
    		public void ExtensionOperation()
    		{
    			long iterations = 10000000000;
    
    			for (long i = 0; i < iterations && !IsStopped; i++)
    			{
    				CurrentIteration = i;
    			}
    
    			IsStopped = true;
    		}
    
    		public void Stop()
    		{
    			IsStopped = true;
    		}
    	}

    Hope this helps. 

    Here is a link which can help you understand threading: http://msdn.microsoft.com/en-us/library/aa645740(v=vs.71).aspx

    But if you only need to know whether the program is running, you could simply write to a file during your operation. And then check whether the file is being updated or not.

    I hope this helps.

    Regards,

    Kishen


    Wednesday, October 24, 2012 12:09 PM
  • Word of warning, if you are not careful you can flood and lock your gui doing this.  This could be part the the reason your GUI does not appear to update :-).

    What you need is to offload the task onto a background worker (as suggested) then use a delegate on the main thread to update the gui:

    public void UpdateTextbox1(string text)
    {
        if (InvokeRequired) { Invoke( new Action<string>(UpdateTextbox1), text ); }
        else { textBox1.Text = text; }
    }


    jon.stromer.galley


    Monday, October 22, 2012 3:00 PM

All replies

  • Are you sure you want to update the label every interation?  If you do this once a second so the user can see the value, it will take over 5 hours to complete the loop.
    Monday, October 22, 2012 8:04 AM
  • Hi John,

    Yes I want to either use a label or a textbox to show the number of iteration.

    After reading so many articles I included the following code snippet on my main form which looks like this.

     public void SetTextForTextBox(string myText)
            {
                this.textBox9.Refresh();
                this.textBox9.Text = myText;
            }

    Then I modified my Myclass this way.  

    Public class Myclass
    {
    List<MyObject> newdata= new List<MyObject>();
    Public List<MyObject> getObjects()
    {

    List<MyObject> alldata= new List<MyObject>();
    alldata=GetRecordsFromSQL(); // Gets all the records from the SQL server

    public int count_iteration= 0; 

    foreach(MyObject object in alldata)
    {
    //do some operation
    count_iteration++;
      myform.SetTextForTextBox(count_iteration.ToString());
    newdata.Add(object);

    }

    Return newdata;
    }
    }


    But still the textbox text is not changing and nothing happens. It is just empty.
    Can you please suggest where the problem is or if there is any modification I need to do?

    Thank you very much for your time.

    Cheers



    Monday, October 22, 2012 8:12 AM
  • Enclose the loop in timer.  Here's an example:

    using System;
    using System.Windows.Forms;
    namespace WindowsFormsApplication2
    {
      public partial class Form1 : Form
      {
        public Form1()
        {
          InitializeComponent();
          countLabel.Parent = this;
          tmr.Interval = 1000;
          tmr.Tick += Tmr_Tick;
          tmr.Start();
        }
        Label countLabel = new Label();
        Timer tmr = new Timer();
        int count = 0;
        int iterations = 20000;
        void Tmr_Tick(object sender, EventArgs e)
        {
          tmr.Stop();
          count += 1;
          countLabel.Text = count.ToString() + "/" + iterations.ToString();
          //Do stuff
          if (count < iterations) tmr.Start(); else count = 0;
        }
      }
    }


    • Edited by JohnWein Monday, October 22, 2012 8:39 AM
    Monday, October 22, 2012 8:38 AM
  • Hi John,

    I do not understand the use of Timer in my application. Actually my requirement is to see if the program is still running since the program has to be used for larger data sets as well. For example, datasets with about more than 10,000,000 records. And I want to see if the loop is still running on my application!

    This is why I thought of returning the object being processed to the main form using a label or a textbox.

    What is the use of Timer for my application? Does it return the elapsed time?

    I hope you can be a little more descriptive?

    Thank you.


    • Edited by Clive2334 Monday, October 22, 2012 11:42 AM
    Monday, October 22, 2012 11:33 AM
  • Also,

    The method which has the foreach iteration is in a different class and its not in the Form class.

    How to link these two? 

    Further if I keep the timer.Interval to 1000 then does it limit all the iterations to 1000 mili seconds? What if a single foreach iteration in my application is faster than that? Than means implementing timer would increase the running time! Am I correct?

    I am a bit confused and I hope you can explain this to me a little more?

    Thank you John for your kind effort.

    Cheers


    • Edited by Clive2334 Monday, October 22, 2012 12:04 PM
    Monday, October 22, 2012 12:02 PM
  • When you run the code I posted do you see what you requested in your original post?  That is, do you see a label changing from 1/20000 to 2/20000 to 3/20000 to ...?

    The timer provides for presentation of this information to a user in an understandable manner.

    • Edited by JohnWein Monday, October 22, 2012 12:20 PM
    Monday, October 22, 2012 12:19 PM
  • Hi John,

    Yes I see the lable change. But how do I append it with my application? My program has two classes. The Form class and MyClass.

    Public class Myclass
    {
    List<MyObject> newdata= new List<MyObject>();
    Public List<MyObject> getObjects()
    {
    
    List<MyObject> alldata= new List<MyObject>();
    alldata=GetRecordsFromSQL(); // Gets all the records from the SQL server
    
    public int count_iteration= 0; 
    
    foreach(MyObject object in alldata)
    {
    //do some operation
    newdata.Add(object);
    }
    
    Return newdata;
    }
    }

    Then I have my form class which invoke Myclass method, getObjects().

    public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }
    
               private void button1_Click(object sender, EventArgs e)
            {
    Myclass class= new Myclass();
    class.getObjects();
            }
        }

    How can I link these two? How can I use the Timer to show the object number being processed?

    Does it limit each iteration to 1000 mili seconds? Or have I misunderstood the whole thing?

    I hope I am not troubling you. I certainly would like to learn how this is related to my peogram.

    Thank you very much John.

    Monday, October 22, 2012 12:43 PM
  • Replace your foreach loop with a timer loop.  In the Form class subscribe to the timer tick event and update the label.  Adjust the parameters to your liking.
    Monday, October 22, 2012 1:59 PM
  • This is exactly the context that `BackgroundWorker` was designed for.

    Do your work in the `DoWork` event of the background worker, and periodically call the `ReportProgress` event to update the UI.  Attach an event handler to the ProgressReported event to actually do the UI updating.

    Note that the DoWork event handler will run in a background thread, and that the ReportProgress event handler will run in the UI thread.

    Monday, October 22, 2012 2:29 PM
  • Word of warning, if you are not careful you can flood and lock your gui doing this.  This could be part the the reason your GUI does not appear to update :-).

    What you need is to offload the task onto a background worker (as suggested) then use a delegate on the main thread to update the gui:

    public void UpdateTextbox1(string text)
    {
        if (InvokeRequired) { Invoke( new Action<string>(UpdateTextbox1), text ); }
        else { textBox1.Text = text; }
    }


    jon.stromer.galley


    Monday, October 22, 2012 3:00 PM
  • Word of warning, if you are not careful you can flood and lock your gui doing this.  This could be part the the reason your GUI does not appear to update :-).

    No, his UI isn't updating because he's doing all of his work in the UI thread, so none of the updates are able to run until after the work has all finished.

     then use a delegate on the main thread to update the gui:

    public void UpdateTextbox1(string text)
    {
        if (InvokeRequired) { Invoke( new Action<string>(UpdateTextbox1), text ); }
        else { textBox1.Text = text; }
    }
    There's no need to do that at all.  The ReportProgress event runs in the UI thread; it's all taken care of for you.
    Monday, October 22, 2012 3:05 PM
  • Hi All,

    I am grateful to all of you. But unfortunately I do not understand any of above as I am not familiar with Timer or Background worker. I do not know how to implement them in my case. I am so sorry may be my knowledge of the subject is very poor. 

    Is there any material I should read that you can suggest or recommend? Also, I read about parallel threading today. Is it something similar to background worker?

    I do not understand why it is this complicated to handle a progress of a program in C# form programs.

    None of the articles I read helped me.

    I am all confused.

    Thank you enormously for your input.

    Cheers 

    Tuesday, October 23, 2012 3:19 AM
  • Greetings Clive.

    You should remove the call to Refresh from your method that changes the text.

     public void SetTextForTextBox(string myText)
     {
         //this.textBox9.Refresh();  // Remove this line.
         this.textBox9.Text = myText;
     }

    Tuesday, October 23, 2012 4:30 AM
  • Hi Clive,

    The first thing to understand how all this works, is to understand why your solution isn't working. 

    If the code below is what you are using:

    Public class Myclass
    {
    	List<MyObject> newdata= new List<MyObject>();
    	public int count_iteration= 0; 
    	Public List<MyObject> getObjects()
    	{
    
    		List<MyObject> alldata= new List<MyObject>();
    		alldata=GetRecordsFromSQL(); // Gets all the records from the SQL server
    
    		foreach(MyObject object in alldata)
    		{
    			//do some operation
    			newdata.Add(object);
    		}
    
    		Return newdata;
    	}
    }
    
    public partial class Form1 : Form
    {
            public Form1()
            {
                InitializeComponent();
            }
    
    	private void button1_Click(object sender, EventArgs e)
    	{
    		Myclass class= new Myclass();
    		class.getObjects();
            }
    }


    Then my explanation is that the reason why you aren't getting is because the class.getObjects() is executed in the same thread as in which the GUI is shown. So what you need to do is to run the code class.getObjects() in another thread. By doing so the GUI will not be blocked and your count will be updated. That's the reason why everyone here is suggesting to use the Timer Object or the Background worker. Here is an exmpale with two separate classes to show you how you can do it. 

    public partial class Form1 : Form
    	{
    		MyClass @class;
    		Timer timer;
    
    		public Form1()
    		{
    			InitializeComponent();
    			timer = new Timer();
    			timer.Interval = 1000; //this is in milli seconds. 
    			//The tick event is then fired in every 1000 ms.
    			timer.Tick += new EventHandler(timer_Tick);
    		}
    
    		void timer_Tick(object sender, EventArgs e)
    		{
    			//label is updated every second, because interval is set to 1000ms.
    			label1.Text = @class.CurrentIteration.ToString();
    			if (@class.IsStopped)
    				timer.Stop();
    		}
    
    		private void button1_Click(object sender, EventArgs e)
    		{
    			@class = new MyClass();
    			var t = new System.Threading.Thread(new System.Threading.ThreadStart(@class.ExtensionOperation));
    			t.Start(); //Run operation in different thread.s
    			timer.Start();
    		}
    
    		private void Form1_FormClosing(object sender, FormClosingEventArgs e)
    		{
    			//Override closing to stop the thread, otherwise the program will continue to run.
    			@class.Stop();
    			timer.Stop();
    		}
    	}
    
    	public class MyClass
    	{
    		public long CurrentIteration { get; set; }
    
    		public bool IsStopped { get; set; }
    
    		public void ExtensionOperation()
    		{
    			long iterations = 10000000000;
    
    			for (long i = 0; i < iterations && !IsStopped; i++)
    			{
    				CurrentIteration = i;
    			}
    
    			IsStopped = true;
    		}
    
    		public void Stop()
    		{
    			IsStopped = true;
    		}
    	}

    Hope this helps. 

    Here is a link which can help you understand threading: http://msdn.microsoft.com/en-us/library/aa645740(v=vs.71).aspx

    But if you only need to know whether the program is running, you could simply write to a file during your operation. And then check whether the file is being updated or not.

    I hope this helps.

    Regards,

    Kishen


    Wednesday, October 24, 2012 12:09 PM