user1175743
user1175743

Reputation:

Cannot detect keypress when using TNotifyEvent in a custom control

I have a custom control derived from TCustomListBox and I am trying to override the OnKeyDown method within my components source.

The problem I have run into is when using my component I cannot detect what key has been pressed. The parameter var Key: Word; appears to have become redundant, so I am not sure if I am doing this the correct way or not which might explain my problem.

Here is a stripped down source example:

type
  TMyListBox = class(TCustomListBox)
  private
    FOnClick: TNotifyEvent;
    FOnKeyDown: TNotifyEvent;
    FOnMouseDown: TNotifyEvent;
  protected
    procedure Click; override;
    procedure KeyDown(var Key: Word; Shift: TShiftState); override;
    procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  published
    property OnClick: TNotifyEvent read FOnClick write FOnClick;
    property OnKeyDown: TNotifyEvent read FOnKeyDown write FOnKeyDown;
    property OnMouseDown: TNotifyEvent read FOnMouseDown write FOnMouseDown;
  end;

implementation

constructor TMyListBox.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);

  OnClick := FOnClick;
  OnKeyDown := FOnKeyDown;
  OnMouseDown := FOnMouseDown;
end;

destructor TMyListBox.Destroy;
begin
  inherited Destroy;
end;

procedure TMyListBox.Click();
begin
  inherited;

  // do something

  if Assigned(FOnClick) then
    FOnClick(Self);
end;

procedure TMyListBox.KeyDown(var Key: Word; Shift: TShiftState);
begin
  inherited;

  // do something

  if Assigned(FOnKeyDown) then
    FOnKeyDown(Self);
end;

procedure TMyListBox.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  inherited;

  // do something

  if Assigned(FOnMouseDown) then
    FOnMouseDown(Self);
end;

When trying my component in a regular project:

procedure TForm1.MyListBox1KeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
  if Key = VK_DELETE then
  begin
    ShowMessage('test'); // never triggers
  end;
end;

I think it may be something to do with how I am overriding the event? The standard declaration for a listbox keydown looks like this:

procedure TForm1.ListBox1KeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
  //
end;

The difference is the Sender parameter but I dont and cant use that in my component source.

So I guess I want to know why I cannot detect the key pressed, and whether I should be overriding the methods in a different/better way?

Thanks.

Upvotes: 0

Views: 1463

Answers (1)

Ken White
Ken White

Reputation: 125757

OnKeyDown, OnKeyUp, and OnKeyPress are not TNotifyEvents.

OnKeyDown and OnKeyUp are TKeyEvent, and OnKeyPress is a TKeyPressEvent.

TNotifyEvent is defined as

type
  TNotifyEvent = procedure (Sender: TObject) of object;

TKeyEvent and TKeyPressEvent are defined as follows:

type
  TKeyEvent = procedure(Sender: TObject; var Key: Word;
    Shift: TShiftState) of object;
  TKeyPressEvent = procedure(Sender: TObject; var Key: Char) of object;

So if you declare FOnKeyDown as type TNotifyEvent, the only parameter it can accept is Sender: TObject. If you want it to accept the same parameters as TKeyEvent, then declare it as TKeyEvent. The same applies to TMouseEvent and OnClick.

Or, better yet, just promote them to published from the ancestor, as they're already declared as protected in TCustomListBox:

type
  TMyListBox = class(TCustomListBox)
  protected
    procedure Click; override;
    procedure KeyDown(var Key: Word; Shift: TShiftState); override;
    procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  published
    property OnClick;
    property OnKeyDown;
    property OnMouseDown;
  end;

Sender will automatically be provided for you; it's the component that triggered the event. So in an OnClick handler for a TButton, Sender will be the button that was clicked. You don't have any choice whether to receive it or not; the VCL will do so. You can choose not to use it, but you're going to get it (and have to declare your procedure as receiving it) whether you want to or not.

Upvotes: 4

Related Questions