Solved

Capturing arrow up and down in CustomControl

Posted on 2002-06-20
10
1,119 Views
Last Modified: 2010-08-05
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
0
Comment
Question by:Levente
  • 5
  • 4
10 Comments
 

Expert Comment

by:ZafodBiblbrox
ID: 7095346
hi, try to use this code

type
  TCustomXEdit = class(TCustomEdit)
  private
    procedure CNKeyDown(var Message: TWMKey); message CN_KEYDOWN;
  protected
  end;


procedure TCustomXEdit.CNKeyDown(var Message: TWMKey);
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, CharCode, 0) = 0) and
         (Perform(WM_GETDLGCODE, 0, 0) and Mask = 0) and
         (GetParentForm(Self).Perform(CM_DIALOGKEY, CharCode, KeyData) <> 0) then
        begin
        x[16] := oldShift;
        SetKeyboardState(x);
        Exit;
        end;
      end;
    x[16] := oldShift;
    SetKeyboardState(x);
    end;
  inherited;
end;



end.
0
 
LVL 7

Accepted Solution

by:
Cynna earned 300 total points
ID: 7095617
Levente,

Delphi , with respect to your problem, has two classes
of components. Those which are expected to use arrow
keys (such as TMemo, TEdit, etc.) and those, like TButton,
who can't do anything useful with arrow key press. For
those, Delphi doesn't propagate message ON_KEYDOWN to
event handler of those components, but instead implements
default behaviour, ie focus change.
The trick is that Delphi 'asks' component 'Do you want to
handle Arrow keys?'. Only if component explicitly answers
'Yes I do!' will Delphi proceed Arrow press to the component.
Message which is sent to component as this question is
WM_GETDLGCODE, and message result (ie, component affirmative answer) is DLGC_WANTARROWS.

So, it boils down to this: implement message handler
for WM_GETDLGCODE message, and return result DLGC_WANTARROWS in your component.



Example (you can Copy/Paste, it will work):
----------------------------------------------------------------

Add this in private section of you component:

  private
    procedure WMGetDlgCode(var message: TWMGetDlgCode); message WM_GETDLGCODE;
  end;


And, implementation is:

procedure TMyComponent.WMGetDlgCode(var message: TWMGetDlgCode);
begin
  inherited;
  // Answer Delphi that this component wants to handle its own arrow key press:
  message.result:=DLGC_WANTARROWS;
end;



0
 

Author Comment

by:Levente
ID: 7098143
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.
0
 
LVL 7

Expert Comment

by:Cynna
ID: 7098460
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_WANTARROWS;
end;

(....)


procedure TForm1.FormCreate(Sender: TObject);
begin
  myButton1:=TmyButton.Create(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_WANTARROWS;
   with:
         message.result:=message.result 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.result 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_NEXTDLGCTL,0,0);
  inherited KeyDown(Key, Shift);
end;


Above code will trigger KeyDown event in case arrow or Tab key is pressed.
0
 

Author Comment

by:Levente
ID: 7116843
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
0
Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

 

Author Comment

by:Levente
ID: 7126449
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),random(255));
   repaint;
end;

procedure TForm1.onMyButtonClick(Sender: 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.
0
 
LVL 7

Expert Comment

by:Cynna
ID: 7127097
Levente,

Just add TabStop:=TRUE; after Caption:= 'bla' in MyButton1 properties.

0
 

Author Comment

by:Levente
ID: 7131456
I'd prefer Cynna's solution.
0
 

Author Comment

by:Levente
ID: 7131460
As simple as that! Thank you!
0
 
LVL 7

Expert Comment

by:Cynna
ID: 7131647
:)
You are welcome...
0

Featured Post

Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

A lot of questions regard threads in Delphi.   One of the more specific questions is how to show progress of the thread.   Updating a progressbar from inside a thread is a mistake. A solution to this would be to send a synchronized message to the…
Hello everybody This Article will show you how to validate number with TEdit control, What's the TEdit control? TEdit is a standard Windows edit control on a form, it allows to user to write, read and copy/paste single line of text. Usua…
This tutorial gives a high-level tour of the interface of Marketo (a marketing automation tool to help businesses track and engage prospective customers and drive them to purchase). You will see the main areas including Marketing Activities, Design …
As a trusted technology advisor to your customers you are likely getting the daily question of, ‘should I put this in the cloud?’ As customer demands for cloud services increases, companies will see a shift from traditional buying patterns to new…

863 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

22 Experts available now in Live!

Get 1:1 Help Now