0

In C#, it was written like this:

private void Form1_KeyDown(object sender, KeyEventArgs e)
{
    if (e.Control && e.Shift && e.KeyCode == Keys.P)
    {
        MessageBox.Show("Hello");
    }
}

Capturing Ctrl + Shift + P key stroke in a C# Windows Forms application [duplicate]

I tried to emulate the way they wrote it in C# to Delphi XE8 but it doesn't seemed to work:

procedure TForm12.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
  if (GetKeyState(VK_CONTROL) < 0) and (Key = 53) then
    ShowMessage('You pressed "ctrl + s"');
end;

Note: The TForm.KeyPreview Troperty is set to true

How can I capture that combination key event?

Community
  • 1
  • 1
Wennie
  • 171
  • 1
  • 3
  • 13

3 Answers3

3

The key you are looking for, S, has code $53. The key you specified has code 53 and is the number 5. The difference is the $ which signifies hexadecimal.

You'd avoid such silly mistakes, and make the code much clearer, if you let the compiler do the work:

Key = ord('S')

You really don't want to use magic constants in your program. That is very important.

Note that Key is a virtual key code and the convention is that for the 26 keys of the Latin alphabet, they are represented by the ordinal value of the uppercase letter.

The message already passes the state of the modifier keys in the Shift argument, so it is idiomatic to write the test as follows:

if (ssCtrl in Shift) and (Key = ord('S')) then

Your test using GetKeyState does work well, but it's just not idiomatic.

Note that this test, which matches that in the question will, ignores the state of the other modifier keys. Indeed, the C# code in the question also ignores the state of the ALT modifier.

So you may want a true test for CTRL + S you must also check that the other modifiers are up:

if ([ssCtrl] = Shift*[ssCtrl, ssShift, ssAlt]) and (Key = ord('S')) then

All this said, it's usually much easier to manage your shortcuts using actions. This will allow you to specify shortcuts directly, and let the framework detect the low level key events that make up a shortcut. What's more actions allow you to centralise handling of the actions behind buttons and menus without you repeating yourself.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • Sir David Heffernan, you're such a legend. Thank you for a very concise answer. – Wennie Nov 15 '15 at 00:06
  • @David - I'm surprised that you would say "GetKeyState() does work well". Using **GetKeyState()** is potentially unreliable since the key state may be different at the time that the event is processed than it was at the time of the input event itself (which is exactly why the state at the time of the event is provided *with* the event). – Deltics Nov 15 '15 at 23:06
  • @Deltics Not so. You are thinking of `GetAsyncKeyState`. From the docs: *An application calls GetKeyState in response to a keyboard-input message. This function retrieves the state of the key when the input message was generated.* – David Heffernan Nov 16 '15 at 07:03
  • @David - yikes, you're right. Failure in the memory banks on that one. Having said that, it's still the case that GetKeyState() will only "work well" if the event is called in response to an actual keyboard input event and there is no way to (absolutely) guarantee this. If the handler were to be called directly, the fact that the implementation ignores the contracted TShiftState parameter is not very helpful. – Deltics Nov 16 '15 at 20:03
  • @Deltics Well, I think it's reasonable to assume that this event is attached to the appropriate event. Do note that I advised the asker to use shift state. – David Heffernan Nov 16 '15 at 20:23
1

You can use actions to automate shortcuts. Drop in a TActionManager and add a TAction to it. On that action, assign a Name, Caption, and an OnExecute event handler, and most importantly a value for ShortCut. This can be a string representing the keystrokes, in your case Ctrl+Shift+P. Then, you can either assign that action to various controls, or call it like MyAction.Execute.

Jerry Dodge
  • 26,858
  • 31
  • 155
  • 327
  • After an hour, I realized that using TActionManager is much efficient than my previous idea. Thanks. – Wennie Nov 15 '15 at 00:52
1

In Delphi you use the Shift: TShiftState to check which 'shift'-keys are pressed.

As pointed out in comments your error is that the key value for letter s is not decimal 53, but hexadecimal 53, iow $53in Delphi syntax.

I first thought you also wanted to check for the shift key as in the referenced source of your inspiration, in which case you can test for the exclusive combination as follows:

procedure TForm15.FormKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
  if (Key = $53) and ([ssCtrl, ssShift] = Shift) then
  begin
    ShowMessage('You pressed "ctrl + shift + s"');
    Key := 0; // optional
  end;
end;

You may or may not want to clear the Key parameter to prevent further action by the control with focus.

Rereading your question after another comment, you seem to want to detect only the Ctrl + s combination, in which case the exclusive condition test becomes

  if (Key = $53) and ([ssCtrl] = Shift) then

I recommed to be precise (exclusive) in the shift state test, because the shift state includes not only the Shift, Ctrland Alt keys but also mouse buttons and some gestures.

The documentation on TShiftState provides other possible values of Shift to check for.

Finally, as @David Heffernan points out in his answer, instead of a magic constant ($53) for the key test, use Ord('S').

Tom Brunberg
  • 20,312
  • 8
  • 37
  • 54
  • The problem was not related to shift state. The `GetKeyState(VK_CONTROL) < 0` works fine, albeit is not idiomatic. The problem was `53` instead of `ord('S') = $53`. – David Heffernan Nov 14 '15 at 07:56
  • @David Thanks for the comment. Yes I saw that but forgot to specifically mention it. – Tom Brunberg Nov 14 '15 at 08:51
  • The code doesn't intend to check for shift key. `ShowMessage('You pressed "ctrl + s"');` The asker is attempting to detect Ctrl + S. – David Heffernan Nov 14 '15 at 09:07