none
How to stop a Loop?

    Question

  • Hi,
    In my windows form there are two buttons , Start and Stop. When i press the Start button a loop (While loop or a foreach or any other loop) starts to execute and it will take some time for it to end.

    My question is how to stop the loop before it ends by pressing the Stop button?
    How can I code the Stop button to stop the Loop?

    Another thing that I noticed is that when the loop is operational the buttons are not active till the loop stops.

    Please let me know the solution for this.

    Thank You.
    Tuesday, October 14, 2008 4:38 PM

Answers

  • You should use a BackgroundWorker to accomplish this task.  You'll also want to call "break" from within the loop. 

    In the following code, I have a form that runs a loop in the DoWork event handler of the BackgroundWorker.  It sleeps for 1 second, then sets the text of a label.  Notice I've had to use the InvokeRequired property and the Invoke method to redirect the command to the UI.  When I click the stop button, I set a local form-level field that will indicate to the loop that it needs to stop.  Once this field is set to true, the for loop breaks, and the method ends.  Take a look at this code, and examine it thoroughly.  Look up the classes I listed on MSDN.  This is a concept you'll need to know if you're going to program in Windows Forms.

    For future reference, please post Windows Forms questions to the Windows Forms form.  See the announcement at the top of the Visual C# General page for more information on that.

    public class LoopForm : Form  
    {  
        BackgroundWorker worker;  
        Button start;  
        Button stop;  
        Label label;  
        bool abortLoop = false;  
     
        public LoopForm()  
        {  
            start = new Button();  
            start.Location = new Point(100, 50);  
            start.Text = "Start";  
            start.Click += new EventHandler(start_Click);  
            stop = new Button();  
            stop.Location = new Point(100, 100);  
            stop.Text = "Stop";  
            stop.Click += new EventHandler(stop_Click);  
            label = new Label();  
            label.Text = "Click Start";  
            label.Location = new Point(100, 150);  
     
            this.Controls.Add(start);  
            this.Controls.Add(stop);  
            this.Controls.Add(label);  
     
        }  
     
        void stop_Click(object sender, EventArgs e)  
        {  
              
            abortLoop = true;  
            worker = null;  
        }  
     
        void start_Click(object sender, EventArgs e)  
        {  
            if (worker == null)  
            {  
                abortLoop = false;  
                worker = new BackgroundWorker();  
                worker.DoWork += new DoWorkEventHandler(worker_DoWork);  
                worker.RunWorkerAsync();  
            }  
        }  
     
        delegate void SetLabelDelegate(string value);  
     
        void SetLabel(string value)  
        {  
            if (this.InvokeRequired)  
                this.Invoke(new SetLabelDelegate(SetLabel), value);  
            else 
                this.label.Text = value;  
        }  
        void worker_DoWork(object sender, DoWorkEventArgs e)  
        {  
              
            for (int i = 0; i < 1000000; i++)  
            {  
                if (abortLoop)  
                {  
                    abortLoop = false;  
                    break;  
                }  
                SetLabel("Iteration " + i);  
                Thread.Sleep(1000);  
            }  
        }  

    David Morton - http://blog.davemorton.net/
    • Marked as answer by GuyHunt Thursday, October 16, 2008 3:14 PM
    Tuesday, October 14, 2008 4:54 PM

All replies

  • While your code is busy executing the loop, it is utilizing the UI thread, so your form will not be accessible.  What you need to do is execute your loop on a separate thread like so:

    System.Threading.Thread thread=null;

    StartButton_Click(object sender, EventArgs e)
    {
        thread = new System.Threading.Thread(new System.Threading.ThreadStart(DoLoop));
       thread.IsBackground = true;
       thread.Start();
    }

    StopButton_Click(object sender, EventArgs e)
    {
       if(thread !=null && thread.IsRunning)
           thread.Stop();
    }

    DoLoop()
    {

    //loop goes here

    }


    Note: Syntax may be iffy, Intellisense has ruined me....
    Note2:  You cannot do anything to affect the UI from within the DoLoop method since it is being executed on a separate thread.  If you need to affect the UI from within DoLoop() you need to use the Invoke() method.

    If you're using .NET 2.0 or greater you could look into using the BackgroundWorker item which is found in the Toolbox.


    If this answers your question, please mark the question as answered.
    Tuesday, October 14, 2008 4:47 PM
  • You should use a BackgroundWorker to accomplish this task.  You'll also want to call "break" from within the loop. 

    In the following code, I have a form that runs a loop in the DoWork event handler of the BackgroundWorker.  It sleeps for 1 second, then sets the text of a label.  Notice I've had to use the InvokeRequired property and the Invoke method to redirect the command to the UI.  When I click the stop button, I set a local form-level field that will indicate to the loop that it needs to stop.  Once this field is set to true, the for loop breaks, and the method ends.  Take a look at this code, and examine it thoroughly.  Look up the classes I listed on MSDN.  This is a concept you'll need to know if you're going to program in Windows Forms.

    For future reference, please post Windows Forms questions to the Windows Forms form.  See the announcement at the top of the Visual C# General page for more information on that.

    public class LoopForm : Form  
    {  
        BackgroundWorker worker;  
        Button start;  
        Button stop;  
        Label label;  
        bool abortLoop = false;  
     
        public LoopForm()  
        {  
            start = new Button();  
            start.Location = new Point(100, 50);  
            start.Text = "Start";  
            start.Click += new EventHandler(start_Click);  
            stop = new Button();  
            stop.Location = new Point(100, 100);  
            stop.Text = "Stop";  
            stop.Click += new EventHandler(stop_Click);  
            label = new Label();  
            label.Text = "Click Start";  
            label.Location = new Point(100, 150);  
     
            this.Controls.Add(start);  
            this.Controls.Add(stop);  
            this.Controls.Add(label);  
     
        }  
     
        void stop_Click(object sender, EventArgs e)  
        {  
              
            abortLoop = true;  
            worker = null;  
        }  
     
        void start_Click(object sender, EventArgs e)  
        {  
            if (worker == null)  
            {  
                abortLoop = false;  
                worker = new BackgroundWorker();  
                worker.DoWork += new DoWorkEventHandler(worker_DoWork);  
                worker.RunWorkerAsync();  
            }  
        }  
     
        delegate void SetLabelDelegate(string value);  
     
        void SetLabel(string value)  
        {  
            if (this.InvokeRequired)  
                this.Invoke(new SetLabelDelegate(SetLabel), value);  
            else 
                this.label.Text = value;  
        }  
        void worker_DoWork(object sender, DoWorkEventArgs e)  
        {  
              
            for (int i = 0; i < 1000000; i++)  
            {  
                if (abortLoop)  
                {  
                    abortLoop = false;  
                    break;  
                }  
                SetLabel("Iteration " + i);  
                Thread.Sleep(1000);  
            }  
        }  

    David Morton - http://blog.davemorton.net/
    • Marked as answer by GuyHunt Thursday, October 16, 2008 3:14 PM
    Tuesday, October 14, 2008 4:54 PM
  • Hello GuyHunt,
        If you want to dynamically stop a loop from finishing, add: "break;" where you want it to stop. The "break;" keyword breaks out of and stops any active loop.

    Hope this helps!
    Thanks!
    chukrum47
    How are a plum and a rabbit similar? They're both purple, except for the rabbit.
    Wednesday, October 15, 2008 1:10 AM
  • Chukrum47,
    But, if the loop is running in same thread as the GUI, the app will appear to hang until the loop completes.  That would not provide the opportunity to even click STOP and execute the break.

    GuyHunt,
    C# and Threads are easy when you start with BackgroundWorker as David suggests.   You'll notice David uses "Invoke" to communicate information to the Form's label.  It's a more difficult concept than say using the BackgroundWorker's ReportProgress handler.  But it's a concept worth learning because because of its prolific use in threads.  And threads are the best practice for what you are describing.

    If you choose to use BigTuna99's approach, then in your loop, if you need to update a control on the Form (the GUI thread), then lift the code from David's post for where he updates the label and use that as an example for other controls.  You have to do this or something similar to update controls in another thread because controls are generally not thread safe.


    Les Potter, Xalnix Corporation, Yet Another C# Blog
    Wednesday, October 15, 2008 1:32 AM
  • You can use a timer rather than threading, if you want to stay on the UI thread:

      public partial class Form1 : Form

      {

        public Form1()

        {

          InitializeComponent();

          timer1.Interval = 1;

        }

        Boolean StopLoop;

        private void button1_Click(object sender, EventArgs e)

        {

          if (button1.Text == "Stop")

          {

            button1.Text = "Start";

            StopLoop = true;

          }

          else

          {

            button1.Text = "Stop";

            StopLoop = false;

            timer1.Start();

          }

        }

        private void timer1_Tick(object sender, EventArgs e)

        {

          timer1.Stop();

          for (int i = 1; i < 1000000000; i++)

          {

            int j = i/i;

            if (i % 1000000 == 0) Application.DoEvents();

            if (StopLoop) return;

          }

        }

      }

    Wednesday, October 15, 2008 6:01 AM
  •  Here is way to stop and if we want in certain cond..


    private void button1_Click(object sender, EventArgs e)

        {

          if (button1.Text == "Stop")

          {

            button1.Text = "Start";

            i=1000000001;
          }

          else

          {

            button1.Text = "Stop";

            StopLoop = false;

            i=0

          }

        }

       
    this type cant be used if we want to use the i value once again where we stop
    for ex:

       we i @ 22

    by this we cant start from 23 but when it meets the condition the their no need to enter loop once again then this is helpful.


    do things different
    Wednesday, October 15, 2008 6:11 AM
  • In C#, the Application.DoEvents() call is avoided in most programs.  That's because, just like with VB6, DoEvents() introduces potential re-entrancy problems, particularly as the GUI becomes more complex.

    As written, there are no problems with the timer approach and DoEvents.  For that matter, you could use DoEvents without the timer.  You just have to consider what will happen if another event could re-enter your event handler.

    Les Potter, Xalnix Corporation, Yet Another C# Blog
    Wednesday, October 15, 2008 1:08 PM
  • ThanX all for the answers.

    David, your answer was the perfect solution for my problem. ThanX again.
    • Edited by GuyHunt Thursday, October 16, 2008 3:17 PM
    Thursday, October 16, 2008 3:15 PM
  • Wow thanks. This is great help. Coming from ASP.NET this is exactly what I need. 
    Thursday, February 06, 2014 2:38 PM