I am programming something right now in c#, trying to "convert" a console application to a windows Forms application and I wanted to do something like the following:
if("keypress == nokey")
{
system.threading.thread.sleep ***
}
while(nokeyispressed)
{
system.threading...
}
Basically ask if no key is pressed sleep for some time and this.close();
so that if no key is pressed, do something...
I just can't get it to work.
I would be very greatful for some help..:D
If no key pressed, then no KeyDown event raised. So, your handler will not be called.
UPDATE (option with loop removed, because timer will make same for you as loop on different thread with sleep timeouts):
Here is sample with timer:
private bool _keyPressed;
private void TimerElapsed(object sender, EventArgs e)
{
if (!_keyPressed)
{
// do what you need
}
}
private void KeyDownHandler(object sender, KeyEventArgs e)
{
_keyPressed = true;
switch (e.KeyCode)
{
// process pressed key
}
_keyPressed = false;
}
UPDATE: I think good idea to verify how many time elapsed since last key down before decide if no keys were pressed
private DateTime _lastKeyDownTime;
private const int interval = 100;
private void LoadHandler(object sender, EventArgs e)
{
// start Threading.Timer or some other timer
System.Threading.Timer timer = new System.Threading.Timer(DoSomethingDefault, null, 0, interval);
}
private void DoSomethingDefault(object state)
{
if ((DateTime.Now - _lastKeyDownTime).TotalMilliseconds < interval)
return;
// modify UI via Invoke
}
private void KeyDown(object sender, KeyEventArgs e)
{
_lastKeyDownTime = DateTime.Now;
switch (e.KeyCode)
{
// directly modify UI
}
}
Related
How to use timers to trigger click button event every 3 seconds?
I'm trying to rotate 2 pictures in pictureboxes by triggering the rotate button automaticly using timer but it seems doesnt works. I never used timer before so this is my first time. Anyone know whats wrong with my code or any other code suggestion for it? Thanks
Code I'm using
private void timer1_Tick(object sender, EventArgs e)
{
rotateRightButton_Click(null, null);
pictureBox1.Refresh();
pictureBox2.Refresh();
}
private void timerStartButton_Click(object sender, EventArgs e)
{
timer1.Start();
}
private void timerStopButton_Click(object sender, EventArgs e)
{
timer1.Stop();
}
It's even possible (and more simple) with tasks
public partial class Form1 : Form
{
// variable to keep track if the timer is running.
private bool _timerRunning;
public Form1()
{
InitializeComponent();
}
private async Task StartTimer()
{
// set it to true
_timerRunning = true;
while (_timerRunning)
{
// call the rotateRightButton_Click (what you want)
rotateRightButton_Click(this, EventArgs.Empty);
pictureBox1.Refresh();
pictureBox2.Refresh();
// wait for 3 seconds (but don't block the GUI thread)
await Task.Delay(3000);
}
}
private void rotateRightButton_Click(Form1 form1, EventArgs empty)
{
// do your thing
}
private async void buttonStart_Click(object sender, EventArgs e)
{
// if it's already started, don't start it again.
if (_timerRunning)
return;
// start it.
await StartTimer();
}
private void buttonStop_Click(object sender, EventArgs e)
{
// stop it.
_timerRunning = false;
}
}
timer1.Interval = 3000; // set interval to 3 seconds and then call Time Elapsed event
timer1.Elapsed += Time_Elapsed;
//Event
private void Time_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
// will be triggered in every 3 seconds
rotateRightButton_Click(null, null);
pictureBox1.Refresh();
pictureBox2.Refresh();
}
Hope this helps!
When I hold a key in my game to move my player:
public void MainForm_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Up)
{
Player.MoveUp();
}
}
The player instantly moves one step as soon as I press the down arrow, and then pauses for a short duration before starting to smoothly move again. Why's this? How can I prevent it?
The answer in the proposed duplicate is incorrect, unfortunately. It doesn't ignore repeated KeyDown events, and so will gradually increase the "delta" value in the direction being handled by each key case. It also doesn't respond to the keypress immediately (i.e. no action happens until the first timer tick).
This answer to Holding Arrow Keys Down For Character Movement C# .Net ISSUES explains how to ignore the subsequent KeyDown events, but doesn't explain how then your character would move.
In other words, I couldn't find a duplicate question that actually correctly answers your question. So…
The basic technique you want to do is:
Don't move on the actual key input. Instead, generate your own timing logic that will move the object.
Instead of using the KeyDown event to actually move the object, use it to set a movement direction, which is then processed by your timing logic.
There are a variety of ways to accomplish this. One version would look like this:
private bool _moveUp;
private bool _moveDown;
private bool _moveLeft;
private bool _moveRight;
// You can add the Timer in the Winforms Designer instead if you like;
// The Interval property can be configured there at the same time, along
// with the Tick event handler, simplifying the non-Designer code here.
private System.Windows.Forms.Timer _movementTimer = new Timer { Interval = 100 };
public MainForm()
{
InitializeComponent();
_movementTimer.Tick += movementTimer_Tick;
}
private void movementTimer_Tick(object sender, EventArgs e)
{
_DoMovement();
}
private void _DoMovement()
{
if (_moveLeft) Player.MoveLeft();
if (_moveRight) Player.MoveRight();
if (_moveUp) Player.MoveUp();
if (_moveDown) Player.MoveDown();
}
// You could of course override the OnKeyDown() method instead,
// assuming the handler is in the Form subclass generating the
// the event.
public void MainForm_KeyDown(object sender, KeyEventArgs e)
{
if (e.IsRepeat)
{
// Ignore key repeats...let the timer handle that
return;
}
switch (e.KeyCode)
{
case Keys.Up:
_moveUp = true;
break;
case Keys.Down:
_moveDown = true;
break;
case Keys.Left:
_moveLeft = true;
break;
case Keys.Right:
_moveRight = true;
break;
}
_DoMovement();
_movementTimer.Start();
}
public void MainForm_KeyUp(object sender, KeyEventArgs e)
{
switch (e.KeyCode)
{
case Keys.Up:
_moveUp = false;
break;
case Keys.Down:
_moveDown = false;
break;
case Keys.Left:
_moveLeft = false;
break;
case Keys.Right:
_moveRight = false;
break;
}
if (!(_moveUp || _moveDown || _moveLeft || _moveRight))
{
_movementTimer.Stop();
}
}
Do note that the timer objects in .NET have limited resolution. I show an interval of 100 ms (10 times per second) above (same as in the other question's answer), and this is about as frequent an update as you're going to reliably get. Even then, the timer's Tick event may not (and probably won't) be raised on exactly 100 ms intervals. There will be some variation back and forth. But it will be close enough for a basic game.
If you need more precision than that, you will have to implement your own state-polling-and-animation loop somewhere. That's a whole other ball o' wax. :)
A subjectivly elegent approach:
public partial class Form1 : Form
{
private static Timer timer;
private static bool[] keys_down;
private static Keys[] key_props;
private void Form1_Load(object sender, EventArgs e)
{
keys_down = new bool[4];
key_props = new []{Keys.A, Keys.D, Keys.W, Keys.S};
timer = new Timer();
timer.Interval = 15; // Roughly 67 FPS
timer.Tick += tick;
timer.Start();
KeyDown += key_down_event;
KeyUp += key_up_event;
... // More things to do when the form loads.
}
private void tick(Object source, EventArgs e)
{
... // Do this every timing interval.
byte n = 0;
foreach (var v in keys_down)
{
if (n == 3 && v)
... // If the "s" key is being held down, no key delay issues. :)
n++;
}
...
}
private void key_down_event(object sender, KeyEventArgs e)
{
byte n = 0;
foreach (var v in keys_down)
{
if (e.KeyCode == key_props[n])
keys_down[n] = true;
n++;
}
}
private void key_up_event(object sender, KeyEventArgs e)
{
byte n = 0;
foreach (var v in keys_down)
{
if (e.KeyCode == key_props[n])
keys_down[n] = false;
n++;
}
}
public Form1()
{
InitializeComponent();
}
}
I was looking for solutions to create a small copy of Flappy Bird. Perhaps my decision will help someone.
I used the addition of a player position with a variable in timer to simulate gravity. When I press W, I turned off the further reception of the command using bool and simply inverted gravity
private void time_Tick(object sender, EventArgs e)
{
if (bird.Location.Y >= 540)
{
bird.Location = new Point(bird.Location.X, 540);
}
else
{
bird.Location = new Point(bird.Location.X, bird.Location.Y+grav);
}
}
private void Form1_KeyPress(object sender, KeyEventArgs e)
{
if (e.KeyData == Keys.W && press == false)
{
press = true;
grav = -10;
}
return;
}
private void Form1_KeyUp(object sender, KeyEventArgs e)
{
if (e.KeyData == Keys.W && press == true)
{
press = false;
grav = 5;
}
return;
}
Basically in the code below where i highlight, i would like to know how to use drawBricks method in the timer tick when that button(btnDisplayBricks) is pressed. Because i am using a timer tick and picturebox for paper drawings etc i cannot just simply call the method from within the button click event because the paper then clears in the timer only allowing me to display the bricks before timer1 starts any ideas.
private void timer1_Tick(object sender, EventArgs e)
{
paper.Clear(Color.LightSteelBlue);
DrawBall();
MoveBall();
DrawBat(paper);
if (btnDisplayBricks_Click[0] = true) ///code here problem
//then call method
DrawBricks(paper);
private void btnDisplayBricks_Click(object sender, EventArgs e)
{
DrawBricks(paper);
}
}
}
problem is in your equation, you should use == instead of =
if (btnDisplayBricks_Click[0] == true)
also move methodbtnDisplayBricks_Click outside of timer1_Tick
private bool buttonClicked = false;
private void timer1_Tick(object sender, EventArgs e)
{
paper.Clear(Color.LightSteelBlue);
DrawBall();
MoveBall();
DrawBat(paper);
if (buttonClicked)
{
DrawBricks(paper);
// maybe you want to set buttonclicked to false again, but specs are not clear to me
// buttonClicked = false;
}
}
private void btnDisplayBricks_Click(object sender, EventArgs e)
{
DrawBricks(paper);
buttonClicked = true;
}
By following code(the interval of timer2 is 1000)
private void timer1_Tick(object sender, EventArgs e) {
timer7.Enabled=false;
timer8.Enabled=false;
lblTimer_Value_InBuildings.Text="0";
}
private void timer2_Tick(object sender, EventArgs e) {
lblTimer_Value_InBuildings.Text=(int.Parse(lblTimer_Value_InBuildings.Text)+1).ToString();
}
we can not create a delay in a for-loop
for(int i=1; i<=Max_Step; i++) {
// my code...
// I want delay here:
timer1.Interval=60000;
timer1.Enabled=true;
timer2.Enabled=true;
// Thread.Sleep(60000); // makes no change even if uncommenting
}
Whether I uncomment the line Thread.Sleep(60000); or not, we see nothing changed with lblTimer_Value_InBuildings in timer2_Tick.
Would you please give me a solution(with or without timers)?
Your timer is your loop, you don't need a for loop. You just keep track of your loop variables outside of the function calls. I would recommend wrapping all of this functionality into a class, to keep it separate from your GUI code.
private int loopVar = 0;
public void Form_Load()
{
// Start 100ms after form load.
timer1.Interval = 100;
timer1.Enabled = true;
}
private void timer1_Tick(object sender, EventArgs e)
{
timer1.Enabled = false;
// My Code Here
loopVar++;
if (loopVar < Max_Step)
{
// Come back to the _tick after 60 seconds.
timer1.Interval = 60000;
timer1.Enabled = true;
}
}
When you do Thread.Sleep(60000), you are telling the UI thread to sleep for 60 seconds. What this also does is prevent the execution of the timer, because the UI thread is hung up sleeping instead of processing events such as the timer sleep.
The code totally made me feel boring
methods:
private void timer1_Tick(object sender, EventArgs e) {
timer2.Enabled=false;
timer1.Enabled=false;
lblTimer_Value_InBuildings.Text="0";
}
private void timer2_Tick(object sender, EventArgs e) {
lblTimer_Value_InBuildings.Text=(int.Parse(lblTimer_Value_InBuildings.Text)+1).ToString();
}
private void timer3_Tick(object sender, EventArgs e) {
if(0!=(int)timer3.Tag) {
// your code goes here and peformed per step
timer1.Enabled=true;
timer2.Enabled=true;
}
timer3.Tag=(1+(int)timer3.Tag)%Max_Step;
}
initials:
var delayedInterval=60000;
timer1.Interval=60000;
timer2.Interval=1000;
timer3.Interval=delayedInterval+timer1.Interval;
lblTimer_Value_InBuildings.Text="0";
timer3.Tag=1;
timer3.Enabled=true;
Your original code never makes timer1 and timer2 stopped, so I think you should correct timer7 and timer8 to timer1 and timer2, otherwise they don't make sense here.
I have a problem that I'm struggling with.. I want to move an image using my keyboard to the left, right, up or down and in a diagonal way. I searched the web and found, that to use 2 diffrent keys I need to remember the previous key, so for that I'm using a bool dictionary.
in my main Form class this is how the KeyDown event looks like:
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
baseCar.carAccelerate(e.KeyCode.ToString().ToLower());
carBox.Refresh(); //carbox is a picturebox in my form that store the image I want to move.
}
My KeyUp event:
private void Form1_KeyUp(object sender, KeyEventArgs e)
{
baseCar.carBreak(e.KeyCode.ToString().ToLower());
}
My Paint event:
private void carBox_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawImage(Car, baseCar.CharPosX, baseCar.CharPosY); // Car is just an image
}
And my baseCar class:
private Dictionary KeysD = new Dictionary(); // there is a method to set the W|A|S|D Keys, like: KeysD.Add("w",false)
public void carAccelerate(string moveDir)
{
KeysD[moveDir] = true;
moveBeta();
}
public void moveBeta()
{
if (KeysD["w"])
{
this.CharPosY -= this.carMoveYSpeed;
}
if (KeysD["s"])
{
CharPosY += carMoveYSpeed;
}
if (KeysD["a"])
{
CharPosX -= carMoveXSpeed;
}
if (KeysD["d"])
{
CharPosX += carMoveXSpeed;
}
}
public void carBreak(string str)
{
KeysD[str] = false;
}
Anyway it works, but my problem is that I can't get back to the first pressed key for example:
I pressed W to move up and then the D key to go diagonal, how ever when I release the D key it wont go Up again because the KeyDown event is "dead" and wont call the carAccelerate() method again.. and I can't figure out how to fix it..
Can any one help me please? Maybe there is a better way to handle the keys? im open to any ideas! And I hope you can understand it, my english isnt the best :S
Typically you don't hande the key events directly for these kinds of things. Instead, you keep track of what keys are currently pressed. Physics calculations are done on some interval, which can be done with a timer. Quick and dirty example below. However, this is not the kind of thing you should be attempting with WinForms.
private const int ACCELERATION = 1;
private HashSet<Keys> pressed;
private int velocityX = 0;
private int velocityY = 0;
public Form1()
{
InitializeComponent();
pressed = new HashSet<Keys>();
}
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
pressed.Add(e.KeyCode);
}
private void Form1_KeyUp(object sender, KeyEventArgs e)
{
pressed.Remove(e.KeyCode);
}
private void timer1_Tick(object sender, EventArgs e)
{
car.Location = new Point(
car.Left + velocityX,
car.Top + velocityY);
if (pressed.Contains(Keys.W)) velocityY -= ACCELERATION;
if (pressed.Contains(Keys.A)) velocityX -= ACCELERATION;
if (pressed.Contains(Keys.S)) velocityY += ACCELERATION;
if (pressed.Contains(Keys.D)) velocityX += ACCELERATION;
}