views:

156

answers:

5

I've built a custom control that I'm trying to send input to. It will accept mouse input and report MouseDown, MouseMove and MouseUp correctly, but for whatever reason, it won't accept keyboard input. When I click on it, it doesn't receive focus, and any keys I press get interpreted by whatever control had the focus already.

This is probably something really simple. The first place I thought to look was in the ControlStyle property, but the only thing I can see in the helpfile about keyboard input is csNoStdEvents, which disables it, and my control doesn't have that. So what do I need to do to make it so my control can receive input focus?

A: 

Is the keystroke available at form level? That is, is KeyPreview turned on, and can you see the keystroke in the form's OnKeypress event? You can follow it from there in the debugger. Is the control (as Dan indicated) suitable for keyboard input? For instance, a TLabel, although it displays text, is a graphical control.

Jay Faubion
A: 

I've checked the code for my control and I can't see anything that might stop this working. Are you calling "inherited" in the Create procedure?

I do handle the following, but nothing special:

procedure WMSetFocus(var Message: TWMSetFocus); message WM_SETFOCUS;
procedure WMKillFocus(var Message: TWMKillFocus); message WM_KILLFOCUS;
procedure WMGetDlgCode(var Message: TWMGetDlgCode); message WM_GETDLGCODE;

procedure KeyDown(var Key: Word; Shift: TShiftState); override;
mj2008
A: 

Do you have WS_TABSTOP set? You don't have input focus without that, I believe. But this is based on a recollection from nearly 10 years ago, when I was writing my own syntax-highlighting code editor, for which I have long since lost the source.

{TWinControl.}TabStop := True; ought to do. A quick test app with a do-nothing component derived from TWinControl and displaying a dialog for key events seems to show that it makes all the difference.

Barry Kelly
Just tried that, and it doesn't seem to change anything. I still can't get keyboard focus.
Mason Wheeler
+2  A: 

Could it be as simple as calling SetFocus on mouse down?

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

  if CanFocus then
    SetFocus;
end;
Lars Truijens
I strongly believe that this is the issue.
Andreas Rejbrand
+1  A: 

A few things to try:

  • On MouseDown, call Windows.SetFocus(Handle). In my experience, the WinAPI function SetFocus often works better than the VCL's SetFocus method.
  • In response to the WM_GETDLGCODE message, reply with Message.Result := Message.Result or DLGC_WANTCHARS or DLGC_WANTARROWS or DLGC_WANTTAB or DLGC_WANTALLKEYS;
Andreas Rejbrand
Thanks. It was either accept yours or Lars's, because you both had the answer to putting SetFocus inside MouseDown, and that worked. I picked yours because of the WM_GETDLGCODE thing, which I also ended up needing since I need to trap the arrow keys specifically.
Mason Wheeler