在Delphi XE中,我试图实现一种“即时搜索”功能-一种类似于Firefox的“键入时搜索”功能,但在开源剪贴板扩展程序Ditto中的类似功能可以更好地说明这一点:

这里有处理典型导航事件的项目列表。但是,所有字母数字键以及导航和编辑命令(向右/向左箭头,Shift +箭头,退格键,删除等)都应重新路由到列表下方的编辑框。编辑框的OnChange事件将触发列表的刷新。

UI的要点是用户不必在控件之间使用Tab键或Shift Tab键。这两个控件(列表和编辑框)应该像单个控件一样“感觉”,搜索UI的行为应而不是取决于控件具有焦点的情况。

似乎我最好的选择是将列表控制(我正在使用TcxTreeList)中的某些键盘事件 转发到编辑框,并将少量导航键从编辑框转发到列表。我该如何实现?

笔记:

  • TcxTreeList当然支持增量搜索,但这不是我想要的。搜索进入SQLite数据库并查找子字符串匹配项。该列表仅显示数据库中匹配的项目。
  • 有一些重叠,例如这两个控件通常都可以处理VK_HOME和VK_END,但这没关系-在这种情况下,键将进入列表。我需要决定是转发每个单独的按键,还是在收到它的控件中进行处理。

  • 编辑时:
    一种明显的方法似乎是调用编辑控件的相应KeyDown,KeyUp和KeyPress方法,如下所示:
    type
      THackEdit = class( TEdit );
    
    procedure TMainForm.cxTreeList1KeyDown(Sender: TObject; var Key: Word;
        Shift: TShiftState);
    begin
      THackEdit( edit1 ).KeyDown( Key, Shift );
    end;
    

    不幸的是,这没有效果。我的猜测是,除非专注,否则TEdit不会处理关键事件。使用SendMessage(THackEdit(edit1).Handle,WM_KEYDOWN,Key,0)也无效。

    最佳答案

    您可以使用VCL控件的消息处理功能,并将相关消息相互发送。我不知道'TcxTreeList',但是以下内容演示了同步响应键盘事件的编辑控件和备忘录控件的想法(当然,尽可能)。

    type
      TEdit = class(stdctrls.TEdit)
      private
        FMsgCtrl: TWinControl;
        FRecursing: Boolean;
        procedure WmChar(var Msg: TWMChar); message WM_CHAR;
        procedure WmKeyDown(var Msg: TWMKeyDown); message WM_KEYDOWN;
        procedure WmKeyUp(var Msg: TWMKeyUp); message WM_KEYUP;
      end;
    
      TMemo = class(stdctrls.TMemo)
      private
        FMsgCtrl: TWinControl;
        FRecursing: Boolean;
        procedure WmChar(var Msg: TWMChar); message WM_CHAR;
        procedure WmKeyDown(var Msg: TWMKeyDown); message WM_KEYDOWN;
        procedure WmKeyUp(var Msg: TWMKeyUp); message WM_KEYUP;
      end;
    
      TForm1 = class(TForm)
        Edit1: TEdit;
        Memo1: TMemo;
        procedure FormCreate(Sender: TObject);
      private
      public
      end;
    
    var
      Form1: TForm1;
    
    implementation
    
    {$R *.dfm}
    
    { TEdit }
    
    procedure TEdit.WmChar(var Msg: TWMChar);
    begin
      if not FRecursing then begin
        inherited;
    
        // Insert test here to see if the message will be forwarded
        // exit/modify accordingly.
    
        if Assigned(FMsgCtrl) then begin
          FRecursing := True;
          try
            FMsgCtrl.Perform(Msg.Msg,
                             MakeWParam(Msg.CharCode, Msg.Unused), Msg.KeyData);
          finally
            FRecursing := False;
          end;
        end;
      end;
    end;
    
    procedure TEdit.WmKeyDown(var Msg: TWMKeyDown);
    begin
      // exact same contents as in the above procedure
    end;
    
    procedure TEdit.WmKeyUp(var Msg: TWMKeyUp);
    begin
      // same here
    end;
    
    { TMemo }
    
    procedure TMemo.WmChar(var Msg: TWMChar);
    begin
      // same here
    end;
    
    procedure TMemo.WmKeyDown(var Msg: TWMKeyDown);
    begin
      // same here
    end;
    
    procedure TMemo.WmKeyUp(var Msg: TWMKeyUp);
    begin
      // same here
    end;
    
    
    { TForm1 }
    
    procedure TForm1.FormCreate(Sender: TObject);
    begin
      Edit1.FMsgCtrl := Memo1;
      Memo1.FMsgCtrl := Edit1;
    end;
    

    您可能需要干预其他消息,但您明白了。

    如果由于某种原因您不能派生新的控件或覆盖消息处理,则可以考虑对控件进行子类化。回答this question将会告诉您如何执行此操作。

    关于windows - 将键盘事件从一个Windows控件转发到另一个,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/4725634/

    10-14 17:59
    查看更多