2011-01-18 8 views
5

En Delphi XE, intento implementar una función de "búsqueda instantánea", que se asemeja algo a la "búsqueda mientras escribe" de Firefox, pero se ilustra mejor mediante una función similar en un extensor de código abierto portapapeles, Ditto:Reenvío de eventos de teclado de un control de Windows a otro

Ditto search interface

Hay una lista de elementos que maneja los eventos de navegación típicos. Sin embargo, todas las teclas alfanuméricas, así como los comandos de navegación y edición (flechas derecha/izquierda, desplazamiento + flechas, retroceso, eliminación, etc.) deben redirigirse al cuadro de edición debajo de la lista. Un evento OnChange del cuadro de edición activará una actualización de la lista.

El objetivo de la IU es que el usuario no tenga que tabular o desplazar la pestaña entre los controles. Los dos controles (la lista y el cuadro de edición) deberían 'sentirse' como si fueran un solo control. El comportamiento de la interfaz de usuario de búsqueda debe no depender de qué control tenga foco

Parece mi mejor opción es hacia adelante ciertos eventos de teclado de la lista de control (estoy usando TcxTreeList) al cuadro de edición, y remitirá un puñado de teclas de navegación desde el cuadro de edición de la lista. ¿Cómo puedo lograr eso?

Notas:

  1. TcxTreeList admite la búsqueda incremental, por supuesto, pero esto no es lo que estoy buscando. La búsqueda va a una base de datos SQLite y busca coincidencias de subcadenas. La lista muestra solo los elementos coincidentes de db.

  2. Existe cierta superposición, p. ambos controles manejarían normalmente VK_HOME y VK_END, pero eso está bien, en este caso las claves irían a la lista. Tendré que decidir si reenviaré cada pulsación de tecla individual o la manejaré en el control que la recibió.

En Editar: Una manera obvia parecía ser para invocar a los respectivos métodos KeyDown, KeyUp y KeyPress del control de edición, así:

type 
    THackEdit = class(TEdit); 

procedure TMainForm.cxTreeList1KeyDown(Sender: TObject; var Key: Word; 
    Shift: TShiftState); 
begin 
    THackEdit(edit1).KeyDown(Key, Shift); 
end; 

Desafortunadamente, esto no tiene ningún efecto . Supongo que TEdit no procesará eventos clave a menos que esté enfocado. El uso de SendMessage (THackEdit (edit1) .Handle, WM_KEYDOWN, Key, 0) tampoco tiene ningún efecto.

Respuesta

6

Puede utilizar la capacidad de manejo de mensajes de un control VCL y enviar los mensajes pertinentes entre sí. No sé acerca de un 'TcxTreeList', pero lo siguiente demuestra la idea en un control de edición y un control de notas que responden a los eventos del teclado de forma síncrona (siempre que sea posible, por supuesto).

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; 

Puede que tenga que intervenir mensajes adicionales, pero se entiende la idea.

Si por alguna razón u otra no puede derivar un nuevo control o anular el manejo de mensajes, puede considerar la subclasificación de los controles. La respuesta al this question le mostraría cómo hacerlo.

+0

Extraño, pero usar el método Perform de un TEdit todavía no tiene ningún efecto, al igual que el ejemplo SendMessage en mi OP. Incluso intentar algo simple como siempre enviar una tecla de retroceso (donde el control de edición tiene texto y la posición de intercalación es> 0) no hace nada: edit1.Perform (WM_KEYDOWN, 8, 0); –

+1

@mood - Pruebe 'edit1.Perform (WM_CHAR, VK_BACK, 0);' para el retroceso. Es por eso que se incluyeron los mensajes WM_CHAR, WM_KEYDOWN, WM_KEYUP en el proyecto de muestra. –

2

No es exactamente lo que está pidiendo, pero para obtener resultados similares, utilizo el siguiente truco.

Supongamos que tiene un TEdit Edit1 y un TListbox Listbox1.

En el caso OnEnter Listbox1, simplemente dió enfoque a Edit1

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

Y en el caso OnKeyDown de Edit1, utilice las flechas arriba y abajo para desplazarse por los elementos del cuadro de lista y utilice la tecla Intro para mueva el elemento seleccionado al cuadro de edición.

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; 
+0

Gracias, PA. He considerado este enfoque y sigue siendo una posibilidad. El inconveniente es que el TEdit se mantendrá enfocado en todo momento, mientras que creo que preferiría que la lista se centrara en su lugar. (Ditto hace algo más todavía: mantiene la lista enfocada, y en una pulsación de tecla alfanumérica le da enfoque al cuadro de edición * y * pasa esa clave a la edición. Así que usando este enfoque todavía necesitaría una forma de reenviar la tecla.) –

Cuestiones relacionadas