How do I capture keystrokes in a Silverlight application? - silverlight

I am trying to capture any keystroke that happens inside my Silverlight 2 application. There are not any input fields, I'm writing a game, and need to know which of the arrow keys are being pressed. My best guesses at how to capture these through an event handler are unsuccessful. Any recommendations?

Good to see you again (virtually).
I'm assuming you already tried wiring KeyDown on the root UserControl element. Here are some tips:
The plugin needs focused before it sees key events. You'll have to force the user into clicking on the plugin to start.
Make sure you don't have another element (like the ScrollViewer) that is eating arrow keys events. If you have a ScrollViewer in play you'll only be seeing KeyUp.
No switching to full screen mode.
Must be something simple you are missing like that. Hope that helps.

Handle the KeyDown and/or the KeyUp event on your root Grid.

Silverlight 2 supports the KeyUp and KeyDown events (only, I believe - not KeyPress).
In these events, you get a KeyEventArgs object as a parameter. You can get the KeyCode or KeyData from this.

In reviewing a few different threads from different forums regarding this matter it would seem it is best to handle your keyevents on your Page (root) element, parse the key for the desired effect, and redirect your command to the particular control.
Page
this.KeyDown += (s, e) =>
{
MyControl control = (MyControl)Layoutroot.FindName("controlname");
if(control != null)
{
control.MyPublicFunction(e.Key);
}
};
MyControl
public MyPublicFunciton(Key pressedKey)
{
if(pressedKey == Key.Enter)
{
//Do something
}
}

Related

Detecting key combinations

I want to detect when a combination like Ctrl-C is pressed in a WPF application. What I've read online says to use something like the following in the KeyDown (or KeyUp) Event:
if ((Keyboard.Modifiers == ModifierKeys.Control) && (e.Key == Key.S))
{
MessageBox.Show("Save!");
}
I'm just trying to understand how this works. As I understand it, e.Key contains the key that was pressed that triggered the event and Keyboard.Modifiers contains the information about the state of the Control key right now. Is it safe to assume that the Control key will still be down by the time the KeyDown event gets handled?
For example, I restart Firefox and it grinds away loading a bunch of tabs, and in the meantime I hit Ctrl-S in my application. There is a delay in getting to KeyDown, and the application thinks just S has been pressed.
Thanks
You could use KeyBindings instead, they define full gestures without such a separation.

Strategy for differentiating TouchUp from TouchLeave, and TouchDown from TouchEnter?

For the basic scenario described in the msdn overview (under Touch and Manipulation) TouchEnter and TouchLeave are fired for every corresponding TouchDown and TouchUp respectively. Unlike the mouse, the Touch and Stylus are not constrained to maintain contact with the screen.
Is there a way to use TouchEnter and TouchLeave is to capture only when a finger is dragged into the UIElement. As these events are fired for every touchUp and touchDown, what is the best way to differentiate these events?
One strategy that would work for the single finger case, is to have a flag set on TouchDown, and check if the flag is set on TouchUp. This allows some condition checks on TouchUp. However, for multiple fingers, it isn't feasible.
There are no PreviewTouchEnter and PreviewTouchLeave events fired, only PreviewTouchDown and PreviewTouchUp. The sequence of events for a finger lowered on to a UIElement and then raised over it is as follows:
TouchEnter
PreviewTouchDown
TouchDown
PreviewTouchUp
TouchUp
TouchLeave
This sequence doesn't help differentiate a TouchEnter that has happened due to a finger dragged across the screen into the UIElement, from a finger that is lowered onto the UIElement directly. Am I missing something, or does the framework not support such differentiation itself?
Can you use the TouchDevice Class to keep track of where touches are generated. New touches are given a new ID, so you could distinguish between existing touches and new ones, and which elements are capturing the device. I guess that circumvents the Manipulation events and the normal processes, but I hope that helps.
If you retrieve a TouchPoint for the event, there is a property on it named Action which tells you whether it is a Down, a Move or a Up event.
void m_element_TouchEnter(object sender, System.Windows.Input.TouchEventArgs e)
{
var touchPoint = e.GetTouchPoint(m_someElement);
if (touchPoint.Action == System.Windows.Input.TouchAction.Move)
{
//This is a "true" TouchEnter event
}
else if (touchPoint.Action == System.Windows.Input.TouchAction.Down)
{
//This is a "true" TouchDown event.
}
}

Events routing in WPF

I have implemented a UserControl. Then I would like to handle an event that is originally handled by Window (keyboard press). What is the best way to route the event caught by another component (higher in the components' tree)?
Thanks in advance for the replies and hints!
Cheers
It depends on the event you're trying to access. If it's a Preview event and the Window is setting e.Handled to true you'll need to use the method Alex suggests to circumvent the Window's handling of the tunneling. If it is a bubbling event (i.e. KeyDown) you don't need to do anything special since bubbling events hit the handlers on child elements first and go up the visual tree so the Window handler won't occur until after your UC's.
One thing you need to be careful with using Key events is that the event is only going to get picked up by your UC in the first place if the Focus is on or inside of it. This isn't something you need to worry about with things like Mouse events since they start at a specific location in the tree.
I believe you cannot gurantee that.
Window class is wrapping Win32 message-based event model and this will be the only WPF entity which will have access to those information.
I suggest that you create an attached property (which will be used by the Window) and implement the routing of the events yourself so that controls could subscribe to.
You can attach the routed handler specifying that you want to handle handled messages as well:
this.AddHandler(routedEvent, handler, true);
where this is an UIElement or derived class.
However there may still be events (key presses in this case) which don't make it past the window, not sure.

What's the best way to prevent losing TextBox focus when there is a validation error?

I've messed around with PreviewLostKeyboardFocus which almost gets you there. I've seen a couple of implementations using LostFocus, but that just forces focus back on the TextBox after it's lost focus and you can easily see this shifting on the screen. Basically, I'm just looking for the same type of behavior you could get with using OnValidating in WinForms.
In my opinion, the best way is generally not to do it. It is almost always better to just disable the other controls or prevent saving until the value is valid.
But if your design really needs this ability, here is what you should do:
Intercept the Preview version of keyboard and mouse events at your window level, or whatever scope you want to prevent focus changes within (eg maybe not your menu bar).
When the Tab KeyDown or Return KeyDown is detected in the text box, or when a MouseDown is detected outside the text box while it has the focus, call UpdateSource() on the binding expression, then if the validation has failed set Handled=true to prevent the KeyDown or MouseDown event from being processed further.
Also continue handling PreviewLostKeyboardFocus to catch any causes of focus change that aren't from the keyboard or mouse, or that your other code didn't recognize.
To add onto Ray's answer:
UpdateSource is called like so:
BindingExpression be = userTextbox.GetBindingExpression(TextBox.TextProperty);
be.UpdateSource();
Also, as an alternative you can set the text box binding to:
UpdateSourceTrigger = "PropertyChanged";
The latter will cause a continuous check, whereas the former will check when needed (performant).
If you attempt to focus an element inside its own LostFocus handler you will face a StackOverflowException, I'm not sure about the root cause (I suspect the focus kind of bounces around) but there is an easy workaround: dispatch it.
private void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
var element = (sender as TextBox);
if (!theTextBoxWasValidated())
{
// doing this would cause a StackOverflowException
// element.Focus();
var restoreFocus = (System.Threading.ThreadStart)delegate { element.Focus(); };
Dispatcher.BeginInvoke(restoreFocus);
}
}
Through Dispatcher.BeginInvoke you make sure that restoring the focus doesn't get in the way of the in-progress loss of focus (and avoid the nasty exception you'd face otherwise)

silverlight keydown event doesn't fire for arrow keys

I have a canvas inside a scrollview. I attached a keydown event handler to the scrollview. For most keys, the handler gets called.
However, for the arrow keys, the handler does not get called. Instead, the scrollview gets scrolled in the appropriate direction.
I also attached a keyup handler to the scrollview and the keyup does get called for the arrow keys.
Is there any way to get the arrow key down event here?
Just use the KeyUp event. Works like a charm.
I found this silly hack to make it work. Setting the scrollview to not be a tabstop keeps it from eating the key events.. but then I had another textbox on the page that all of a sudden ALWAYS had focus because the scrollview didn't anymore. So I fixed that by letting an invisible textbox get focus.
scrollView.IsTabStop = false;
invisibleTextBox.Foreground = new SolidColorBrush(Colors.Transparent);
invisibleTextBox.Background = new SolidColorBrush(Colors.Transparent);
Canvas.SetZIndex(invisibleTextBox, -1000);
invisibleTextBox.KeyDown += new KeyEventHandler(HandleKeyDown);
invisibleTextBox.KeyUp += new KeyEventHandler(HandleKeyUp);
Edit: I also had to move the text box off the canvas because despite being invisible, its outline still showed up.
2nd Edit: I used a textbox because that was the first thing I found that could capture KeyDown events. However, a UserControl can. So it would probably be better practice to use a UserControl instead of an invisible text box. You can call Focus() on the UserControl if needed.
This is a possible answer - I haven't had a chance to test this. I've had similar trouble in the past though, when a control is consuming the events before you can get at them. There's a few things you may be able to try:
Use the PreviewKeyDown event, I think that's what it's called. It may let you get at the event before it's consumed by the control.
Try mblandfo's suggestion, although if you do this you probably ant to wrap the whole thing up in a user control to hide what you're doing from the rest of your code.
Add a key handler to the Canvas object, you may be able to catch the event there, and "bubble" it up through your own event.
Except for 1) all of these count as hacks, really, but good luck, I hope one of them works for you!
"Use the PreviewKeyDown event, I think that's what it's called. It may let you get at the event before it's consumed by the control."
This works. Just set the event arguments "Handled = true" and the ScrollViewer (or ListBox) wont grab onto the event after you've already handled it. I didn't need to use the IsTabStop property for the ListBox though; that and it didn't seem to do anything anyways.

Resources