1
votes

I have an interesting problem that is asked often and yet, I have found that the solutions they give don't work...

I'm using Delphi XE3 on Win 7.

I had always understood that in FormKeyDown or FormKeyPress, you can set the Key to 0 or #0 to indicate that the key has been handled (and so it should not make the "ding" sound for invalid key presses).

I did the following:

  1. new VCL application
  2. Form KeyPreview:=true
  3. add some event handlers (see below)
  4. run
  5. Alt+T (ding sound occurs)

I successfully detect Alt+T and set Key to 0. Form OnKeyPress and Form OnKeyUp do not fire. this is not surprising to me since I had set the Key to 0.

the surprise is that it still makes the "ding" sound after OnKeyDown completes.

how can I stop it from doing that?

In some debugging, it would seem as though a message WM_SYSKEYDOWN is causing this to occur.

thanks!

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;

type
  TForm1 = class(TForm)
    procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
    procedure FormKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
    procedure FormKeyPress(Sender: TObject; var Key: Char);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
  if (key=ord('T')) and (shift=[ssAlt]) then
    key:=0;
end;

procedure TForm1.FormKeyPress(Sender: TObject; var Key: Char);
begin
  showmessage('keypress');
end;

procedure TForm1.FormKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
  showmessage('keyup');
end;

end.


object Form1: TForm1
  Left = 267
  Top = 163
  Caption = 'Form1'
  ClientHeight = 565
  ClientWidth = 654
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'MS Sans Serif'
  Font.Style = []
  KeyPreview = True
  OldCreateOrder = False
  Position = poScreenCenter
  ShowHint = True
  OnKeyDown = FormKeyDown
  OnKeyPress = FormKeyPress
  OnKeyUp = FormKeyUp
  PixelsPerInch = 96
  TextHeight = 13
end
3
Have you tried simply catching the WM_SYKEY... messages? WM_KEY... and WM_SYSKEY... are separate sets of messages. The event handlers you are using are triggered for WM_KEY... messages, not WM_SYSKEY... messages.Remy Lebeau
If your app doesn't have an accelerator attached to alt+t, it should dingDavid Heffernan
deleted my answer; it doesn't work properly!X-Ray

3 Answers

3
votes

Remove the WM_SYSCHAR message from the queue which is already posted (hence the 'ding') after being translated from the WM_SYSKEYDOWN message.

procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
var
  Msg: TMsg;
begin
  if (key=ord('T')) and (shift=[ssAlt]) then begin
    PeekMessage(Msg, 0, WM_SYSCHAR, WM_SYSCHAR, PM_REMOVE);
    key:=0;
  end;
end;


You cannot handle WM_SYSCHAR at the form level (unless it is posted to the form itself) because the VCL does not have a 'SysKeyPreview' feature. However, if it's not only 'T' that you don't want it to 'ding', and this is relevant for all/most forms in your application, you may find handling the message at the application level more convenient. This does not require setting KeyPreview either.

procedure TForm1.ApplicationEvents1Message(var Msg: tagMSG;
  var Handled: Boolean);
begin
  if Msg.message = WM_SYSCHAR then begin
    if Char(Msg.wParam) in ['f','F','g','t','T'] then
      Handled := True;
  end;
end;


Note, if you don't have any special processing for these keys, you'd better let the 'ding' away, otherwise users will be lacking the feedback that these key combinations do not serve any purpose.

0
votes

For backward compatibility reasons I had to make my small, simple tool app to response to 'ALT-N', 'ALT-S', 'ALT-E' and 'ALT-D' key presses. If I handle these in FormKeyDown, it raises that annoying 'beep' or 'ding' sound. Problem was solved by using FormShortCut event instead.

procedure TForm1.FormShortCut(var Msg: TWMKey; var Handled: Boolean);
{Instead of FormKeyDown event, use FormShortCut event.
Form's KeyPreview can be set to False or True.}
begin
    if (GetKeyState(VK_MENU) < 0) then
    case Msg.CharCode of
        Ord('N'): Label1.Caption := 'ALT-N';
        Ord('S'): Label1.Caption := 'ALT-S';
        Ord('E'): Label1.Caption := 'ALT-E';
        Ord('D'): Label1.Caption := 'ALT-D';
    end
end;
0
votes

Just write this on KeyPress event:

procedure TForm1.FormKeyPress(Sender: TObject; var Key: Char);
begin
  Key := #0;
end;