How to continue through a UI with multiple loops and waiting for user feedback?

123 Views Asked by At

I am writing in C# and using .net framework 3.5. I am running through multiple loops that on each iteration build the UI and then wait for user feedback (waiting for a Pass or Fail button click). Here is what I am looking to do:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();

        // add both button handlers to function btn_Click

        for(int test = 0; test < 5; test++)
           for(int variation = 0; variation < 4; variation++)
              for(int subtest = 0; subtest < 3; subtest++)
              {
                // call function to update GUI
                // may need to do stuff while at this state
                // wait for user to click pass/fail button
              }
    }

    private void btn_Click(object sender, EventArgs e)
    {
        // pass button pressed, inform for loop to iterate to next test
        // if fail button, then stop tests
    }
}

The wait inside the for loop is what gets me. Since this is single threaded I was running into issues with Sleep and do not like the idea of putting the check for a button press inside a while loop with a global variable. I tried AutoResetEvent to wait for the button click but that gave me issues too. What is the best way to handle this?

I did write the code in a way that I could try to implement it but do not think it is a very elegant solution:

public partial class Form1 : Form
{
    int test;
    int variation;
    int subtest;

    public Form1()
    {
        InitializeComponent();

        test = 0;
        variation = 0;
        subtest = 0;

        // update GUI
        btnUpdate.Text = "Hello # " + test.ToString() + "." + variation.ToString() + "." + subtest.ToString();
    }

    private void btnUpdate_Click(object sender, EventArgs e)
    {
        subtest++;

        if (test >= 5)
            if (variation >= 4)
                if (subtest >= 3)
                {
                    // done case
                    Console.WriteLine("Tests are complete");
                    btnUpdate.Visible = false;
                }

        if (subtest > 3)
        {
            subtest = 0;
            variation++;
        }

        if (variation > 4)
        {
            variation = 0;
            test++;
        }

        Console.WriteLine("I was clicked");

        // update button
        btnUpdate.Text = "Hello # " + test.ToString() + "." + variation.ToString() + "." + subtest.ToString();
    }

}

Thanks in advance!

2

There are 2 best solutions below

1
On

Just use a separate thread to UpdateUI and block it while button not pressed:

public partial class Form1 : Form
{
    AutoResetEvent are = new AutoResetEvent(true);

    public Form1()
    {
        InitializeComponent();

        _updateGUI = new UpdateGUIDelegate(UpdateGUI);

        Task.Factory.StartNew(() => BuildTest());
    }

    private void BuildTest()
    {

        for (int test = 0; test < 5; test++)
            for (int variation = 0; variation < 4; variation++)
                for (int subtest = 0; subtest < 3; subtest++)
                {
                    are.WaitOne();
                    if (this.InvokeRequired)
                        this.Invoke(_updateGUI, test, variation, subtest);
                    else
                        UpdateGUI(test, variation, subtest);

                }
    }

    delegate void UpdateGUIDelegate(int test, int variation, int subtest);
    private UpdateGUIDelegate _updateGUI;
    private void UpdateGUI(int test, int variation, int subtest)
    { }

    private void btn_Click(object sender, EventArgs e)
    {
        are.Set();   
    }
}
0
On

Threads are overkill for this kind of scenario. You just need to do some code cleanup and refactoring and it will immediately look much nicer and logical.

public class LoopCounter
{
    int test = 0;
    int variation = 0;
    int subtest = 0;

    const int MAX_TEST = 5;
    const int MAX_VARIATION = 4;
    const int MAX_SUBTEST = 3;

    public int Test {get{return test;}}
    public int Variation {get{return variation;}}
    public int Subtest {get{return subtest;}}

    public bool DoNext()
    {
        if (test >= MAX_TEST) // test for end all cycling
            return false;

        subtest++;
        if (subtest < MAX_SUBTEST)
            return true;

        subtest = 0;

        variation ++;
        if (variation < MAX_VARIATION)
            return true;

        variation = 0;

        test++;
        if (test < MAX_TEST)
            return true;

        return false;
    }
}

Then, you can easily use this code as follows:

public partial class Form1 : Form
{
    LoopCounter counter = new LoopCounter();

    public Form1()
    {
        InitializeComponent();

        UpdateUI();
    }

    private void UpdateUI()
    {
        // update GUI
        btnUpdate.Text = "Hello # " + counter.Test.ToString() + "." + counter.Variation.ToString() + "." + counter.Subtest.ToString();
    }

    private void btnUpdate_Click(object sender, EventArgs e)
    {
        Console.WriteLine("I was clicked");

        if (counter.DoNext())
        {
            UpdateUI();
        }
        else
        {
            // done case
            Console.WriteLine("Tests are complete");
            btnUpdate.Visible = false;
        }
    }
}