2011-01-18 11 views
5

W Delphi XE, próbuję zaimplementować funkcję "natychmiastowego wyszukiwania" - taką, która przypomina nieco "wyszukiwanie w trakcie pisania" przez Firefoksa, ale jest lepiej ilustrowana podobną open source schowek Extender Ditto:Przekazywanie zdarzeń klawiatury z jednego kontrolki Windows do innego

Ditto search interface

jest to lista elementów, który obsługuje typowe zdarzenia nawigacji. Jednak dowolne klawisze alfanumeryczne, a także polecenia nawigacji i edycji (strzałki w prawo/lewo, shift + strzałki, backspace, usuwanie itp.) Powinny zostać przekierowane do pola edycji pod listą. Zdarzenie OnChange w polu edycyjnym spowoduje odświeżenie listy.

Punkt interfejsu użytkownika polega na tym, że użytkownik nie musi przechodzić między kartami ani tabulatorem zmian między elementami sterującymi. Te dwie kontrolki (lista i pole edycji) powinny "czuć" się tak, jakby były pojedynczą formantem.Otrzymanie interfejsu wyszukiwania powinno być zależne od tego, która kontrola ma fokus.

Wydaje mi się, że jest to najlepsza opcja jest naprzód pewnych zdarzeń klawiaturowych z kontrolą listy (używam TcxTreeList) do pola edycji i przekazać garść klawiszy nawigacyjnych z pola edycji do listy. w jaki sposób można to osiągnąć?

Notatki:

  1. TcxTreeList obsługuje oczywiście wyszukiwanie przyrostowe, ale nie o to mi chodzi. Wyszukiwanie przechodzi do bazy danych SQLite i wyszukuje dopasowania podłańcuchowe. Lista wyświetla tylko pasujące elementy z bazy danych.

  2. Występuje pewne pokrywanie się, np. obie kontrolki normalnie obsłużyłyby VK_HOME i VK_END, ale to jest w porządku - w tym przypadku klucze trafiłyby na listę. Muszę zdecydować, czy przekazać każde pojedyncze naciśnięcie klawisza, czy też obsłużyć je w sterowaniu, które je otrzymało.

Na EDIT: Jednym z oczywistych sposobów wydawało się powołać odpowiednie metody KeyDown, keyUp i naciśnięciu klawisza z kontrolą edycji, tak jak poniżej:

type 
    THackEdit = class(TEdit); 

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

Niestety, nie ma to wpływu . Zgaduję, że TEdit nie przetworzy kluczowych zdarzeń, chyba że jest skoncentrowany. Używanie SendMessage (THackEdit (edit1) .Handle, WM_KEYDOWN, Key, 0) również nie ma żadnego efektu.

Odpowiedz

6

Można używać funkcji obsługi komunikatów kontrolki VCL i wysyłać odpowiednie wiadomości do siebie nawzajem. Nie wiem o "TcxTreeList", ale poniższe pokazuje ideę sterowania edycją i kontrolki memo reagujące synchronicznie na zdarzenia na klawiaturze (gdzie tylko to możliwe oczywiście).

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; 

Być może będziesz musiał interweniować w dodatkowych wiadomościach, ale wpadniesz na pomysł.

Jeśli z tego czy innego powodu nie możesz wyprowadzić nowego sterowania lub przesłonić obsługi komunikatów, możesz rozważyć podklasowanie kontrolek. Odpowiedź na this question pokazuje, jak to zrobić.

+0

Dziwne, ale użycie metody Perform modułu TEdit nadal nie ma żadnego efektu - tak jak przykład SendMessage w moim OP. Nawet próba czegoś prostego, jak zawsze wysyłanie klawisza backspace (gdzie kontrola edycji ma tekst i pozycja karetki jest> 0) nic nie robi: edit1.Perform (WM_KEYDOWN, 8, 0); –

+1

@mood - Wypróbuj 'edit1.Perform (WM_CHAR, VK_BACK, 0);' dla backspace. Dlatego dołączono komunikaty WM_CHAR, WM_KEYDOWN, WM_KEYUP do przykładowego projektu. –

2

Nie dokładnie to, o co prosisz, ale dla podobnych wyników używam następującej sztuczki.

Załóżmy, że masz jeden TEdit Edit1 i jeden TListbox Listbox1.

W przypadku OnEnter z Listbox1, po prostu uzyskując skupienie się Edit1

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

A w onKeyDown przypadku Edit1, używać w górę iw dół strzałek do poruszania się w pozycji pola listy i użyć klawisza enter przenieś wybrany element do pola edycji.

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

Dzięki, PA. Rozważyłem to podejście i pozostaje to możliwe. Minusem jest to, że TEdit pozostanie skupiony przez cały czas, podczas gdy ja myślę, że wolałbym zamiast tego skupić się na liście. (Ditto robi jeszcze coś: ustawia listę na skoncentrowaną, a na klawiaturze alfanumerycznej aktywuje pole edycyjne * i * przekazuje klucz do edycji, więc stosując to podejście, nadal potrzebuję sposobu na przekazanie naciśnięcie klawisza.) –

Powiązane problemy