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

104 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 answers

1
al_amanat 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
Euphoric 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;
        }
    }
}