Пересылка событий клавиатуры от одного элемента управления Windows к другому - PullRequest
5 голосов
/ 18 января 2011

В Delphi XE я пытаюсь реализовать функцию «мгновенного поиска», которая несколько напоминает Firefox «поиск по мере ввода», но лучше иллюстрируется аналогичной функцией в расширителе буфера обмена с открытым исходным кодом, То же

Ditto search interface

Существует список элементов, которые обрабатывают типичные события навигации. Однако любые буквенно-цифровые клавиши, а также команды навигации и редактирования (стрелки вправо / влево, стрелки Shift + Backspace, Delete и т. Д.) Должны быть перенаправлены в поле редактирования под списком. Событие OnChange поля редактирования вызовет обновление списка.

Смысл интерфейса состоит в том, что пользователю не нужно вкладывать или сдвигать вкладку между элементами управления. Два элемента управления (список и поле редактирования) должны «ощущаться», как если бы они были единым элементом управления. Поведение интерфейса поиска должно , а не зависеть от того, какой элемент управления имеет фокус.

Похоже, мой лучший вариант - переслать определенные события клавиатуры из элемента управления списком (я использую TcxTreeList ) в поле редактирования и переслать несколько клавиш навигации из поле для редактирования списка. Как мне этого добиться?

Примечания:

  1. TcxTreeList, конечно, поддерживает пошаговый поиск, но это не то, что мне нужно. Поиск идет по базе данных SQLite и ищет совпадения подстрок. В списке отображаются только соответствующие элементы из базы данных.

  2. Есть некоторые совпадения, например оба элемента управления обычно обрабатывают 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) также не имеет никакого эффекта.

Ответы [ 2 ]

6 голосов
/ 18 января 2011

Вы можете использовать возможность обработки сообщений элемента управления VCL и отправлять соответствующие сообщения друг другу. Я не знаю о 'TcxTreeList', но следующее демонстрирует идею элемента управления редактирования и элемента управления memo, отвечающего на события клавиатуры синхронно (где бы это ни было возможно, конечно).

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;

Возможно, вам придется вмешаться в дополнительные сообщения, но вы поняли идею.

Если по той или иной причине вы не можете получить новый элемент управления или переопределить обработку сообщений, вы можете рассмотреть подклассификацию элементов управления. Ответ на на этот вопрос покажет вам, как это сделать.

2 голосов
/ 18 января 2011

Не совсем то, что вы просите, но для похожих результатов я использую следующий прием.

Предположим, у вас есть один TEdit Edit1 и один TListbox Listbox1.

В событии OnEnter Listbox1 просто установите фокус на Edit1

procedure TForm1.ListBox1Enter(Sender: TObject); 
 begin   
  edit1.SetFocus; 
 end;

И всобытие OnKeyDown для Edit1, используйте стрелки вверх и вниз для перемещения по элементам списка и используйте клавишу ввода для перемещения выбранного элемента в поле редактирования.

procedure TForm1.Edit1KeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); 
 var k:word; 
 begin   
  if (Shift=[]) and (key=VK_DOWN) then    
   begin
    listbox1.ItemIndex:=listbox1.ItemIndex+1;
    key:=0;    
   end   
  else if (Shift=[]) and (key=VK_UP) then
   begin
    listbox1.ItemIndex:=listbox1.ItemIndex-1;
    key:=0;    
   end   
  else if (Shift=[]) and (key=VK_RETURN) then
   begin
    edit1.text:=listbox1.items[listbox1.itemindex];
   end; 
 end;
...