I need to respond to keydown events (O, C, G keys etc., not modifier keys) while a Drag+Drop operation is in progress over my control (i.e. between DragEnter and DragLeave). However the KeyDown event is not called at this stage.
I've tried selecting my control and specifically setting focus on DragEnter, but that doesn't work.
EDIT:
Hans' answer is basically correct, except I had to use GetAsynchKeyState to get the behaviour I wanted.
The QueryContinueDrag event is raised on the drag source. Checking for the state of the keys you are interested in is going to require pinvoke, the event is only designed to help recognize the Escape key and modifier key state changes. Which is something to keep in mind, that these keys have any special action is very undiscoverable.
[DllImport("user32.dll")]
private static extern short GetKeyState(Keys key);
It returns a value < 0 when the key is down. I can't say it's guaranteed to work correctly but it looked good when I tried it.
You can also try:
Keyboard.IsKeyDown(); method to check if a specific key is pressed, i.e.:
bool isKeyPressed = Keyboard.IsKeyDown(Key.LeftAlt);
It's similar to the previous answer, but it's a native .NET method, so it doesn't require you to import any functions.
A similar question has been asked here: Handle KeyDown during a drag drop. Or keydown event not workign, but there was a suggestion to make it work like an event.
UPDATE
The first solution seems to work only in WPF. If you want to check states of modifier keys, there is, however, a method utilizing a property Form.ModifierKeys that should work correctly in WinForms. The example shows how to check if alt (left alt) and ctrl keys are both pressed:
if (Form.ModifierKeys == (Keys.Alt | Keys.Control))
{
//TODO: insert your code here
}
Related
I making a multi select list. On item click it toggles a certain class on that item. I would like to implement multiple selection while CTRL key is down.
Is this possible?
The ctrlkey property of your event will evaluate to true if the control key is down when it's called. So basically something like:
if (e.ctrlkey) {
your code
}
Update: In answer to your question in the comments, yes. The event object is pretty robust and exposes a number of properties that you can use to determine the type of event you're dealing with, how it was triggered and so on. This is a pretty decent and concise writeup on the event object: http://www.htmlgoodies.com/beyond/javascript/events-and-javascript-part-3-the-event-object.html.
I've seen a number of answers suggesting the use of Keyboard.Modifiers to determine whether a KeyDown event is for a key that had a modifier set. Unfortunately, because Keyboard.Modifiers returns the current state of the modifiers (instead of the state of the modifier when the key was pressed), this results in a really annoying intermittent bug for quick typists.
Specifically, imagine someone presses Ctrl+A, and releases Ctrl only a few milliseconds after pressing A. Now imagine that the system was under heavy load; the key handler started executing but was preempted for 50ms. By the time the key handler is executing again, the current state of Ctrl is "released". The key handler will now think that "A" was pressed without Ctrl, and this is bad.
Similarly, if a fast typist enters A, Ctrl+End and my application uses Keyboard.Modifiers, it could instead end up observing Ctrl+A...
In WinForms, the KeyDown event tells me the state of Ctrl exactly, even if it's already released by the time the event is being handled. How can I get this same behaviour in WPF?
Edit: it's possible that Keyboard.Modifiers doesn't actually retrieve the "current" modifier keys, but instead the modifier keys as related to the key down message currently being processed. In WinAPI, this was the distinction between "async" and non-async key state functions. Unfortunately, the documentation doesn't mention what exactly "current" means. If anyone knows, please say so.
As there does not appear to be any modifier information in the event args you could keep track of the state yourself in some fields and handling both KeyUp and KeyDown to update them accordingly.
e.g.
private bool ctrl = false;
private void This_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.LeftCtrl) //or switch, also: `LeftCtrl` & `RightCtrl` are treated as separate keys
ctrl = true;
//etc..
}
private void This_KeyUp(object sender, KeyEventArgs e)
{
if (e.Key == Key.LeftCtrl)
ctrl = false;
//etc..
}
Whether this is actually a good idea i cannot say...
If you want to handle key gestures i would suggest using dedicated methods like KeyBindings they should only fire when the gesture happened. For other input you may also want to have a look at TextInput which is more abstract and returns the text that the input is translated to.
sorry for this destructive answer but...
after a little bit of research it becomes clear to me...
the event is called "KeyDown" not "KeyCombinationDown" so it is totally independent of any Modifiers pressed BEFORE...
Actually there is a right way for achieve your goal:
Using the Commanding-Pattern.
You define a COMMAND (see google for WPF-Commanding) and add a KeyBinding to your application, where you define the key or the keys/key-combination which will fire up the command...
See the example here:
http://msdn.microsoft.com/en-us/library/system.windows.input.keybinding.aspx
IMHO, this is the only way and semantically more elegant, too.
(if this pattern will not be working for you in GENERAL, you maybe have to use native api with pinvoke).
cheers.
I have been using WPF with all the tunneling and bubbling events and I must say they are simply great and powerful.
But what I always question myself is whether to use the [PreviewKeyUp and KeyUp] or [PreviewKeyDown and Keydown]?
Which combination should I be using to react to key presses and why?
Depends on what you want to do:
PreviewKeyDown = BEFORE the key is pressed
Example: If you have the event on a textbox the current key pressed is not added to the TextBox.Text
PreviewKeyUp = BEFORE the key is let go
KeyDown = AFTER the key is pressed
Example: If you have the event on a textbox the current key pressed is added to the TextBox.Text
KeyUp = AFTER the key is let go
So again it depends on what you want to do. If you want to add TextBox validation use PreviewKeyDown to ignore the text if its incorrect. If you want to do something when the user let go of SPACE then us KeyUp
It all depends upon the situation, for example, selecting an item in a list will want to be KeyDown so that the key can be held and the selected item changed.
If you have an element with multiple visual states, e.g. a button, the KeyDown may change the visual state, then the KeyUp will return the visual to the original state and execute the command.
In your situation it will all depend upon when you want the event to be raised, I wouldn't say either one was better than the other as they are for different uses.
I have a ListBox with a custom data template which contains a CheckBox, a TextBlock and a TextBox. Normally when you select an item in a ListBox, the underlying ListBoxItem is actually what has the focus and as such, it responds to the up and down keys. Additionally, if the CheckBox has focus, since it doesn't do anything with the up and down keys itself, it just happily ignores them and they're handled by the underlying ListBoxItem as well. All's good so far.
However, a TextBox has its own processing rules for the up and down keys, namingly moving the caret up or down a line in text, which doesn't apply here because in this case it's a single line (it's a number actually.) As such, if the TextBox has focus, the up and down keys break the navigation of the ListBox's selection, nor do they really help with editing.
Now while I could handle the PreviewKeyDownEvent (which I do below, but for different reasons) and manually process the behaviors depending on the pressed keys, that's a very specific solution and requires the control to have knowledge of its container's behavior.
In a perfect world (and in pseudocode), I'd like to just say MyTextBox.KeysToIgnore(Up, Down) or something similar and have it do just that... ignore those keys as if it wasn't even there. (Again, not swallow, but ignore so they pass through.)
But until then, here's what I've come up with, which seems to work, but just looks so 'hacky' to me...
private void PreviewKeyDownHandler(object sender, KeyEventArgs e) {
switch (e.Key){
case Key.Up:
case Key.Down:
case Key.OtherKeyToIgnore
case Key.AndAnother
e.Handled = true;
FrameworkElement target = VisualTreeHelper.GetParent(
e.Source as DependencyObject) as FrameworkElement;
target.RaiseEvent(
new KeyEventArgs(
e.KeyboardDevice,
PresentationSource.FromVisual(target),
0,
e.Key
){
RoutedEvent=Keyboard.KeyDownEvent
}
);
break;
}
}
This also has the added negative of not sending the PreviewKeyDown event to the target. Now I could work around that and fake it by sending that event first, then looking at the e.Handled before sending the actual KeyDown message, which makes sense, but then I hit another wall with the PreviewKeyUp and KeyUp events since thanks to setting e.Handled above, I never get the real 'key up' events to know when to send the fake ones. Plus I'm pretty sure I'd also be breaking the direction of the PreviewKeyxxx messages since they bubble the opposite direction from the regular non non-preview versions. (Maybe that's handled internally but I don't think so.)
Like I said... hacky, hacky, hacky!
But it does work so there's that. And I can implement this via Attached Behaviors which is why I even went this route. (In the attached behaviors implementation, it's not a case statement but rather a check against a collection of keys that I specify in XAML.) I just don't like the idea of losing all the other behaviors that I want.
Again, I just want to say 'Hey TextBox... when you see the Up or Down keys being pressed, STFU ya b*stard!!' and otherwise make it key-transparent.
Thoughts anyone?
Man... not even one comment, let alone an answer in almost a month! Oh well... guess my 'hacky' solution above is the way to do this so I'm marking this as the answer.
Yeh, TextBox key event handling sure can give you a headache.
The trouble is that it is impossible to determine call order of callbacks, registered via EventManager.RegisterClassEventHandler. I.e. your callback gets called on unhandled event, then event is handled, and thats it...
I found a way to "unhandle" key events by subclassing TextBox and by calling "AddEventHandler(KeyDownEvent, callback, true)" in constructor. Then set e.Handled = false by circumstances. Seems like callback gets called after event is processed by TextBox.
It is very not nice to have instance of delegate per instance of TextBox, rather than to have one delegate instance per class, but i can't see any other way to workaround.
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.