In an older blog post I wrote about AutoComplete for TEdits using SHAutoComplete.
I just actually tried to use that function in one of my applications and found that there is a quite annoying problem with it: If you have set the OK button’s Default property to true (so it gets “clicked” when you press return), selecting an entry from the autocomplete list with the return key also closes the form, which is usually not what the user wants.
I turns out that I am not the first to stumble upon that problem.
The suggestion posted there by mghie is a bit ugly because it hooks the Application.OnMessage event which might conflict with other code that uses it.
I had another problem anyway (see below) so I extended a class that hooks a TEdit’s WindowProc method instead. Here is the code:
procedure TAutoCompleteActivator.NewWindowProc(var _Msg: TMessage); begin if (_Msg.Msg = CM_WANTSPECIALKEY) then begin if (_Msg.wParam = VK_RETURN) or (_Msg.wParam = VK_ESCAPE) then begin if IsAutoSuggestDropdownVisible then begin _Msg.Result := 1; Exit; //==> end; end; end; inherited NewWindowProc(_Msg); end;
The IsAutoSuggestDropdownVisible function is directly taken from mghie’s answer:
function EnumThreadWindowsProc(AWnd: HWnd; AParam: LParam): BOOL; stdcall; var WndClassName: string; FoundAndVisiblePtr: PInteger; begin SetLength(WndClassName, 1024); GetClassName(AWnd, PChar(WndClassName), Length(WndClassName)); WndClassName := PChar(WndClassName); if WndClassName = 'Auto-Suggest Dropdown' then begin // do not translate FoundAndVisiblePtr := PInteger(AParam); FoundAndVisiblePtr^ := Ord(IsWindowVisible(AWnd)); Result := False; end else Result := True; end; function IsAutoSuggestDropdownVisible: Boolean; var FoundAndVisible: Integer; begin FoundAndVisible := 0; EnumThreadWindows(GetCurrentThreadId, @EnumThreadWindowsProc, LParam(@FoundAndVisible)); Result := FoundAndVisible > 0; end;
This works fine in my program compiled with Delphi 2010 and running on Windows 8.1 (your mileage may vary).
Now to the other problem mentioned above:
In the old blog post I published a TEdit_SetAutocomplete function that activates autocomplete for a TEdit control. This function works fine as as long as you don’t try to call it in the form’s constructor. If you do, it does nothing. The reason is that the TEdit’s handle gets destroyed and recreated after the form’s constructor was called, which results in autocomplete being turned off again. One option would have been to put the function call into the form’s OnShow handler, but I am no fan of distributing code that in my opinion belongs into the constructor to these event handlers, so I wanted a different solution.
It turned out that I already had one in my dzlib.u_dzVclUtils unit: TWinControl_ActivateDropFiles returns a TObject that hooks the TWinControl’s WindowProc and handles the WM_NCCREATE and WM_NCDESTROY messages. I refactored that class a bit to create a generic TWindowProcHook ancestor and derived TAutoCompleteActivator from it. Its WmNcCreate method now looks like this:
procedure TAutoCompleteActivator.WmNcCreate; begin inherited; SetAutoComplete; end; procedure TAutoCompleteActivator.SetAutoComplete; begin TEdit_SetAutocomplete(FCtrl as TCustomEdit, FSource, FType); end;
So every time the window handle gets created anew, it activates autocomplete for it again.