每个人可能都知道我的意思,但要澄清控制需要:
那么,是否有一个像这样工作的 Delphi 编辑/组合控件?
使用所有 Windows 编辑控件中内置的 自动完成功能。
首先,根据需要填充
TStrings
对象。然后使用 GetOleStrings
创建一个 TStringsAdapter
将其包裹起来。 (适配器不会声明 TStrings
对象的所有权,因此您必须确保在适配器仍处于活动状态时不会销毁它。)适配器为您提供了 IStrings
接口,您需要该接口,因为自动完成功能需要一个 IEnumString
接口来提供完成匹配。请致电 _NewEnum
询问。
CoCreateInstance
创建一个 IAutoComplete
对象。调用其 Init
方法将其与编辑控件的窗口句柄关联起来。如果您使用的是组合框,请向其发送 cbem_GetEditControl
消息以查找底层编辑窗口。
您可以在此时停止,自动完成功能应该会自动运行。如果需要,您可以禁用自动完成功能,也可以设置任意数量的自动完成选项。
你说你不想要自动补全,但在操作系统术语中,我认为你真正不想要的是所谓的“自动追加”,其中字符串的其余部分会在用户键入时自动输入到编辑框中,但选择后,进一步输入将覆盖它,并且如果所需的值短于其中一个匹配项,则用户需要删除多余的文本。 还有
自动建议,它显示建议的下拉列表。 您可以启用其中一个或两个选项。您不需要自己过滤建议列表;自动完成对象会自行过滤
IEnumString
列表。
TComboBox
和 faststrings 库(用于
stringMatches()
功能)。procedure TForm1.cbChange(Sender: TObject);
var
s:Integer;
tmpstr:string;
begin
//suggestions: tstringlist
cb.AutoComplete:=false;
tmpstr:=cb.Text;
cb.Items.Clear;
for s:=0 to suggestions.Count - 1 do
if StringMatches(suggestions[s],cb.Text+'*') then
cb.Items.Add(suggestions[s]);
cb.DroppedDown:=(cb.Items.Count<>0) and (Length(cb.Text)<>0);
cb.Text:=tmpstr;
cb.SelStart:=Length(cb.Text)
end;
SHAutoComplete(GetWindow(eb_MyComboBox->Handle, GW_CHILD), SHACF_AUTOSUGGEST_FORCE_ON | SHACF_FILESYS_DIRS);
TComboBoxEx
具有属性
AutoCompleteOptions
,我将acoAutoSuggest
设置为True
,将acoAutoAppend
设置为False
。现在,组合框在执行某些输入时会过滤其项目列表并显示匹配的项目。我正在使用 RAD Studio 10 Seattle 和 XE2,但不知道此功能在旧版本中是否可用。
UNIT cvDropDownSearch;
{-------------------------------------------------------------------------------------------------------------
GabrielM
2023.10
Searchbox with auto-suggest
A dropdown box similar to the Help Insight in Delphi IDE.
Displays a list of items. The list is filtered (gets smaller) as the user types in more characters into the searchbox.
-------------------------------------------------------------------------------------------------------------}
//ToDo: Issue: the drop down does not respond to scroll
//ToDo: Issue: the drop down needs two clicks instead of one in order to be closed
//ToDo: When the drop down is focused, let user close the drop down with Enter
INTERFACE
USES
System.Classes, Vcl.Controls, System.Types, Vcl.WinXCtrls,
cvListBox;
TYPE
TSelectNotifyEvent = procedure (Sender: TObject; SelectedItem: TObject) of object;
TDropDownSearchBox= class(TSearchBox)
private
FOnEndSearch: TSelectNotifyEvent;
procedure showDropDown;
procedure endSearch(Sender: TObject);
protected
procedure KeyPress(var Key: Char); override;
procedure Click; override;
procedure Change; override;
protected
lbxSearch: TCubicListBox;
public
constructor Create(AOwner: TComponent); override;
procedure CreateWindowHandle(const Params: TCreateParams); override;
procedure SetParent(AParent: TWinControl); override; { SetParent is called during construction AND also during deconstruction with aParent=nil }
destructor Destroy; override;
procedure Populate(Objects: TStringList);
procedure SetHostParent(aParent: TWinControl);
function SelectedObject: TObject;
published
property OnEndSearch: TSelectNotifyEvent read FOnEndSearch write FOnEndSearch; { Triggered when the user selected an item from the list }
end;
procedure Register;
IMPLEMENTATION
USES
ccCore;
constructor TDropDownSearchBox.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
AlignWithMargins := True;
TextHint := 'Search...';
OnInvokeSearch := EndSearch;
Text := '';
lbxSearch := TCubicListBox.Create(Self);
lbxSearch.Visible := FALSE;
lbxSearch.OnClick := EndSearch;
lbxSearch.Parent := Self;
end;
procedure TDropDownSearchBox.CreateWindowHandle(const Params: TCreateParams);
begin
inherited;
lbxSearch.Name := 'lbxSearch';
lbxSearch.Width := Width;
lbxSearch.Height := 81;
lbxSearch.MultiSelect := False;
lbxSearch.ItemHeight := 13;
lbxSearch.Sorted := TRUE;
lbxSearch.Visible := FALSE;
Text:= '';
end;
procedure TDropDownSearchBox.SetParent(AParent: TWinControl);
begin
inherited;
end;
destructor TDropDownSearchBox.Destroy;
begin
///FreeAndNil(lbxSearch); Destroyed by the Parent
inherited;
end;
procedure TDropDownSearchBox.Change;
begin
inherited;
showDropDown;
end;
procedure TDropDownSearchBox.Click;
begin
inherited;
showDropDown;
end;
procedure TDropDownSearchBox.showDropDown;
begin
lbxSearch.Visible:= TRUE;
end;
procedure TDropDownSearchBox.SetHostParent(aParent: TWinControl);
begin
VAR Pos:= ClientToParent(Point(0, 0), aParent);
lbxSearch.Parent := aParent;
lbxSearch.Top := Pos.Y+ Self.Height+ 2;
lbxSearch.Left := Pos.X;
lbxSearch.Width := Width;
end;
// Test what happens if the text is not found so the user selects nothing
procedure TDropDownSearchBox.EndSearch(Sender: TObject);
begin
lbxSearch.Visible:= FALSE; // This must be at the end because of edtSearchReagent.Text:= ActionBl.Reagent.AsText
if lbxSearch.SelectedObject = NIL then EXIT; // When it happens?
Self.Text:= lbxSearch.SelectedItem;
if Assigned(FOnEndSearch) then FOnEndSearch(Self, lbxSearch.SelectedObject);
end;
// Cancel search on Escape
procedure TDropDownSearchBox.KeyPress(var Key: Char);
begin
inherited KeyPress(Key);
if key = ESC
then lbxSearch.Visible:= FALSE;; //ToDo: Allow the user move the cursor down to select from the list.
end;
procedure TDropDownSearchBox.Populate(Objects: TStringList);
begin
lbxSearch.Clear;
for VAR i:= 0 to Objects.Count-1 DO
if (Text = '') OR (PosInsensitive(Text, Objects[i]) > 0)
then lbxSearch.Items.AddObject(Objects[i], Objects.Objects[i]);
lbxSearch.SelectFirstItem;
lbxSearch.SetHeightAuto(300, Parent); //Resize it based on the number of rows in it, but never make it bigger than the 1/2 form
end;
function TDropDownSearchBox.SelectedObject: TObject;
begin
Result:= lbxSearch.SelectedObject;
end;
procedure Register;
begin
RegisterComponents('LightSaber', [TDropDownSearchBox]);
end;
end.
我将在 GitHub 上发布更新。