How to correctly pause/delay Windows Forms application - c#

I am a beginner to the OOP and the C#.
I am working on a quiz game using the Windows Forms.
My problem is related to two classes, the form and the game logic.
I have a basic UI with classic Froms controls. Take a look.
The thing I want to achieve is, when a player presses any answer button, it will higlight that pressed button by red or green color, depending on if it is right or wrong answer. After changing the color I want the program to wait for a while and then go to the next question.
Probelm is, that I don´t know how to achieve this correctly. I don´t know how to work with threads and how exactly the Form app works related to threads. Should I use a thread sleep or a timer or a async?
I will show you the method in game logic class which should handle this.
public static void Play(char answer) //Method gets a char representing a palyer answer
{
if (_rightAnswer == answer) //If the answer is true, the button should become green
{
Program.MainWindow.ChangeBtnColor(answer, System.Drawing.Color.LightGreen);
_score++;
}
else //Otherwise the button becomes Red
{
Program.MainWindow.ChangeBtnColor(answer, System.Drawing.Color.Red);
}
//SLEEP HERE
if (!(_currentIndex < _maxIndex)) //If it is the last question, show game over
{
Program.MainWindow.DisplayGameOver(_score);
}
else //If it is not the last question, load next question and dispaly it and finally change the button color to default
{
_currentIndex++;
_currentQuestion = Database.ListOfQuestions.ElementAt(_currentIndex);
_rightAnswer = _currentQuestion.RightAnswer;
Program.MainWindow.DisplayStats(_score, _currentIndex + 1, _maxIndex + 1);
Program.MainWindow.DisplayQuestion(_currentQuestion.Text);
Program.MainWindow.DisplayChoices(_currentQuestion.Choices);
}
Program.MainWindow.ChangeBtnColor(answer, System.Drawing.SystemColors.ControlLight);
}
I don´t want to completely block the UI but also I don´t want users to make other events by pressing other buttons during the pause. Because it will result in improper run of app.

If the program is really simple and you do not want to implement Threads I would suggest using Timer. Just start your Timer when clicking answer button. Your timer should contain function which would stop itself after some time and do other actions needed (e.g. pick another question).

Once the user has selected an answer you can disable all the buttons so they can't press anything else.
Then start a timer so you don't block the UI. The timer is basically a thread but handles all the threading for you so you don't have to worry about that aspect.
When the timer reaches the desired delay stop it and fire an event to select the next question.

At //SLEEP HERE add this line of code
Timer timer = new Timer(new TimerCallback(timerCb), null, 2000, 0);
The 2000 is milliseconds and is the wait time, timerCb is a call back method.
Also under that disable all your buttons so new events wont be generated.
private void timerCb(object state)
{
Dispatcher.Invoke(() =>
{
label1.Content = "Foo!";
});
}
You can do whatever you want in the callback, however if you do something that would change anything in the UI, you need to use the Dispatcher like I have changing the label content.

Suspending execution in a GUI scenario is very easy thanks to await:
await Task.Delay(2000);
This does not block the UI.
You should research what await does and how to use it. If you have never heard about it and are programming WinForms you are doing something wrong.
No timers or threads are needed. No callbacks, no Invoke.

Related

GUI items stop working when entering Async function in a MVP pattern

I am using C# and Visual Studio 2013. I've read up on design patterns and I'm currently using the MVP design pattern.
My issue is that I start a Wait Cursor on the mouse and what I want it to do is go into an Aysnc function and perform the time consuming action, but keep the wait cursor going until the time consuming action is complete to assure people the program is still running. Outside of the MVP pattern I've done this before and the whole process works as it should. 1. start wait cursor, 2. start long function, 3. long function finishes, 4. change cursor back to default. However when I go from my presenter to my model from what I can tell in the debugger the wait cursor changes back to a default cursor as soon as the Async function begins.
I've done various tests to see if this affects all GUI components. It also affects marquee progress bars, but if I have a message box after my Async call it will activate the message box after the task is finished.
Is there something I'm missing that happens between the cursor being turned into a wait cursor and the task starting? I've looked around and I don't think anyone else has experienced this problem from what I can tell...
ex code: (please excuse sloppiness writing this from memory so syntax may be closer to pseudo code)
//View
//start wait cursor
public void StartWaitCursor()
{
Cursor.Current = Cursors.WaitCursor;
}
//stop wait cursor
public void StopWaitCursor()
{
Cursor.Current = Cursors.Default;
}
//Presenter
TheView.StartWaitCursor();//For some reason this goes to default cursor before task is finished
await Task_MethodAsync();
TheView.StopWaitCursor();
It is hard to determine anything from the pseudo code you provided.
From this article, the method from which you are calling Task_MethodAsync(); must be inside async method, in your Presenter. I may be wrong though.
Other than that, I'd start debugging and put a watch on your cursor and see with each step how/when it changes.

Progress indicator in C# Winforms

I have created some basic applications using windows forms C#.
What I am trying to achieve is that I have some task taking place inside a function. While executing that task I need to display a message box (with no buttons) with the text "Configuring...". I also need to blink this text. How can I do that?
Do I need to have another form for this? After completing this task this form needs to be hidden or closed?
I have googled this but couldn't see an answer, may be because of my unclear question in google.
If you really need a progress indicator you have to do as toATwork said, and do a Background worker
However, it is not a really async task, so you might find it hard to make it work properly
If you finally don't care about the message, and just need to show the users that something is happening, you can always use:
// To start
Cursor cursor = Cursor.Current;
Cursor.Current = Cursors.WaitCursor;
// To finish
Cursor.Current = Cursors.Default;
This will just put the mouse cursor in "loading", but it might work for you
You can use a label on your form and change its text in function Body.
{
//your function body
label.Text="Configuring";
}

C# breaking out of a loop from separate class/method

I have a block of code that repeats using a "for" loop, and each loop constructs a form to display some text. Some thing like the shorthand code below.
Main()
{
For (int x: x<=20; x++)
{
createform(string[x]);
}
}
So for each loop a different string is passed to a method that will construct a form as below.
createform void (string input_)
{
...
code to build form and add a button "cancelbutton"
form.text = intput_
....
form.cancelbutton.Click += // and I want this to cause the original loop to end....
}
No I know I could use the button to make int x greater than 20 and that would end the loop, but I don't actually know what the max value will be as this is dynamic. Again I could work this out and do he same thing but it seems a bit "messy".
Is there a neater way to cause the button click to exit the loop. How about if the Createform method is in a separate class to main, does that make any difference?
If your loop runs from 0 to 20 (or even over 9000) the user won't be able to click the cancel button in time. In fact, since it is all on one thread, the loop will finish before the UI responds to the click, but maybe I have misunderstood. Could you just have a boolean flag which you check each time you enter the loop and set it to false once the user clicks the button?
Just add a variable and code the click event:
static bool clicked;
Main()
{
clicked = false;
For (int x: (x<=20) && !clicked; x++)
{
createform(string[x]);
}
}
public static void Click_Detector(object sender, EventArgs e) {
clicked = true;
}
Your routine would need to do something like this:
createform void (string input_)
{
...
code to build form and add a button "cancelbutton"
form.text = intput_
....
form.cancelbutton.Click += MainClass.Click_Detector;
}
You shouldn't be using a loop to create each form. Assuming you have a submit button. Every time the user clicks submit, you should explicitly show the next form. That way, if a user clicks cancel, you don't have to worry about the rest of the forms.
The first solution that comes to my mind is: return value of the method True/False and in foreach check for its return value, if False=> break.
Your psuedo code isn't indicating how you are showing the form in CreateForm(). Is this going to be a modal or non modal? It would have to be modal, otherwise, you'd just keep creating forms until the cows come home. Remember, adding a handler to the click button doesn't actually execute the handler method until the click button is clicked.
Presumably your click event handler could set the value of x to be greater than 20, but since you said you're not sure what the maximum is, you could have a do loop governed by a boolean flag, and your createform() could set the state of the flag.
I'd recommend rethinking your problem space, as this overall approach seems really convoluted. I don't understand your UI, but it seems like your intent is to have the user control the exit of an infinite loop, like those old RPGs that ask you "Do you want to go on an adventure Yes/No", and it would continue asking you until you hit "Yes". These are pointless and confusing.
Add a boolean to the class and have the click event set the boolean value. Then you can break from the loop. However, if your real world use is the question above I would agree with the answer given by mikerobi.
One good solution would be to use the CancelAsync() method of the BackgroundWorker class: http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.cancelasync.aspx
If you don't want to work with a separate thread to improve the performance, that I don't recommend, is to set a class level boolean value when the button clicked, then the for loop checks that boolean value in each next process.

Can't find image/child that was just created

C#
My code is in this order:
create and add an image as a child
switch to the tab where the image has been added
run code based on that child/image
My problem is, when number 3 runs, it can't find the child, and things break. It get's weird, if I put a MessageBox.Show() right after number 2, then click OK on the MessageBox, then number 3 runs correctly, it finds the child. What am I missing?
Try putting an DoEvents() just before #3.
Because of the MessageBox you are letting the application pump all the messages, so with the DoEvents() you are pumping the messages.
I create a similar method to the Forms Application.DoEvents() to pump messages in WPF with this code,
public static void DoEvents()
{
Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, new ThreadStart(delegate { }));
}
It can't be called often, but it can help sometimes.

Paragraph.BringIntoView() works only when focused

I am working on a text editor that is based on RichEditBox. I have implemented functionality "Go to line" which eventually resolves to
TextPointer.Paragraph.BringIntoView();
Along with this I also set the caret position.
What I found out is that BringIntoView only works when I click on the RichEditBox first (focus it). Otherwise it seems to get ignored. I can see that the caret position has been adjusted by the code around BringIntoView though.
Does anybody know what is the reason/nature of that problem? How can I overcome it?
Found a workaround for this, not sure if it will work in a pure WPF environment, in my case I'm running WPF inside a mainly Windows Forms solution using WPF UserControls where needed.
Instead of invoking BringIntoFocus() immediately, defer it to a later moment by adding it to a queue that gets handled by a timer. For example:
System.Windows.Forms.Timer DeferredActionTimer = new System.Windows.Forms.Timer() { Interval = 200 };
Queue<Action> DeferredActions = new Queue<Action>();
void DeferredActionTimer_Tick(object sender, EventArgs e) {
while(DeferredActions.Count > 0) {
Action act = DeferredActions.Dequeue();
act();
}
}
In your forms constructor, or in the OnLoad event add:
DeferredActionTimer.Tick += new EventHandler(DeferredActionTimer_Tick);
DeferredActionTimer.Enabled = true;
Finally, instead of calling TextPointer.Paragraph.BringIntoView(); directly, call it like this:
DeferredActions.Enqueue(() => TextPointer.Paragraph.BringIntoView());
Note that the Windows Forms timer kicks events off in the main thread (via the message pump loop). If you have to use another timer you need a bit of extra code. I'd recommend you to use System.Timers.Timer rather than the System.Threading.Timer (it's a little more thread-safe). You would also have to wrap the action in a Dispatcher.Invoke structure. In my case, the WinForms timer works like a charm.
Can't you just give the RichTextBox(?) focus first then, using Keyboard.Focus(richTextBox) or richTextBox.Focus()?

Categories