简体   繁体   中英

Pause the while loop until the button is pressed w/o using event handler C#

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");
            }
        }
    }
}

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.

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.

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;
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM