In this application, I need to be able to stop the response from a key which is held down in order to prevent unnessecary data from entering the output. The problem I'm having is, using the methods in my code below does prevent the keys from repeating, but it also stops them from being responsive enough - as the users are hitting the keys very quickly.
I'm not sure if it's my hardware, api restriction or a problem with my code, but the routines I have below do not simply come round fast enough to work without making the program impossible to use. A way of identifying if a key is being actively held down (and for how long) would also help another feature for the program and solve this current issue.
Any ideas?
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
e.SuppressKeyPress = isKeyDown;
isKeyDown = true;
}
private void Form1_KeyUp(object sender, KeyEventArgs e)
{
isKeyDown = false;
}
private void Form1_KeyPress(object sender, KeyPressEventArgs e)
{
if (!isStreamPlaying) return;
if (e.KeyChar.Equals('d') || e.KeyChar.Equals('j'))
{
//red hit
SoundPlayer hitSounds = new SoundPlayer(taikoLiveMapper.Properties.Resources.normal_hitnormal);
hitSounds.Play();
outputlist.Add(string.Format("320,240,{0},1,{1}", ms, 0));
lastms = ms;
}
else if (e.KeyChar.Equals('s') || e.KeyChar.Equals('k'))
{
//blue hit
SoundPlayer hitSounds = new SoundPlayer(taikoLiveMapper.Properties.Resources.normal_hitclap);
hitSounds.Play();
outputlist.Add(string.Format("320,240,{0},1,{1}", ms, 8));
lastms = ms;
}
}
You can use GetKeyState to find out if a key is down and use that to track the keys:
[DllImport("user32.dll")]
static extern short GetKeyState(int key);
static bool IsKeyPressed(Keys key)
{
short state = GetKeyState((int)key);
return ((state & 128) != 0);
}
int i = 0;
Dictionary<Keys, DateTime> downSince = new Dictionary<Keys, DateTime>();
private void UpdateKeyStates()
{
foreach (var entry in downSince.ToArray())
{
if (!IsKeyPressed(entry.Key))
downSince.Remove(entry.Key);
}
}
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
UpdateKeyStates();
if (!downSince.ContainsKey(e.KeyCode))
{
downSince.Add(e.KeyCode, DateTime.UtcNow);
i++;
}
Text = i.ToString() + " " +(int)(DateTime.UtcNow - downSince[e.KeyCode]).TotalMilliseconds;
}
private void Form1_KeyUp(object sender, KeyEventArgs e)
{
UpdateKeyStates();
}
This example counts i up every time a key is pressed, and shows for how long it has been pressed. It uses GetKeyState instead of tracking KeyDown/KeyUp since you might miss those messages if something else has focus.
According to the documentation, "[d]uplicate KeyDown events occur each time the key repeats, if the key is held down, but only one KeyUp event is generated when the user releases the key."
So the simplest solution is to ignore a repeated KeyDown event unless its corresponding KeyUp event has been seen.
Just worked for me.
Use Timers instead: initialize timers, one for each "action" (e.g. pressing d/j or s/k) move the red hit/blue hit code inside the timer and instead of your current code, have this:
if (e.KeyChar.Equals('d') || e.KeyChar.Equals('j'))
{
//red hit
if (!tmrRedHit.Enabled)
tmrRedHit.Enabled = true;
}
else if (e.KeyChar.Equals('s') || e.KeyChar.Equals('k'))
{
//blue hit
if (!tmrBlueHit.Enabled)
tmrBlueHit.Enabled = true;
}
And in the timers Elpased event also set their Enabled to false after the code is executed.
Related
I have a camera that I move left and right with the arrows or 'A' and 'D' keys, unfortunately the camera response time is slower than the key held options in the application. I would like to move the camera smoothly while pressing the arrows but the program is stuck when I do it because it's too much pressing for the camera to process.
How can I hold the key and reduce the big number of commands that the camera gets and can't handle?
#Webnoob is wise ... i never knew they have a name.
You'll want to do something like :
private DateTime _lastValidKey;
private int delay;
private void SomePreviewKeyDown(object sender, KeyEventArgs e)
{
// follow line need tweaking to your liking and needs
if (now - delay > lastValidKey){
// update _lastValidKey and forward key
}
else {
// return, ignoring the keys
}
switch (e.Key)
{
case Key.Up:
// do something
break;
case Key.Down:
// do something
break;
}
}
basically ...you're filtering out extra keypresses
You could define a "time quantum" for user input processing. Let say we have some event (mouse, keyboard) that may cause lengthy processing in model and/or view. In simplest case we receive the keypress and process it immediately.
public void OnUserInput( )
{
DoSomething( );
}
private void DoSomething( )
{
// and here we do it
}
We may change this into:
public void OnUserInput( )
{
// DoSomething( );
StartTimedUpdate( );
}
private void DoSomething( )
{
// and here we do it
}
DispatcherTimer m_timer = null;
private void StartTimedUpdate()
{
if (m_timer == null)
{
m_timer = new System.Windows.Threading.DispatcherTimer();
m_timer.Tick += TimedRefresh;
m_timer.Interval = TimeSpan.FromMilliseconds(100);
m_timer.Start();
}
}
private void TimedRefresh(object sender, EventArgs e)
{
if (m_timer != null)
m_timer.Stop();
m_timer = null;
DoSomething();
}
You may also record all received keystrokes in list and process them in timer event handler. The 100 ms delay is just an example.
I've got two questions about some problems on this code:
1) How can I stop the sounds? Sounds are in a separate thread, and I don't know how to stop them.
2) If I continue pressing the key this code will play a lot of times the same sounds and this is not realistic (imagine a piano keyboard: if I press a key and I continue pressing it just one sounds (the firs) will play). How to solve this problem?
I found a solution but now with threads I don't know how to do.
private void Form1_KeyDown(object sender, KeyPressEventArgs e)
{
[...] // Other code
th = new Thread(press));
th.Start(new object[] { key, name });
}
private void Form1_KeyUp(object sender, KeyEventArgs e)
{
[...] // Other code
th = new Thread(leave);
th.Start(new object[] { key, name });
}
private void press(object data)
{
[...] // Other code
playSound(name);
}
private void leave(object data)
{
[...] // Other code
stopSound(name);
}
private void playSound(string name)
{
[...] // Other code
string url = Application.StartupPath + "\\notes\\" + name + ".wav";
var sound = new System.Windows.Media.MediaPlayer();
sound.Open(new Uri(url));
sound.play();
}
private void stopSound(string name)
{
???
}
Thank'you so much!
Create the thread as a variable in the class so that it can be accessed whenever you want.
Use the Handled Property to prevent the event from being handled again
e.Handled = true;
So, I'm trying to develop a simple application in visual C# which gets data from serial port and displays it in a textbox (to monitor temperature). I'm acquiring and displaying the data successfully, using the DataReceived event to update a global string variable and a timer to update the text field on my text box, as shown:
private void port_DataReceived_1(object sender, SerialDataReceivedEventArgs e)
{
try
{
globalVar.updateTemp = port.ReadLine(); //This is my global string
}
catch (IOException)
{
}
catch (InvalidOperationException)
{
}
catch (TimeoutException)
{
}
}
private void timer1_Tick(object sender, EventArgs e)
{
tempDisplayBox.Text = globalVar.updateTemp; //This is my textbox updating
}
The only issue I have is that the value shown in the textbox keeps flashing, making it hard to read. My timer is set to trigger every 10 ms (which should be fast enough, right?). Is there any way to make it more stable? I realize this may be a newb question, but to be fair I am a newb :) Any help is appreciated! Thanks!
Do you really need it updating every 10ms? What about every 500 ms or if not that then 100ms. 100ms will require your update method run 10 times less and therefore update 10 times less. The flickering you are expiriencing is due to the refresh speed. You could create custom method which will only update the temp only when target Label or textBox value is different than source port. But that will only sort the flickering when temp is steady, when temp will start vary it will bring back the flickering. Good luck ;-)
UPDATE
Hi I tried to reproduce the conditions and could not make my textbox nor Label flash. The way I tested it was by assigning int ntick = 0; and then increment the ++ntick; inside of the timer_tick method. The results didn't make any of the controls flash and were updated even every milisecond at some point. I also tried string.Format to put some load on the method. Is your app responsive?
The trick is to use double buffering. This way the operating system will redraw the Control off-screen, and only show the control when it is fully redrawn.
I have had the same problem, and solved it by extending the TextBox control like this:
public FastLogBox()
{
InitializeComponent();
_logBoxText = new StringBuilder(150000);
timer1.Interval = 20;
timer1.Tick += timer1_Tick;
timer1.Start();
SetStyle(ControlStyles.DoubleBuffer, true);
}
void timer1_Tick(object sender, EventArgs e)
{
if (_timeToClear)
{
_logBoxText.Clear();
_timeToClear = false;
}
if (_logQueue.Count <= 0) return;
while (!_logQueue.IsEmpty)
{
string element;
if (!_logQueue.TryDequeue(out element)) continue;
{
_logBoxText.Insert(0, element + "\r\n");
}
}
if (_logBoxText.Length > 150000)
{
_logBoxText.Remove(150000, _logBoxText.Length - 150001);
}
Text = _logBoxText.ToString();
}
public new void Clear()
{
_timeToClear = true;
while (!_logQueue.IsEmpty)
{
string element;
_logQueue.TryDequeue(out element);
}
}
public void AddToQueue(string message)
{
_logQueue.Enqueue(message);
}
}
I also use a timer and a concurrentQueue to avoid using Invoke to update the control from another thread. I also use a StringBuilder to prepare the string before putting it into the TextBox. StringBuilder is faster when building larger strings.
You can use ReadExisting() to read the whole data at a time.
You need to handle DataReceived Event of SerialPort
serialPort1.ReadExisting();
Sample:
private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
String myData=serialPort1.ReadExisting();
}
Example Code: Here i would like to show you the code to Read Data(RFID Tag Code which is basically of length 12)
String macid = "";
private void DoWork()
{
Invoke(
new SetTextDeleg(machineExe ),
new object[] { macid });
macid = "";
}
private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
string str1;
macid += serialPort1.ReadExisting();
if (macid.Length == 12)
{
macid = macid.Substring(0, 10);
Thread t = new Thread(new ThreadStart(DoWork));
t.Start();
}
}
public void machineExe(string text)
{
TextBox1.Text=text;
}
Thank you so much for the answers! I found a way to work around this issue:
Instead of replacing the contents of my textbox by rewriting the TextBox.Text property - which, as HenningNT implied, refreshes the control and causes the flickering - I'm now using the TextBox.AppendText method. Though, as I want to display only one line of data at a time, I use the textbox in multiline mode and the Environment.NewLine to jump to a new line before appending the text. As for the method of updating, I've gone back to using the timer because with the invoke method was crashing my application when I close the form, for some reason. Also, enabling double buffering didn't do me much good, although I guess I was doing it wrong... It still flickers a bit, but it's much better now :) I know this is not really a perfect solution (much more of a workaround), so I'll keep looking for it. If I find it, I'll be sure to update it here ;) My code:
private void timer1_Tick(object sender, EventArgs e) //Timer to update textbox
{
if (tempDisplayBox.Text != globalVar.updateTemp) //Only update if temperature is different
{
try
{
tempDisplayBox.AppendText(Environment.NewLine);
tempDisplayBox.AppendText(globalVar.updateTemp);
}
catch (NullReferenceException)
{
}
}
}
How to detect the ctrl key is pressed twice if the program is minimized or in system tray
I am trying to develop a c# program where the main form will be shown to the user when the control key is pressed twice. I found samples for hotkey combinations but this is not hotkey with combination, like control+ some other key. This is like google desktop app where the search box is displayed when control key is pressed twice.
Keyboard hooking as suggested. It's been nicely wrapped for you at CodePlex, where you get a .NET API simply raising Key and Mouse events, regardless of the state your app is in.
This seems like a case of keyboard hooking (WH_KEYBOARD).
What you could do is capture each time the key is pressed, and perhaps in a background worker compare the difference in time.
Set yourself a threshold and if it is less than that, you would consider it a double press and do what you need to.
Untested the components could look something like:
private readonly DateTime _originDateTime = new DateTime(0);
private DateTime _lastKeyPress;
Hook up worker:
_backgroundWorker = new BackgroundWorker { WorkerSupportsCancellation = false };
_backgroundWorker.DoWork += DoBackgroundWork;
_backgroundWorker.RunWorkerAsync();
Implement DoBackgroundWork method:
private void DoBackgroundWork(object sender, DoWorkEventArgs doWorkEventArgs)
{
do
{
if (_lastKeyPress != _originDateTime)
{
Thread.Sleep(DelayInMilliseconds);
DateTime now = DateTime.Now;
TimeSpan delta = now - _lastKeyPress;
if (delta < new TimeSpan(0, 0, 0, 0, DelayInMilliseconds))
{
continue;
}
}
//do stuff
} while (true);
}
And don't forget to capture the key:
private void SomeEvent_KeyDown(object sender, KeyEventArgs e)
{
_lastKeyPress = DateTime.Now;
}
This is based on XPath Visualizer
Use keyboard hooks like foxx1337 suggested, then do something like this:
int triggerThreshold = 500; //This would be equivalent to .5 seconds
int lastCtrlTick = 0;
private void OnCtrlPress()
{
int thisCtrlTick = Environment.TickCount;
int elapsed = thisCtrlTick - lastCtrlTick;
if (elapsed <= triggerThreshold)
{
LaunchYourAppOrWhatever();
}
lastCtrlTick = thisCtrlTick;
}
UPDATE:
The managed wrapper .NET library library mentioned in an accepted answer has moved here. Now there is also a nuget package MouseKeyHook available.
Recently support for detecting shortcuts, key combinations and sequences was added. Here is a usage example:
void DoSomething()
{
Console.WriteLine("You pressed UNDO");
}
Hook.GlobalEvents().OnCombination(new Dictionary<Combination, Action>
{
{Combination.FromString("Control+Z"), DoSomething},
{Combination.FromString("Shift+Alt+Enter"), () => { Console.WriteLine("You Pressed FULL SCREEN"); }}
});
For more information see: Detecting Key Combinations and Seuqnces
You may find if the CapsLock key has been pressed subscribing to the KeyDown/KeyUp event. And then toggle the state of the CapsLock based on that input. The problem with this approach is that you need the initial state of the CapsLock key to start toggling that.
One application of this could be giving the user a notification on a Login Page (this is what i need).
By the way i'm using Silverlight 5.
EDIT
The solution posted here says:
You can however find out if Capslock is on by making use of
KeyEventArgs.PlatformKeyCode that's actually send at onKeyDown.You can
look up the Virtual Key-code for capslock in here:
http://msdn.microsoft.com/en-us/library/ms927178.aspx
With this solution you can't determine the CapsLock state, because KeyEventArgs.PlatformKeyCode returns "an integer value that represents the key that is pressed or released (depending on which event is raised)". So if CapsLock is On and Key A is pressed then KeyEventArgs.PlatformKeyCode = 65, and on the other hand if CapsLock is off and Key A is pressed then KeyEventArgs.PlatformKeyCode = 65.
In other words you can't determine if the CapsLock is enabled or not based on the KeyEventArgs.PlatformKeyCode property.
The answer to this question also seems to have a solution, it checks two things:
the letter typed is Upper Case and Shift isn't pressed
the letter typed is Lower Case and Sift is pressed
Both of this cases implies that the CapsLock is On, but there is also a problem with this solution, given a KeyEventArgs you can know the pressed key in the keyboard but can't know the Char outputted by that key.
I'd suggest using a Behavior for this detection since you can hook into the PasswordChanged and KeyDown events to determine if the Caps Lock is on. Here is a quick behavior I wrote to detect if the Caps Lock is on. You can bind to the CapsLockOn behavior and use something like a data state behavior to hide/show your warning message.
public class DetectCapsLockBehavior : Behavior<PasswordBox>
{
private int _lastKey;
private ModifierKeys _modifiers;
[Category("Settings")]
public bool CapsLockOn
{
get { return (bool)GetValue(CapsLockOnProperty); }
set { SetValue(CapsLockOnProperty, value); }
}
public static readonly DependencyProperty CapsLockOnProperty = DependencyProperty.Register("CapsLockOn", typeof(bool), typeof(DetectCapsLockBehavior), new PropertyMetadata(null));
protected override void OnAttached()
{
AssociatedObject.PasswordChanged += new RoutedEventHandler(AssociatedObject_PasswordChanged);
AssociatedObject.KeyDown += new KeyEventHandler(AssociatedObject_KeyDown);
}
void AssociatedObject_KeyDown(object sender, KeyEventArgs e)
{
_lastKey = e.PlatformKeyCode;
_modifiers = Keyboard.Modifiers;
}
void AssociatedObject_PasswordChanged(object sender, RoutedEventArgs e)
{
if (_lastKey >= 0x41 && _lastKey <= 0x5a)
{
var lastChar = AssociatedObject.Password.Last();
if (_modifiers != ModifierKeys.Shift)
{
CapsLockOn = char.ToLower(lastChar) != lastChar;
}
else
{
CapsLockOn = char.ToUpper(lastChar) != lastChar;
}
}
}
}
NOTE: This is sample code, so there could be bugs. Just trying to demonstrate how it could be done.
region KeysDetection
bool bCaps = false;
bool bIns = false;
bool bNum = false;
public void FloatableWindow_KeyDown(object sender, KeyEventArgs e)
{
switch (e.Key)
{
case Key.CapsLock:
bCaps = !bCaps;
lbl_caps.Opacity = (bCaps) ? 1 : 0.5;
break;
case Key.Insert:
bIns = !bIns;
lbl_ins.Opacity = (bIns) ? 1 : 0.5;
break;
case Key.Unknown:
{
if (e.PlatformKeyCode == 144)
{
{
bNum = !bNum;
lbl_num.Opacity = (bNum) ? 1 : 0.5;
}
}
break;
}
}
}
#endregion