在Delphi XE中,我正在尝试实现一个“即时搜索”功能 - 一个类似于Firefox的“在键入时搜索”的功能,但在开源剪贴板扩展器中的类似功能更好地说明了{{3 }}:
有一个处理典型导航事件的项目列表。但是,任何字母数字键以及导航和编辑命令(向右/向左箭头,移位+箭头,退格键,删除等)都应该重新路由到列表下方的编辑框。编辑框的OnChange事件将触发刷新列表。
UI的要点是用户不必在控件之间使用制表符或shift键。两个控件(列表和编辑框)应该“感觉”好像它们只是一个控件。搜索UI的行为应该不取决于哪个控件具有焦点。
似乎我最好的选择是将某些键盘事件从列表控件(我正在使用Ditto)转发到编辑框,然后转发一些导航键。编辑框到列表。我怎样才能做到这一点?
注意:
TcxTreeList当然支持增量搜索,但这不是我追求的。搜索转到SQLite数据库并查找子字符串匹配。该列表仅显示db中的匹配项。
有一些重叠,例如两个控件通常都会处理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)也没有效果。
答案 0 :(得分:6)
您可以使用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会告诉你如何做到这一点。
答案 1 :(得分:2)
不完全是你要求的,但是对于类似的结果,我使用以下技巧。
假设您有一个TEdit Edit1
和一个TListbox Listbox1
。
在Listbox1的OnEnter事件中,只需将焦点放到Edit1
procedure TForm1.ListBox1Enter(Sender: TObject);
begin
edit1.SetFocus;
end;
在Edit1的OnKeyDown事件中,使用向上和向下箭头导航列表框的项目,并使用回车键将所选项目移动到编辑框。
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;