views:

538

answers:

3

In my TEdit field I have text in the form <number1>..<number2>.

My idea is:

When a user enters the control using TAB from another control, number1 gets selected.

When my TEdit control has a focus and user presses TAB again, I want the number2 to get selected and number1 to be deselected.

And if current caret position is at the place where number2 is, pressing TAB should act normal and move the focus to the next control on the form.

I have 2 problems.

  1. I cannot catch the tab key press when the Edit field is active already. I can only catch it when this control is being entered/focused.

  2. I don't know if there is a key similar to #0 so I could turn the key into NoOP.

Any ideas, how to do it?

A: 
  1. you can catch a Tab press on a KeyDown event. (Happens before the KeyPress Event)
  2. in a KeyDown event you can set the Key to #0

Oh and in a Key Press event you can set the Key to 0

EDIT The following is a call stack capturing a KeyDown event for a TAB key

Form1.TForm1.GetTab((45061, 9, 0, 983041, 0))
:0101f444 TForm1.GetTab
:004dca20 TWinControl.WndProc + $500
:004ef754 TCustomForm.WndProc + $558
:004d86b3 TControl.Perform + $27
:004ded6a TWinControl.CNKeyDown + $D6
:004dca20 TWinControl.WndProc + $500
:004dc147 TWinControl.MainWndProc + $2F
:004306ea StdWndProc + $16
:7e418734 USER32.GetDC + 0x6d
:7e418816 ; C:\WINDOWS\system32\USER32.dll
:7e41b4c0 ; C:\WINDOWS\system32\USER32.dll
:7e41b50c ; C:\WINDOWS\system32\USER32.dll
:7c90eae3 ntdll.KiUserCallbackDispatcher + 0x13
:7e42f3cc USER32.SendMessageA + 0x49

As you can see it doesn go through KeyDown as normal keys do but instead calls BroadCast to send the message out....

So you'll need a message catcher

Procedure GetTab( var Message: TCMDialogkey ); message CM_DIALOGKEY;

to catch it.

JamesB
KeyPress event requires Key: Char. However, TAB key is a special key and as far as i know it cannot be recognized in this method. the OnKeyDown and OnKeyUp event require Key: Word. These methods do recognize the TAB key becauset here is "VK_TAB". Problem with this is, on key down event does not fire up at all while Edit control is active. OnKeyUp, is fired only in the case when my Edit control -receives- the focus. I need to capture the TAb key press while the Edit control -already has the focus-
Im pretty sure #9 is a the char equivilent to VK_Tab
JamesB
A: 

You might want to look at TJvIPAddress in JvComCtrls. I guess searching for TabThroughFields and VK_TAB should put you on track.

Ulrich Gerhardt
+8  A: 

You are better to write your own TEdit descendant that processes WM_GETDLGCODE message. The general idea is:

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TMyEdit = class(TEdit)
    procedure WMGetDlgCode(var Message: TWMGetDlgCode); message WM_GETDLGCODE;
  end;

type
  TForm1 = class(TForm)
    Edit2: TEdit;
    procedure FormCreate(Sender: TObject);
    procedure FormKeyPress(Sender: TObject; var Key: Char);
  private
    { Private declarations }
    FMyEdit: TMyEdit;
    FDone: Boolean;
    procedure MyEditEnter(Sender: TObject);
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

{ TMyEdit }

procedure TMyEdit.WMGetDlgCode(var Message: TWMGetDlgCode);
begin
  inherited;
  Message.Result:= Message.Result or DLGC_WANTTAB;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  FMyEdit:= TMyEdit.Create(Self);
  FMyEdit.Left:= 40;
  FMyEdit.Top:= 40;
  FMyEdit.Parent:= Self;
  FMyEdit.Text:= '45..90';
  FMyEdit.OnEnter:= MyEditEnter;
  KeyPreview:= True;
end;

procedure TForm1.FormKeyPress(Sender: TObject; var Key: Char);
begin
  if (Key = #9) and (ActiveControl = FMyEdit) then begin
    if FDone then begin
      Perform(CM_DialogKey, VK_TAB, 0);
    end
    else begin
      FMyEdit.SelStart:= 4;
      FMyEdit.SelLength:= 2;
    end;
    FDone:= not FDone;
    Key:= #0;
  end;
end;

procedure TForm1.MyEditEnter(Sender: TObject);
begin
  FDone:= False;
  FMyEdit.SelStart:= 0;
  FMyEdit.SelLength:= 2;
end;

end.

Updated: The same idea without making a TEdit descendant class:

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Edit1: TEdit;
    Edit2: TEdit;
    procedure Edit2Enter(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormKeyPress(Sender: TObject; var Key: Char);
  private
    { Private declarations }
    FDone: Boolean;
    FOldWndProc: TWndMethod;
    procedure Edit2WindowProc(var Message: TMessage);
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Edit2Enter(Sender: TObject);
begin
  FDone:= False;
  Edit2.SelStart:= 0;
  Edit2.SelLength:= 2;
end;

procedure TForm1.Edit2WindowProc(var Message: TMessage);
begin
  if Message.Msg = WM_GETDLGCODE then
    Message.Result:= Message.Result or DLGC_WANTTAB
  else
    if Assigned(FOldWndProc) then FOldWndProc(Message);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  KeyPreview:= True;
  Edit2.Text:= '45..90';
  FOldWndProc:= Edit2.WindowProc;
  Edit2.WindowProc:= Edit2WindowProc;
end;

procedure TForm1.FormKeyPress(Sender: TObject; var Key: Char);
begin
  if (Key = #9) and (ActiveControl = Edit2) then begin
    if FDone then begin
      Perform(CM_DialogKey, VK_TAB, 0);
    end
    else begin
      Edit2.SelStart:= 4;
      Edit2.SelLength:= 2;
    end;
    FDone:= not FDone;
    Key:= #0;
  end;
end;

end.
Serg
Handling `wm_GetDlgCode` is definitely the way to go. You don't *have* to make a descendant class, though. You can set any control's `WndProc` property to catch whatever messages you want.
Rob Kennedy