0

I am writing a piece of code that takes a user through a guided script. The user will have a few seconds to answer before the answer will be displayed.

So far, my code looks like this:

GuidedExercise3 exercise3 = new GuidedExercise3();
string AntonioAnswer = string.Empty; // expected answer

int upperBound = exercise3.Script.Count - 1; // zero-based counting
for(int i = 0; i < upperBound; i += 2)
{
    labelInstructions.Text = exercise3.Script[i].TextToSpeak;
    AntonioAnswer = exercise3.Script[i+1].TextToSpeak; // answer
    SetTimer(AntonioAnswer, txtAntonio); // set timer sending in the answer and the TextBox object.
    sysTimer.Start();
}

The odd lines of a List contain the question and the even lines contain the expected answer. My question is how do I display the question for X seconds and then get the user's answer in this WinForms app and then display the answer when the timer elapses, keeping the user from going to the next step of the script but allowing them to answer the question (which is in a Textbox).

I examined this StackOverflow question, but it doesn't match: Implementing a loop using a timer in C#

Alexei Levenkov
  • 98,904
  • 14
  • 127
  • 179
Su Llewellyn
  • 2,660
  • 2
  • 19
  • 33
  • I think you need to lookup threading in c# – GuidoG Nov 03 '17 at 16:49
  • 3
    You don't need a timer or threads. You can use `await Task.Delay(...)` to await asynchronously (ie without blocking the UI) in an asynchronous event handler, eg `async void Button1_Click(...){ ..... for(){ ....;await Task.Delay(30000);...` – Panagiotis Kanavos Nov 03 '17 at 16:52
  • That's handy! I will try that out. – Su Llewellyn Nov 03 '17 at 16:57
  • @GuidoG. You are right! I found this great article on MSDN just a second ago: ["Safe, Simple Multithreading in Windows Forms, Part 1"](https://msdn.microsoft.com/en-us/library/ms951089.aspx). Thanks! – Su Llewellyn Nov 03 '17 at 16:59
  • If things get more complex, consider a workflow engine, such as .NET's [WF4](http://code-coverage.net/workflow-foundation-tutorial-part-2-the-basics/) or a 3rd-party one. – Tom Blodget Nov 03 '17 at 19:29
  • @SuLlewellyn - You do **not** need threading for this. Going down that path is going to make this harder for you. – Enigmativity Nov 04 '17 at 23:33

2 Answers2

1

Here's how I would handle something like this:

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

        MoveNextQuestion();

        timerAnswer.Interval = 5000;
        timerAnswer.Start();
    }

    private string[] _questionsAndAnswers = new[]
    {
        "What colour is the sky?",
        "Blue",
        "What do chickens lay?",
        "Eggs",
    };

    private int _currentIndex = -2;

    private void timerAnswer_Tick(object sender, EventArgs e)
    {
        MoveNextQuestion();
    }

    private void buttonAnswer_Click(object sender, EventArgs e)
    {
        MoveNextQuestion();
    }

    private void MoveNextQuestion()
    {
        _currentIndex += 2;
        if (_currentIndex < _questionsAndAnswers.Length)
        {
            labelQuestion.Text = _questionsAndAnswers[_currentIndex];
        }
        else
        {
            timerAnswer.Stop();
        }
    }
}
Enigmativity
  • 113,464
  • 11
  • 89
  • 172
  • This answer is useful for the problem I posed to begin with. So people needing to know how this might be done could look at this code. I'm saving it in my algorithms folder. – Su Llewellyn Nov 09 '17 at 17:43
-1

I was able to get this working fairly easily with a BackgroundWorker object. See the following article at MSDN for the exact coding. BackgroundWorker Class. In particular they have two examples in the documentation and the first example is sufficient. The BackgroundWorker class is allowing my UI to continue to accept user input while waiting on a timed answer. It displays the correct answer on the RunWorkerComplete event. So the BackgroundWorker's RunAsync gets called in my for loop.

I've run into the additional issue of the BackgroundWorker not returning control to my loop. I'm looking into that problem separately.

Su Llewellyn
  • 2,660
  • 2
  • 19
  • 33
  • This is a bad idea. You just need to use a `System.Windows.Forms.Timer`. – Enigmativity Nov 04 '17 at 23:34
  • But BackGroundWorker was designed to allow the user to use the UI while background processing could occur without tying up the UI thread. So, I don't understand your -1. – Su Llewellyn Nov 05 '17 at 22:57
  • Yes, `BackgroundWorker`is used to allow background processing to not tie up the UI, but you're not doing any background processing. You're just wanting to respond to one of two events. The CPU is idle otherwise. – Enigmativity Nov 06 '17 at 00:16
  • So, I ended up refactoring the code for this. I don't time the user any more, I let them take as much time as they want. I ended up not needing multi-threading, when I started out thinking it would be necessary. – Su Llewellyn Nov 09 '17 at 17:42