C# 10 second timer on combox selected Index - c#

After being out of scripting for ages I have decided to learn a programming language and I have gone for C#. I'm getting along pretty well but now for the first time I seem to have been faced with a problem that I have not been able to solve with google.
I am making a simulated aircraft system as a learning exercise and I want to invoke a loop when an option is selected from a drop down combobox.
I have a combobox/list with three options which simulates the starter switch, the values are (0)Off, (1)On, (2)Ignition Only . In the real aeroplane, when 'On' is selected the switch locks in place for 10 seconds and then releases. So what I am trying to achieve is :
private void comboBox2_SelectedIndexChanged(object sender, EventArgs e)
{
if (starterRight.SelectedIndex == 0)
{
//Starter is off
eng2Start.Value = 0;
}
if (starterRight.SelectedIndex == 1)
{
//starter is on
//Start Timer
eng2Start.Value = 1;
if (eng2Tourqe >= 6000)
{
//open fuel valve
// set Hot Start counter to 0
}
else
{
//ensure fuel valve stays closed
// set Hot Start counter to x+1
}
// End of Timer
// set selected index back to 0
(starterRight.SelectedIndex == 0)
}
}
I have googled and googled and the more I read the more I am getting lost in this. I have found answers containing a mass of code which I am not able to fully decipher just yet.
Is it possible to do what I want to do?
Thanks in advance for your time.

You can Add Timer to your Form and Set the Interval property to 10000(10 seconds).
from code:
if (starterRight.SelectedIndex == 1)
{
//starter is on
//Start Timer
timer1.Enabled=true;
}
//in timer tick Event write the following:
private void timer1_Tick(object sender, EventArgs e)
{
timer1.Enabled=false;
//Statements to start aircraft
}

You could achieve this by setting it to true (or selected, or whatever you want) sleeping for 10 seconds, like this:
Thread.Sleep(10000) ;
and then set it back to false (or unselect, or whatever you want)
Another way to go would be to start a background thread that will sleep for ten seconds, and then call a method that will "unset" the button, this way, not blocking the GUI ...
Or depending on what you're using, I could probably come up with other options, but i'll take it you're trying to learn the basics atm ... :)

See this one msdn timer
And you can use Threed.Sleep(10000);

I think this should works. I didn't compile it, but with this you lock the swtich, do your stuff checking a timer that when arrives to 10 sec, re-enable your switch.
private void comboBox2_SelectedIndexChanged(object sender, EventArgs e)
{
if (starterRight.SelectedIndex == 0)
{
//Starter is off
eng2Start.Value = 0;
}
if (starterRight.SelectedIndex == 1)
{
//starter is on
starterRight.enable = false;
StopWatch sw = new StopWatch();
sw.Start();
eng2Start.Value = 1;
if (eng2Tourqe >= 6000)
{
//open fuel valve
// set Hot Start counter to 0
}
else
{
//ensure fuel valve stays closed
// set Hot Start counter to x+1
}
if (sw.ElapsedMilliseconds <= 10000)
{
do
{
//Dummy Loop
}
while (sw.ElapsedMilliseconds > 10000)
sw.Stop();
}
else
{
// set selected index back to 0
sw.Stop();
starterRight.Enabled = true;
(starterRight.SelectedIndex == 0)
}
}
}

You can use a Timer and the Tick event. Disable your switch, and when the Timer tick, Enabled it.
Timer timerSwitchOn;
public SomeConstructor()
{
timerSwitchOn = new Timer(){Interval = 10*1000}; // 10 seconds
timerSwitchOn.Tick += new EventHandler(timerSwitchOn_Tick);
}
private void comboBox2_SelectedIndexChanged(object sender, EventArgs e)
{
if (starterRight.SelectedIndex == 1)
{
//starter is on
starterRight.Enabled = false;
timerSwitchOn.Start();
}
}
void timerSwitchOn_Tick(object sender, EventArgs e)
{
timerSwitchOn.Stop();
starterRight.Enabled = true;
// set selected index back to 0
starterRight.SelectedIndex = 0;
}

In my initial question I said I had wanted to invoke a loop, I meant a timer but it seems that you all figured that out.
Thank you for the rapid answers, I am going to get stuck into them this weekend and see if I can solve my problem.

Related

Make Picturebox Bounds "collision" add to score Label

Trying to make a simple C# program where a ship goes through several "checkpoints" and when it's bounds interesect, it adds to the players score, then game ends when you reach the final checkpoint. Can't figure out how to make the score go up and print to the label each time. Thanks!
UPDATE: I can get my boxes to increase the score once, but not on all the other pictureboxes. Also, when I hit the final "spaceport" picturebox I get stuck in a messagebox redirect loop. How do I solve these 2 things? Tutors at school are no help.
public partial class consoleForm : Form
{
public consoleForm()
{
InitializeComponent();
}
private void consoleForm_Load(object sender, EventArgs e)
{
}
private void outputBox_TextChanged(object sender, EventArgs e)
{
}
private void timer1_Tick(object sender, EventArgs e)
{
int score = (0);
if (ship.Bounds.IntersectsWith(wormhole1.Bounds))
{
score += (1);
userScore.Text = score.ToString();
this.Refresh();
}
else if (ship.Bounds.IntersectsWith(wormhole2.Bounds))
{
score += (1);
userScore.Text = score.ToString();
this.Refresh();
}
else if (ship.Bounds.IntersectsWith(wormhole3.Bounds))
{
score += (1);
userScore.Text = score.ToString();
this.Refresh();
}
else if (ship.Bounds.IntersectsWith(wormhole4.Bounds))
{
score += (1);
userScore.Text = score.ToString();
this.Refresh();
}
if (ship.Bounds.IntersectsWith(spaceport.Bounds))
{
MessageBox.Show("you win");
this.Refresh();
}
}
The problem you've got is, that you're only doing this check once when the form loads, and never again.
Remove the logic from the consoleForm_Load event, and put it in your own method, called CheckScore(), or something else meaningful.
What would be good is to use a timer to check for the intersection every, let's say 100ms (0.1 seconds).
Create a timer:
In your constructor for the consoleForm, create a timer and a handler for it, then Start it. *You could even put it in your already-existing consoleForm_Load event - your choice :)
Like so:
public consoleForm()
{
var timer = new System.Timers.Timer(100); // Create a timer that fires every 100ms (0.1s)
timer.Tick += OnTimer_Tick;
timer.Start();
}
Add an event for the Tick event of the timer:
The OnTimer_Tick you can either "auto create" from VS, or add yourself:
private void OnTimer_Tick(object sender, ElapsedEventArgs e)
{
CheckScore(); // Call your logic method to check the score
}
Do the logic:
If you haven't already, make sure you've created the CheckScore() method using your original logic (that used to be in the consoleForm_Load event).
Final note:
I would seriously consider tidying up your CheckScore() (or whatever you like to call it) method, but that's just me :)
Further final note: there are loads of other ways of creating timers; I'm just being nice and using the most basic use of a Timer in WinForms :)
Hope this helps!
MORE!!!
At the moment, you're creating a new "score" every time you call the CheckScore() method.
In order to store your score, create a private field called _score or similar:
private int _score = 0;
Then, whenever you're adding to the user's score, reference the _score field, and use that to calculate and display:
_score++;
// or your own logic
_score += 20; // etc. etc.
// At the bottom of your logic,
// when you're ready to display the score:
userScore.Text = "Your score: " + _score;
Then, you can reference the _score wherever else you need it in your form.
Such as re-setting it:
private void ResetScore()
{
_score = 0;
}
or checking if the score has reached a certain value:
public void CheckScore()
{
...
// Your other logic to increment the score.
...
if (_score >= 10) // Check if the score has reached a certain value.
{
MessageBox.Show("You reached 10 points! YOU WIN!");
}
}
Bingo! ;)
Ok so your issue is that your only calling your code once - when the form is loaded. You need an update event set up and called regularly with the above code to make it work. Otherwise your code looks quite sound. Look into c# timers to get your update method called.

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

How to check if an incident happened during a time frame in C#?

I am developing software that adds if a button is clicked 5 times, a variable is incremented by '1'
IF A then B++
everything is good, but now I want the system to reset its counter if that 5 times did not happen within 10 seconds. I.e the speed of clicking matters.
If I click too slow, the increment should not happen even though I clicked 5 times as it exceeds that 10 secs period.
Any suggestion?
This could be done much nicer but it should work:
DateTime time = new DateTime();//time of first click
int counter = 0;
void button_click(object sender, EventArgs e)
{
if(counter == 0)
{time = DateTime.Now}
else if(counter == 5)
{
if( DateTime.Now.Subtract(time).Duration().Seconds <= 10)
{/*Do some cool stuff*/}
else
{counter = -1;}
}
counter++;
}
I'd do something like this:
const int ClicksRequired = 5;
readonly TimeSpan ClickTimeSpan = new TimeSpan(0, 0, 10);
Queue<DateTime> _clicks = new Queue<DateTime>();
private void clickTarget_MouseUp(object sender, MouseEventArgs e)
{
var currentTime = DateTime.Now;
_clicks.Enqueue(currentTime);
if (_clicks.Count == ClicksRequired)
{
var firstTime = _clicks.Dequeue();
if (currentTime - firstTime <= ClickTimeSpan)
{
MessageBox.Show("Hello World!");
_clicks.Clear();
}
}
}
I use Queue to keep track of clicks because you don't know which mouse click will actually be the first click until you have five clicks in the time window. You need to know the time of the fifth click back in time and that click changes with each subsequent click.
I use MouseUp instead of Click because Click might not fire the correct number of times if clicks occur within the system double-click interval (because those get routed to DoubleClick).

SoundEffect from XNA keeps looping even when the if condition explicitly forbids it to

private void updateDisplay(Object displayBlock)
{
TimeSpan ts = timeWatch.Elapsed;
if (stopRing == true)
{
stopRing = true;
Thread.Sleep(1000);
stopRing = false;
}
if (ts.Minutes == 0 && ts.Seconds == 10 && ts.Milliseconds <= 100 && stopRing == false)
{
stopRing = true;
btnSound_Click_1(null, null);
}
TextBlock db = (TextBlock)displayBlock;
db.Dispatcher.BeginInvoke(delegate() { db.Text = String.Format("{0:00}:{1:00}", ts.Minutes, ts.Seconds); });
}
EDIT: The reason for the if(stopRing == true) is because in the future, I'd like to add more conditions to the function below so that it will ring again after a certain period of time, for example:
if (ts.Minutes == 0 && ts.Seconds == 10 && ts.Milliseconds <= 100 && stopRing == false || ts.Minutes == 0 && ts.Seconds == 20 && ts.Milliseconds <= 100 && stopRing == false)
but to make things simpler during debugging, I only made the if condition so that it rings only once when ts.Seconds = 10. When I press the reset button, and ts.Seconds goes from 0 to 10 again, it should ring only once, but at this moment it rings multiple times, unpredictably.
and my code for the sound effect is:
private void btnSound_Click_1(object sender, RoutedEventArgs e)
{
Stream stream = TitleContainer.OpenStream("sounds/Ding.wav");
SoundEffect effect = SoundEffect.FromStream(stream);
FrameworkDispatcher.Update();
effect.Play();
}
the update display function is called by:
timer = new Timer(new TimerCallback(updateDisplay), textblockTimer, 0, 100);
What happens is that if I switch the TimerCallBack period to a lower value (such as 100 seen here), the SoundEffect will be played multiple times, very quickly. If I increase the TimerCallBack to a higher value such as 500, the SoundEffect will be played 2-3 times, sometimes quickly sometimes with about 200ms of delay in between.
I'm running out of ideas here... I don't know how can the sound be played when I specify the boolean value to be true, thereby blocking it from executing.
Any help will be much appreciated.
(stopRing is defined on top of the code)
EDIT: few more things to add: the first time round, the SoundEffect usually plays well (only a single time). But if I press the reset button and try again, most of the time the SoundEffect plays multiple times, some times it is okay.
Here is my code for the reset button:
private void btnReset_Click(object sender, RoutedEventArgs e)
{
timeWatch.Stop();
timeWatch.Reset();
paused = true;
btnStartStop.Content = "Start";
textblockTimer.Text = "00:00";
}
and my Start/Stop button:
private void btnStartStop_Click(object sender, RoutedEventArgs e)
{
if (paused == true)
{
timeWatch.Start();
timer = new Timer(new TimerCallback(updateDisplay), textblockTimer, 0, 100);
paused = false;
btnStartStop.Content = "Pause";
}
else
{
timeWatch.Stop();
paused = true;
btnStartStop.Content = "Start";
}
}
A few things just scream "logic error" in your code:
The 2nd if in updateDisplay checks the value of stopRing, but stopRing will always be false at that point (if it was true when the method was called, the previous block of code set it to false)
You set the updateDisplay function to be called every 100ms, but it contains a Thread.Sleep of 1000ms.
At this point I'm not sure what exactly is causing the behavior you're seeing, but this should give you some avenues to investigate.
I found out the problem, I need to call the timer.Dispose() method when I press the reset button to recalibrate the timer, so that previous instance of the timer doesn't interfere with the new one.
Solved!
So what does this mean?
This means that I don't have to use the stopRing boolean anymore (not that it helps), resulting in a much cleaner code.
Thank you all for helping!

Slide object using For Loop (C#)

I did quite a bit of searching around and didn't find anything of much help.
Is it possible to "slide" or "move" using C#, an object from one Location to another using a simple For loop?
Thank you
I would suggest you rather use a Timer. There are other options, but this will be the simplist if you want to avoid threading issues etc.
Using a straight for loop will require that you pump the message queue using Application.DoEvents() to ensure that windows has the opportunity to actually render the updated control otherwise the for loop would run to completion without updating the UI and the control will appear to jump from the source location to the target location.
Here is a QAD sample for animating a button in the Y direction when clicked. This code assumes you put a timer control on the form called animationTimer.
private void button1_Click(object sender, EventArgs e)
{
if (!animationTimer.Enabled)
{
animationTimer.Interval = 10;
animationTimer.Start();
}
}
private int _animateDirection = 1;
private void animationTimer_Tick(object sender, EventArgs e)
{
button1.Location = new Point(button1.Location.X, button1.Location.Y + _animateDirection);
if (button1.Location.Y == 0 || button1.Location.Y == 100)
{
animationTimer.Stop();
_animateDirection *= -1; // reverse the direction
}
}
Assuming that the object you're talking about is some kind of Control you could just change the Location property of it.
So something like this:
for(int i = 0; i < 100; i++)
{
ctrl.Location.X += i;
}
Should work I think.

Categories

Resources