Delphi 在自定义控件中使用TNotifyEvent时无法检测按键

Delphi 在自定义控件中使用TNotifyEvent时无法检测按键,delphi,Delphi,我有一个来自TCustomListBox的自定义控件,我正试图在我的组件源代码中重写OnKeyDown方法 我遇到的问题是,当使用我的组件时,我无法检测到按下了什么键。参数var关键字:Word似乎已变得多余,因此我不确定是否以正确的方式执行此操作,这可能解释了我的问题 下面是一个精简的源代码示例: type TMyListBox = class(TCustomListBox) private FOnClick: TNotifyEvent; FOnKeyDown: TNo

我有一个来自
TCustomListBox
的自定义控件,我正试图在我的组件源代码中重写
OnKeyDown
方法

我遇到的问题是,当使用我的组件时,我无法检测到按下了什么键。参数
var关键字:Word
似乎已变得多余,因此我不确定是否以正确的方式执行此操作,这可能解释了我的问题

下面是一个精简的源代码示例:

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;
在常规项目中尝试我的组件时:

procedure TForm1.MyListBox1KeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
  if Key = VK_DELETE then
  begin
    ShowMessage('test'); // never triggers
  end;
end;
我想这可能与我如何凌驾于事件之上有关?listbox键控的标准声明如下所示:

procedure TForm1.ListBox1KeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
  //
end;
区别在于
Sender
参数,但我在我的组件源代码中不使用该参数

因此,我想知道为什么我无法检测到按下的键,以及我是否应该以不同/更好的方式重写这些方法


谢谢。

OnKeyDown
onkeydup
OnKeyPress
都不是
TNotifyEvents

OnKeyDown
onkeydup
TKeyEvent
,而
OnKeyPress
TKeyPressEvent

TNotifyEvent
定义为

type
  TNotifyEvent = procedure (Sender: TObject) of object;
TKeyEvent
TKeyPressEvent
的定义如下:

type
  TKeyEvent = procedure(Sender: TObject; var Key: Word;
    Shift: TShiftState) of object;
  TKeyPressEvent = procedure(Sender: TObject; var Key: Char) of object;
因此,如果将
FOnKeyDown
声明为type
TNotifyEvent
,则它可以接受的唯一参数是
Sender:TObject
。如果希望它接受与
TKeyEvent
相同的参数,则将其声明为
TKeyEvent
。这同样适用于
TMouseEvent
OnClick

或者,更好的方法是将它们升级为从祖先发布,因为它们已经在
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;

发件人
将自动为您提供;这是触发事件的组件。因此,在
OnClick
处理程序中,
TButton
Sender
将是单击的按钮。你没有选择是否接受它;VCL将这样做。您可以选择不使用它,但无论您是否愿意,您都将获得它(并且必须声明您的过程正在接收它)。
OnKeyDown
onkeydup
OnKeyPress
都不是
TNotifyEvents

OnKeyDown
onkeydup
TKeyEvent
,而
OnKeyPress
TKeyPressEvent

TNotifyEvent
定义为

type
  TNotifyEvent = procedure (Sender: TObject) of object;
TKeyEvent
TKeyPressEvent
的定义如下:

type
  TKeyEvent = procedure(Sender: TObject; var Key: Word;
    Shift: TShiftState) of object;
  TKeyPressEvent = procedure(Sender: TObject; var Key: Char) of object;
因此,如果将
FOnKeyDown
声明为type
TNotifyEvent
,则它可以接受的唯一参数是
Sender:TObject
。如果希望它接受与
TKeyEvent
相同的参数,则将其声明为
TKeyEvent
。这同样适用于
TMouseEvent
OnClick

或者,更好的方法是将它们升级为从祖先发布,因为它们已经在
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;

发件人
将自动为您提供;这是触发事件的组件。因此,在
OnClick
处理程序中,
TButton
Sender
将是单击的按钮。你没有选择是否接受它;VCL将这样做。您可以选择不使用它,但无论您是否愿意,您都将获得它(并且必须声明您的过程正在接收它)。

因此,您正在尝试创建自己的
OnKeyDown
OnMouseDown
类型的
TNotifyEvent
事件,但是在您的项目中,您希望它们的类型是
TKeyEvent
TMouseEvent
?或者问题出在哪里?只要看看
TListBox
的源代码并从中学习:o)@TLama当你这样说的时候,我所做的看起来确实很奇怪,直到后来有人向我指出这个问题,我才意识到这个问题。因此,你试图制作自己的
OnKeyDown
OnMouseDown
类型的
TNotifyEvent
事件,但在你的项目中,你希望它们是
TKeyEvent
TMouseEvent
类型的?或者问题出在哪里?只要看看
TListBox
的源代码,并从中学习:o)@TLama当你这样说的时候,我所做的确实很奇怪,直到后来有人向我指出,我才意识到问题的存在。好东西,谢谢你的帮助和意见肯:)我想为前几周的事道歉,因为我可能对你说了攻击性的或粗鲁的话,所以没有什么不愉快的感觉?:-)好极了:)我现在明白我做错了什么,尤其是在特拉玛的评论之后。基本上,我是在将正确定义的键盘和鼠标事件转换为标准事件,因此正如您在回答中所说,我只是删除了所有TNotifyEvent内容,并更改了发布的方法,如:
property OnClick好东西,谢谢你的帮助和输入肯:)我想为前几周的事道歉,因为我可能对你说了攻击性的或粗鲁的话,所以没有什么不愉快的感觉?:-)好极了:)我现在明白我做错了什么,尤其是在特拉玛的评论之后。基本上,我是在将正确定义的键盘和鼠标事件转换为标准事件,因此正如您在回答中所说,我只是删除了所有TNotifyEvent内容,并更改了发布的方法,如:
property OnClick