0

I am struggling to workout how to create something that essentially pauses my while loop until my button1 is pressed, I know about the event handler button1_Click but I don't think that will work in this situation as I have lots of loops nested in each other on my form_load.

Any help would be highly appreciated!

This is a snipped of my code where I want the loop to be 'paused' with the notes:

while (reader2.Read())
{
    QuestionSpace = Convert.ToString(reader2["Question Space"]);
    label1.Text = QuestionSpace;
    if (button1.Click = true) // if the button is clicked)
    {
        // continue with the while loop (I am going to add an INSERT SQL query in here later)
    }
    else
    {
        // pause until the button is pressed
    }
}  

My whole code for the form:

public partial class CurrentlySetTestForm : Form
{
    private int QuestionID { get; set; }
    private string QuestionSpace { get; set; }
    public CurrentlySetTestForm()
    {
        InitializeComponent();
    }

    private void CurrentlySetTestForm_Load(object sender, EventArgs e)
    {
        string y = GlobalVariableClass.Signedinteacher;
        MessageBox.Show(y);
        Convert.ToInt32(y);

        string connectionString = ConfigurationManager.ConnectionStrings["myconnectionstring"].ConnectionString;
        SqlConnection connect = new SqlConnection(connectionString);

        connect.Open();

        SqlCommand command18 = new SqlCommand("SELECT [QuestionID] FROM QuestionStudentAssociation WHERE ( [StudentID]=@Signedinstudent)", connect);
        command18.Parameters.AddWithValue("@Signedinstudent", y);

        var reader = command18.ExecuteReader();

        while (reader.Read())
        {
            QuestionID = Convert.ToInt32(reader["QuestionID"]);

            SqlCommand command19 = new SqlCommand(@"SELECT [Question Space] FROM Questions WHERE ( [QuestionID] = @currentQID )", connect);
            command19.Parameters.AddWithValue("@currentQID", QuestionID);

            try
            {
                var reader2 = command19.ExecuteReader();

                while (reader2.Read())
                {
                    QuestionSpace = Convert.ToString(reader2["Question Space"]);
                    label1.Text = QuestionSpace;
                    if (button1.Click = true) // if the button is clicked)
                    {
                        // continue with the while loop (I am going to add an INSERT SQL query in here later)
                    }
                    else
                    {
                        // pause until the button is pressed
                    }


                }
            }
            catch (SyntaxErrorException ex)
            {
                MessageBox.Show(ex.Message);
            }
            finally
            {
                MessageBox.Show("Done one loop");
            }
        }
    }
}
chomba
  • 1,390
  • 11
  • 13
mot375
  • 99
  • 1
  • 13
  • 4
    If you block execution in the UI thread, UI won't respond to the user so she won't be able to click the button. You could consider putting the background task in a background thread and using a AutoResetEvent of using Tasks. – Mehrzad Chehraz Apr 26 '15 at 16:39
  • I'm not 100% sure what you mean by that – mot375 Apr 26 '15 at 16:41
  • If you want to execute a blocking code, you need to execute that in a thread other than the UI thread. – Mehrzad Chehraz Apr 26 '15 at 16:45
  • I'm sorry I don't really understand what a UI thread is would you be able to give me a small example in an answer? – mot375 Apr 26 '15 at 16:48
  • 1
    Try reading about [multi threading](http://en.m.wikipedia.org/wiki/Multithreading). To fix you code without bothering yourself with threading issues, try write the code to use the event handlers instead of a polling mechanism by a while loop. – Mehrzad Chehraz Apr 26 '15 at 16:57

3 Answers3

1

Sounds like your not ready to learn TPL

So maybe a BackgroundWorker , you can paint it on the form

To make the click cancel the background worker have a look at Cancel backgroundworker

I would some time to learn TPL as its going to create a simpler and more elegant solution.

As for pausing I would refactor the code, you should not keep the reader open waiting on the user.

Community
  • 1
  • 1
Steve Drake
  • 1,968
  • 2
  • 19
  • 41
1

You do want event-driven response to UI events, always. However, I guess that you don't want to split your logic into a state machine by hand (where each event triggers progress to the next state). Well, you're in luck, the C# compiler has some keywords to build state machines automagically so you don't have to manage the details.

There are actually two different mechanisms for continuation-passing style implemented in C#. The old one, yield return, works great if your UI events are pretty much interchangeable (or you're only interested in one). Works like this:

IEnumerator<int> Coroutine;

// this could be a Form_Load, but don't you need to get the user information before making the database connection?
void BeginQuiz_Click( object sender, EventArgs unused )
{
    Coroutine = RunQA();
}

IEnumerator<int> RunQA()
{
     // connect to DB

     // show first question on UI

     return ContinueQA();
}

IEnumerator<int> ContinueQA()
{
     // you can use a while loop instead if you really want
     for( int question = 0; question < questionCount; ++question )
     {
         // check answer
         if (/* too many wrong answers*/) {
               // report failure in DB
               yield break;
         }

         // get next question from DB

         // show new question on the UI

         // wait for UI action
         yield return question;
     }

     // report score in DB
      // update UI with completion certificate
}

void AnswerButton_Click( object sender, EventArgs unused )
{
    answer = sender;
    Coroutine.MoveNext(); // MAGIC HAPPENS HERE
}

void TimeoutTimer_Tick( object sender, EventArgs unused )
{
    answer = TimeoutTimer;
    Coroutine.MoveNext();
}

The magic comes from yield return. Every time the function reaches yield return, the compiler saves what you were doing. When the button click event comes and calls MoveNext, the compiler generates code that starts where yield return paused everything, and keeps going from there until the next yield return.

Important note, the code inside ContinueQA doesn't start when RunQA() does return ContinueQA(); It actually starts on the first MoveNext(). So split your code between RunQA() and ContinueQA accordingly.

If you need different pause reasons at different places in your code, then async/await will be more helpful.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
0

A better way to handle this would be the use of a timer. This would allow the form to draw it's controls and handle all input, such as clicking the button. Adjust the timer interval (ms) to your needs.

Another way of doing this would be, as Mehrzad Chehraz said, to use multi-threading.

On a side note, I would strongly recommend condition checks over the try/catch checks if possible.

Enable/Disable the timer using the button and call the loop when the timer ticks. Example:

    Timer loopTimer = new Timer();

    private void Form1_Load(object sender, EventArgs e)
    {
        loopTimer.Interval = 100;
        loopTimer.Tick += loopTimer_Tick;
        loopTimer.Enabled = true;
    }

    void loopTimer_Tick(object sender, EventArgs e)
    {
        //perform the loop here at the set interval
    }

    private void button1_Click(object sender, EventArgs e)
    {
        //pause/play the loop
        loopTimer.Enabled = !loopTimer.Enabled;
    }
aaronedmistone
  • 929
  • 10
  • 17