【问题标题】:How do I catch a VK_TAB key in my TEdit control and not let it lose the focus?如何在我的 TEdit 控件中捕获 VK_TAB 键而不让它失去焦点?
【发布时间】:2011-01-22 17:40:33
【问题描述】:

在我的 TEdit 字段中,我的文本格式为 <number1>..<number2>。

我的想法是:

当用户使用 TAB 从另一个控件进入该控件时,number1 被选中。

当我的 TEdit 控件具有焦点并且用户再次按下 TAB 时,我希望 number2 被选中并取消选中 number1。

如果当前插入符号的位置在 number2 所在的位置,按 TAB 应该正常并将焦点移动到窗体上的下一个控件。

我有 2 个问题。

  1. 当“编辑”字段已处于活动状态时,我无法捕捉到 Tab 键按下。只有在输入/聚焦此控件时才能捕捉到它。

  2. 不知道有没有类似#0的key,所以我可以把key变成NoOP。

有什么想法,怎么做?

【问题讨论】:

    标签: delphi delphi-2009 keypress


    【解决方案1】:

    您最好编写自己的 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.
    

    【讨论】:

    • 处理wm_GetDlgCode 绝对是要走的路。但是,您没有必须创建后代类。您可以设置任何控件的WndProc 属性来捕获您想要的任何消息。
    【解决方案2】:
    1. 您可以在 KeyDown 事件上捕捉 Tab 按下。 (发生在 KeyPress 事件之前)
    2. 在 KeyDown 事件中,您可以将 Key 设置为 #0

    哦,在 Key Press 事件中,您可以将 Key 设置为 0

    编辑 以下是捕获 TAB 键的 KeyDown 事件的调用堆栈

    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;
    

    抓住它。

    【讨论】:

    • KeyPress 事件需要 Key: Char。但是,TAB 键是一个特殊键,据我所知,这种方法无法识别它。 OnKeyDown 和 OnKeyUp 事件需要 Key: Word。这些方法确实可以识别 TAB 键,因为这里是“VK_TAB”。问题是,在编辑控件处于活动状态时,按下按键事件根本不会触发。 OnKeyUp,仅在我的编辑控件接收焦点时触发。我需要在编辑控件 - 已经有焦点时捕获 TAb 按键 -
    • 我很确定 #9 是 VK_Tab 等价的字符
    【解决方案3】:

    您可能想查看JvComCtrls 中的 TJvIPAddress。我想搜索 TabThroughFields 和 VK_TAB 应该会让你走上正轨。

    【讨论】:

      猜你喜欢
      • 2013-06-12
      • 2013-06-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-22
      • 2012-08-13
      • 1970-01-01
      相关资源
      最近更新 更多