How to delay program for a certain number of milliseconds, or until a key is pressed?
I need to delay my program's execution for a specified number of milliseconds, but also want the user to be able to escape the wait when a key is pressed. If no key is pressed the program should wait for the specified number of milliseconds.
I have been using Thread.Sleep
to halt the pro开发者_运维百科gram (which in the context of my program I think is ok as the UI is set to minimise during the execution of the main method).
I have thought about doing something like this:
while(GetAsyncKeyState(System.Windows.Forms.Keys.Escape) == 0 || waitTime > totalWait)
{
Thread.Sleep(100);
waitTime += 100;
}
As Thread.Sleep
will wait until at least the time specified before waking the thread up, there will obviously be a large unwanted extra delay as it is scaled up in the while loop.
Is there some sort of method that will sleep for a specified amount of time but only while a condition holds true? Or is the above example above the "correct" way to do it but to use a more accurate Sleep method? If so what method can I use?
Thanks in advance for your help.
Edit ---- Possible Idea...
DateTime timeAtStart = DateTime.Now;
int maxWaitTime = 15000;
while (true)
{
if (GetAsyncKeyState(System.Windows.Forms.Keys.Escape) != 0)
{
break;
}
if ((DateTime.Now - timeAtStart).TotalMilliseconds >= maxWaitTime)
{
break;
}
}
This doesn't use any sort of timer but looks like it could work, any suggestions?
Edit 2: The above works for me and now allows me to break the wait when escape is pressed. I have noticed the delay is more accurate than using Thread.Sleep
too!
Consider reversing the concepts... instead of delaying it for a certain time, think about starting execution in a certain time, or when a key is pressed.
Start a Windows Forms timer with a tick handler which will kick off whatever you want to happen, and also a key event handler which will start it and stop the timer.
First sample is using Timer, ManuelResetEvent and Global Keyboard hook:
I did not include keyboard hook code because it's too large. You can find it here.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Threading;
namespace WindowsFormsApplication1
{
static class Program
{
private static System.Threading.Timer _timer;
private static ManualResetEvent _signal;
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
_signal = new ManualResetEvent(false);
_timer = new System.Threading.Timer(Timer_Signaled, null, 15000, 0);
_signal.WaitOne();
_signal.Reset();
Application.Run(new Form1());
}
private static void Timer_Signaled(object state)
{
_signal.Set();
}
}
}
When you hook to keyboard and ESC is pressed, simply call: _signal.Set(). This first sample is just to give you an idea.
Second sample:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace WindowsFormsApplication1
{
static class Program
{
[DllImport("user32.dll")]
static extern short GetAsyncKeyState(System.Windows.Forms.Keys vKey);
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
int maxWaitTime = 15000;
int tc = System.Environment.TickCount;
while (true)
{
System.Threading.Thread.Sleep(1000);
if (System.Environment.TickCount - tc > maxWaitTime)
{
break;
}
if (GetAsyncKeyState(Keys.Escape) > 0)
{
break;
}
}
Application.Run(new Form1());
}
}
}
EDITED:
First sample is more reliable as keyboard hook use callback to inform which key was pressed. Second sample works like 'Pull' and it can happen not every key press will be collected.
Looking at your code, I'm assuming you're using Windows Forms for the UI.
There are many ways to solve your issue, but given the framework the easiest that comes to my mind is:
- Blocking your UI (i.e. :
this.Enabled = false
) - Setting up a timer (i.e.:
timer.Tick += ContinueUITimer();
) - Setting up a keyboard handler (i.e.:
this.KeyDown += ContinueUIKeyboard
)
With the ContinueUI functions like this:
void ContinueUIXxx(...)
{
timer.Tick -= ContinueUITimer;
this.KeyDown -= ContinueUIKeyboard;
this.Enabled = true;
... whatever else in the continuation;
}
精彩评论