Как мне поймать ключ VK_TAB в моем элементе управления TEdit и не дать ему потерять фокус? - PullRequest
4 голосов
/ 02 марта 2010

В моем поле TEdit у меня есть текст в виде <number1>..<number2>.

Моя идея:

Когда пользователь входит в элемент управления с помощью TAB из другого элемента управления, выбирается number1.

Когда мой элемент управления TEdit имеет фокус и пользователь снова нажимает TAB , я хочу, чтобы number2 был выбран, а number1 отменен.

И если текущая позиция каретки находится в том месте, где находится число 2, нажатие TAB должно привести к нормальной работе и переместить фокус на следующий элемент управления в форме.

У меня 2 проблемы.

  1. Я не могу поймать нажатие клавиши табуляции, когда поле редактирования уже активно. Я могу поймать его только тогда, когда этот элемент управления вводится / фокусируется.

  2. Я не знаю, есть ли ключ, похожий на # 0, поэтому я мог бы превратить ключ в NoOP.

Есть идеи, как это сделать?

Ответы [ 3 ]

13 голосов
/ 02 марта 2010

Вам лучше написать свой собственный потомок TEdit, который обрабатывает сообщение WM_GETDLGCODE. Общая идея:

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.

Обновлено : та же идея без создания класса потомков TEdit:

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.
0 голосов
/ 02 марта 2010

Возможно, вы захотите посмотреть адрес TJvIP в JvComCtrls . Я думаю, что поиск TabThroughFields и VK_TAB должен поставить вас на правильный путь.

0 голосов
/ 02 марта 2010
  1. вы можете поймать нажатие Tab на событии KeyDown. (Происходит до события KeyPress)
  2. в событии KeyDown вы можете установить ключ на # 0

О, и в случае нажатия клавиши вы можете установить ключ на 0

EDIT Ниже приведен стек вызовов, захватывающий событие KeyDown для клавиши TAB

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

Как вы можете видеть, он не проходит KeyDown, как обычные ключи, а вместо этого вызывает BroadCast, чтобы отправить сообщение ....

Так что вам понадобится ловец сообщений

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

чтобы поймать его.

...