Levente
asked on
Capturing arrow up and down in CustomControl
I've got a component which is the descendant of TCustomContol. I need to capture when user presses cursor up and down keys if my component has the focus.
Currently, I override the KeyDown method of TCustomControl but it executes when user presses an alphanumerical character. If I press the up or down arrows the focus jumps to the previous or the next component. (Capturing WM_KEYDOWN message doesn't help, either.)
Any ide how to capture the cursor moving keys?
Thanks,
Levente
Currently, I override the KeyDown method of TCustomControl but it executes when user presses an alphanumerical character. If I press the up or down arrows the focus jumps to the previous or the next component. (Capturing WM_KEYDOWN message doesn't help, either.)
Any ide how to capture the cursor moving keys?
Thanks,
Levente
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
Cynna,
Your solution works. One more thing I noticed: TMyComponent appears in the Tab order list of the parent Form, but if I press tab to skip focus TMyComponent never gets focus. (I need to click on the component and manually call TMyComponent.SetFocus in the onClick handler to get focus.)
Is there anything that can be done to have my component get focus automatically?
Thank you,
Levente
ZafodBiblbrox, thank you for your solution, however I'd prefer Cynna's.
Your solution works. One more thing I noticed: TMyComponent appears in the Tab order list of the parent Form, but if I press tab to skip focus TMyComponent never gets focus. (I need to click on the component and manually call TMyComponent.SetFocus in the onClick handler to get focus.)
Is there anything that can be done to have my component get focus automatically?
Thank you,
Levente
ZafodBiblbrox, thank you for your solution, however I'd prefer Cynna's.
Levente,
I'm not sure if I understand you correctly. Could you
post a simple reproducible example?
(1) Are you saying that your component can't receive focus using Tab key? That shouldn't happen because of the code
segment I posted.
I just tried a simple component that uses this principle,
and it works just as expected.
It's really simple, you can copy/paste it directly
in your project and tell me how it went:
TmyButton = class(TButton)
private
procedure WMGetDlgCode(var message: TWMGetDlgCode); message WM_GETDLGCODE;
end;
(....)
procedure TmyButton.WMGetDlgCode(var message: TWMGetDlgCode);
begin
inherited;
message.result:=DLGC_WANTA RROWS;
end;
(....)
procedure TForm1.FormCreate(Sender: TObject);
begin
myButton1:=TmyButton.Creat e(Self);
with myButton1 do begin
Left:=150;
Top:=70;
Width:=40; Height:=20;
Caption:='bla';
Parent:=Form1;
end;
end;
(2) Does this example do what you want?
If no, then I missunderstood you, please try to be more detailed.
If yes, then please try posting simple example-component, like the one above, that doesn't work OK, so I can reproduce your problem and try to solve it.
Since I'm not quite shure what exactly is the problem,
I'll now try a few solutions based on my guess on point (1):
a) try replacing line
message.result:=DLGC_WANTA RROWS;
with:
message.result:=message.re sult or DLGC_WANTARROWS;
b) if that doesn't work, you might override KeyDown event
and handle focus change yourself if pressed key is Tab:
private
procedure WMGetDlgCode(var message: TWMGetDlgCode); message WM_GETDLGCODE;
// override KeyDown for special preprocessing:
procedure KeyDown(var Key: Word; Shift: TShiftState); override;
end;
... and in implemetion:
procedure TmyButton.WMGetDlgCode(var message: TWMGetDlgCode);
begin
inherited;
// We now want to catch TAB, too:
message.result:=message.re sult or DLGC_WANTARROWS or DLGC_WANTTAB;
end;
procedure TMyComponent.KeyDown(var Key: Word; Shift: TShiftState);
begin
// If Tab is pressed, explicitly change focus to next component:
if Key=VK_TAB then PostMessage(Parent.Handle, WM_NEXTDLG CTL,0,0);
inherited KeyDown(Key, Shift);
end;
Above code will trigger KeyDown event in case arrow or Tab key is pressed.
I'm not sure if I understand you correctly. Could you
post a simple reproducible example?
(1) Are you saying that your component can't receive focus using Tab key? That shouldn't happen because of the code
segment I posted.
I just tried a simple component that uses this principle,
and it works just as expected.
It's really simple, you can copy/paste it directly
in your project and tell me how it went:
TmyButton = class(TButton)
private
procedure WMGetDlgCode(var message: TWMGetDlgCode); message WM_GETDLGCODE;
end;
(....)
procedure TmyButton.WMGetDlgCode(var
begin
inherited;
message.result:=DLGC_WANTA
end;
(....)
procedure TForm1.FormCreate(Sender: TObject);
begin
myButton1:=TmyButton.Creat
with myButton1 do begin
Left:=150;
Top:=70;
Width:=40; Height:=20;
Caption:='bla';
Parent:=Form1;
end;
end;
(2) Does this example do what you want?
If no, then I missunderstood you, please try to be more detailed.
If yes, then please try posting simple example-component, like the one above, that doesn't work OK, so I can reproduce your problem and try to solve it.
Since I'm not quite shure what exactly is the problem,
I'll now try a few solutions based on my guess on point (1):
a) try replacing line
message.result:=DLGC_WANTA
with:
message.result:=message.re
b) if that doesn't work, you might override KeyDown event
and handle focus change yourself if pressed key is Tab:
private
procedure WMGetDlgCode(var message: TWMGetDlgCode); message WM_GETDLGCODE;
// override KeyDown for special preprocessing:
procedure KeyDown(var Key: Word; Shift: TShiftState); override;
end;
... and in implemetion:
procedure TmyButton.WMGetDlgCode(var
begin
inherited;
// We now want to catch TAB, too:
message.result:=message.re
end;
procedure TMyComponent.KeyDown(var Key: Word; Shift: TShiftState);
begin
// If Tab is pressed, explicitly change focus to next component:
if Key=VK_TAB then PostMessage(Parent.Handle,
inherited KeyDown(Key, Shift);
end;
Above code will trigger KeyDown event in case arrow or Tab key is pressed.
ASKER
Cynna,
Thank you for your answer. I've been on holiday that's whay I couldn't try your solution. In a few days I'll get back tou you.
Regards,
Levente
Thank you for your answer. I've been on holiday that's whay I couldn't try your solution. In a few days I'll get back tou you.
Regards,
Levente
ASKER
Cynna,
Sorry for the late answer. Here is the code that demonstrates my problem. Focus is in the EditBox. Press Tab, but focus doesn't move to the TMyButton, unless you click on it and force focus to move to it!
unit Temp;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
procedure onMyButtonClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
TMyButton = class(TCustomControl)
private
procedure WMGetDlgCode( var Msg: TWMGetDlgCode); message WM_GETDLGCODE;
procedure KeyDown(var Key: Word; Shift: TShiftState); override;
protected
procedure Paint; override;
end;
var
Form1: TForm1;
MyButton1: TMyButton;
Edit1: TEdit;
implementation
{$R *.DFM}
procedure TMyButton.WMGetDlgCode( var Msg: TWMGetDlgCode);
begin
inherited;
msg.result:= DLGC_WANTARROWS;
end;
procedure TMyButton.Paint;
begin
Canvas.Rectangle( 0,0, Width, Height);
end;
procedure TMyButton.KeyDown(var Key: Word; Shift: TShiftState);
begin
Canvas.Brush.Color:= RGB( random(255),random(255),ra ndom(255)) ;
repaint;
end;
procedure TForm1.onMyButtonClick(Sen der: TObject);
begin
MyButton1.SetFocus;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
MyButton1:= TmyButton.Create( self );
with MyButton1 do begin
SetBounds( 100, 50, 60, 60);
Caption:= 'bla';
Parent:= Form1;
onClick:= onMyButtonClick;
end;
Edit1:= TEdit.Create( self );
with Edit1 do begin
SetBounds( 10, 10, 60, 20);
Parent:= Form1;
Text:= 'Sample';
end;
end;
end.
Sorry for the late answer. Here is the code that demonstrates my problem. Focus is in the EditBox. Press Tab, but focus doesn't move to the TMyButton, unless you click on it and force focus to move to it!
unit Temp;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
procedure onMyButtonClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
TMyButton = class(TCustomControl)
private
procedure WMGetDlgCode( var Msg: TWMGetDlgCode); message WM_GETDLGCODE;
procedure KeyDown(var Key: Word; Shift: TShiftState); override;
protected
procedure Paint; override;
end;
var
Form1: TForm1;
MyButton1: TMyButton;
Edit1: TEdit;
implementation
{$R *.DFM}
procedure TMyButton.WMGetDlgCode( var Msg: TWMGetDlgCode);
begin
inherited;
msg.result:= DLGC_WANTARROWS;
end;
procedure TMyButton.Paint;
begin
Canvas.Rectangle( 0,0, Width, Height);
end;
procedure TMyButton.KeyDown(var Key: Word; Shift: TShiftState);
begin
Canvas.Brush.Color:= RGB( random(255),random(255),ra
repaint;
end;
procedure TForm1.onMyButtonClick(Sen
begin
MyButton1.SetFocus;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
MyButton1:= TmyButton.Create( self );
with MyButton1 do begin
SetBounds( 100, 50, 60, 60);
Caption:= 'bla';
Parent:= Form1;
onClick:= onMyButtonClick;
end;
Edit1:= TEdit.Create( self );
with Edit1 do begin
SetBounds( 10, 10, 60, 20);
Parent:= Form1;
Text:= 'Sample';
end;
end;
end.
Levente,
Just add TabStop:=TRUE; after Caption:= 'bla' in MyButton1 properties.
Just add TabStop:=TRUE; after Caption:= 'bla' in MyButton1 properties.
ASKER
I'd prefer Cynna's solution.
ASKER
As simple as that! Thank you!
:)
You are welcome...
You are welcome...
type
TCustomXEdit = class(TCustomEdit)
private
procedure CNKeyDown(var Message: TWMKey); message CN_KEYDOWN;
protected
end;
procedure TCustomXEdit.CNKeyDown(var
var Mask: Integer;
x: TKeyboardState;
oldShift: Byte;
begin
with Message do
begin
GetKeyboardState(x);
oldShift := x[16];
if CharCode = VK_UP then
begin
CharCode := VK_TAB;
x[16] := 128;
SetKeyboardState(x);
end
else if (CharCode = 13) or (CharCode = VK_DOWN) then
begin
CharCode := VK_TAB;
x[16] := 1;
SetKeyboardState(x);
end;
Result := 1;
if not (csDesigning in ComponentState) then
begin
if Perform(CM_CHILDKEY, CharCode, Integer(Self)) <> 0 then
begin
x[16] := oldShift;
SetKeyboardState(x);
Exit;
end;
Mask := 0;
case CharCode of
VK_TAB: Mask := DLGC_WANTTAB;
VK_LEFT, VK_RIGHT, VK_UP, VK_DOWN: Mask := DLGC_WANTARROWS;
VK_RETURN, VK_EXECUTE, VK_ESCAPE, VK_CANCEL: Mask := DLGC_WANTALLKEYS;
end;
if (Mask <> 0) and (Perform(CM_WANTSPECIALKEY
(Perform(WM_GETDLGCODE, 0, 0) and Mask = 0) and
(GetParentForm(Self).Perfo
begin
x[16] := oldShift;
SetKeyboardState(x);
Exit;
end;
end;
x[16] := oldShift;
SetKeyboardState(x);
end;
inherited;
end;
end.