Search code examples
delphifocusvcltcustomcontrol

Why is my custom control not receiving focus on its own?


I am working on a custom control, inherited from a TCustomControl. I am overriding a few events such as KeyDown so that I can capture keyboard and mouse input. However, for whatever reason it is not automatically getting focus on its own when user clicks it. I have to add SetFocus in the MouseDown event, and only then does it actually receive focus. But I'm pretty sure I shouldn't have to do that. Why is this happening, and how do I fix it so that it will get focus on its own without manually calling SetFocus?

  • Custom control's TabStop is set to True.
  • Control's parent form (and all forms) have KeyPreview set to False.

I am defining these methods like so:

type
  TMyControl = class(TCustomControl)
  ...
  protected
    procedure KeyDown(var Key: Word; Shift: TShiftState); override;
    procedure KeyUp(var Key: Word; Shift: TShiftState); override;
    procedure KeyPress(var Key: Char); override;
    procedure MouseDown(Button: TMouseButton; Shift: TShiftState;
      X, Y: Integer); override;
    procedure MouseMove(Shift: TShiftState; X, Y: Integer); override;
    procedure MouseUp(Button: TMouseButton; Shift: TShiftState;
      X, Y: Integer); override;
    ...
  end;

And then the implementation:

procedure TMyControl.KeyDown(var Key: Word; Shift: TShiftState);
begin
  inherited;
  ...
  Invalidate;
end;

procedure TMyControl.KeyPress(var Key: Char);
begin
  inherited;
  ...
  Invalidate;
end;

procedure TMyControl.KeyUp(var Key: Word; Shift: TShiftState);
begin
  inherited;
  ...
  Invalidate;
end;

procedure TMyControl.MouseDown(Button: TMouseButton; Shift: TShiftState; X,
  Y: Integer);
begin
  inherited;
  SetFocus; //<-- This shouldn't be necessary...?
  ...
  Invalidate;
end;

procedure TMyControl.MouseMove(Shift: TShiftState; X, Y: Integer);
begin
  inherited;
  ...
  Invalidate;
end;

procedure TMyControl.MouseUp(Button: TMouseButton; Shift: TShiftState; X,
  Y: Integer);
begin
  inherited;
  ...
  Invalidate;
end;

Solution

  • It seems that manually calling SetFocus on your control's MouseDown handler is exactly what's required. After all, some controls get a "click" event outside of actual click scenarios, and thus shouldn't automatically get focus. And so, if you want to grab focus from the mouse, you must do so exclusively upon a mouse click.

    Furthermore, regarding the TabStop property, it only defines whether it gets focus upon the user tabbing between controls. TabOrder is directly related, defining in what order it gets focus upon tabs.

    Perhaps the custom controls you've made in the past were inherited from a specific control which already had this functionality built-in?