Watch, Follow, &
Connect with Us

Please visit our new home
community.embarcadero.com.


Welcome, Guest
Guest Settings
Help

Thread: TComboBoxEx acoSearch AutoCompleteOption Implementation


This question is answered.


Permlink Replies: 4 - Last Post: Sep 9, 2015 10:32 AM Last Post By: Remy Lebeau (Te...
Wilbert Chua

Posts: 3
Registered: 1/14/09
TComboBoxEx acoSearch AutoCompleteOption Implementation  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Sep 8, 2015 6:06 PM
In Delphi documentation, the TComboBoxEx defines acoSearch AutoCompleteOption as "Add a search item to the list of completed strings. Selecting this item launches a search engine." Anybody knows how this works?

With this property turned on and Style to csExDropDown, when a user types a string in the combobox,the list will contain "Search for x", if the user selects this item, what search engine is launched? Is it possible to launch my own search dialog box?

Any example or links on how to implement this would be appreciated.

I'm using D7.

Thanks.
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: TComboBoxEx acoSearch AutoCompleteOption Implementation
Correct
Click to report abuse...   Click to reply to this thread Reply
  Posted: Sep 8, 2015 9:55 PM   in response to: Wilbert Chua in response to: Wilbert Chua
Wilbert wrote:

In Delphi documentation, the TComboBoxEx defines acoSearch
AutoCompleteOption as +"Add a search item to the list of completed
strings. Selecting this item launches a search engine."+ Anybody
knows how this works?

TComboBoxEx implements a standard Win32 COMBOBOXEX control and attaches the
IAutoComplete and IAutoComplete2 interfaces to its edit field.

ComboBoxEx Control Reference
https://msdn.microsoft.com/en-us/library/windows/desktop/bb775740.aspx

IAutoComplete interface
https://msdn.microsoft.com/en-us/library/windows/desktop/bb776292.aspx

IAutoComplete2 interface
https://msdn.microsoft.com/en-us/library/windows/desktop/bb776288.aspx

Using Autocomplete
https://msdn.microsoft.com/en-us/library/windows/desktop/bb776884.aspx

Despite what the documentation says, AutoComplete does not automatically
launch a search engine. You have to launch it yourself. However, the documentation
does not say how to detect when the "Search for ..." item is selected, and
there is no event or message or callback that will give you that information.

Fortunately, when the "Search for ..." item is selected, the TComboBoxEx.OnChange
event is triggered, and the ComboBox's text will be set to '? <search text>'.
You can handle that event, eg:

uses
  StrUtils;
 
procedure TForm1.ComboBoxEx1Change(Sender: TObject);
var
  SearchText: string;
begin
  SearchText := ComboBoxEx1.Text;
  if StartsText('? ', SearchText) then
  begin
    Delete(SearchText, 1, 2);
    // use SearchText as needed...
  end;


However, the OnChange event is also triggered when the user types into the
edit field. So there is nothing stopping the user from typing '? ...' and
triggering the above code. You would have to differentiate between those
two actions.

One way I found to do that is to query the AutoComplete's drop-down list
and see if it is still visible when the OnChange event is triggered after
selecting the "Search for ..." item, eg:

uses
  Winapi.ActiveX, Winapi.ShlObj, Winapi.Commctrl;
 
type
  TComboBoxExAccess = class(TCustomCombo)
  private
    FAutoCompleteIntf: IAutoComplete;
  end;
 
procedure TForm57.ComboBoxEx1Change(Sender: TObject);
var
  DropDownList: IAutoCompleteDropDown;
  Flags: DWORD;
  pText: PWideChar;
  SearchText: string;
begin
  if TComboBoxExAccess(ComboBoxEx1).FAutoCompleteIntf = nil then Exit;
 
  DropDownList := TComboBoxExAccess(ComboBoxEx1).FAutoCompleteIntf as IAutoCompleteDropDown;
  if DropDownList.GetDropDownStatus(Flags, pText) <> S_OK then Exit;
  if pText = nil then Exit;
 
  try
    if Flags <> ACDD_VISIBLE then Exit;
    if StrLComp(pText, '? ', 2) <> 0 then Exit;
    SearchText := pText + 2;
  finally
    CoTaskMemFree(pText);
  end;
 
  // use SearchText as needed...
end;


A gotcha I found with this approach is that if the user starts typing and
then uses the keyboard to select the "Search for ..." item, the OnChange
event is still triggered as the user scrolls through the items, and the drop-down
list is still visible as the user has not actually choosen an item yet.
So the above code is still triggered. The only solution I can come up with
for that is to use a timer to wait until the AutoComplete's drop-down list
closes, and then act on the latest text that has been assigned to the edit
field, eg:

procedure TForm1.ComboBoxEx1Change(Sender: TObject);
var
  DropDownList: IAutoCompleteDropDown;
  Flags: DWORD;
  pText: PWideChar;
  SearchText: string;
begin
  Timer1.Enabled := False;
 
  if TComboBoxExAccess(ComboBoxEx1).FAutoCompleteIntf = nil then Exit;
 
  DropDownList := TComboBoxExAccess(ComboBoxEx1).FAutoCompleteIntf as IAutoCompleteDropDown;
  if DropDownList.GetDropDownStatus(Flags, pText) <> S_OK then Exit;
  if pText = nil then Exit;
 
  try
    Timer1.Enabled := (StrLComp(pText, '? ', 2) = 0);
  finally
    CoTaskMemFree(pText);
  end;
end;
 
procedure TForm1.Timer1Timer(Sender: TObject);
var
  DropDownList: IAutoCompleteDropDown;
  Flags: DWORD;
  pText: PWideChar;
  SearchText: string;
begin
  if TComboBoxExAccess(ComboBoxEx1).FAutoCompleteIntf <> nil then
  begin
    DropDownList := TComboBoxExAccess(ComboBoxEx1).FAutoCompleteIntf as IAutoCompleteDropDown;
    if DropDownList.GetDropDownStatus(Flags, pText) = S_OK then
    begin
      if pText <> nil then
      begin
        CoTaskMemFree(pText);
        Exit;
      end;
    end;
  end;
 
  Timer1.Enabled := False;
 
  SearchText := ComboBoxEx1.Text;
  if not StartsText('? ', SearchText) then Exit;
 
  Delete(SearchText, 1, 2);
  // use SearchText as needed...
end;


However, that does not address the issue of if the user presses ESC to cancel
the drop-down, especialy while there is still previous '? ...' text in the
edit field. I don't really have a solution for that one.

With this property turned on and Style to csExDropDown, when a user
types a string in the combobox,the list will contain "Search for x",
if the user selects this item, what search engine is launched?

Whichever one you want.

Is it possible to launch my own search dialog box?

Yes.

--
Remy Lebeau (TeamB)
Wilbert Chua

Posts: 3
Registered: 1/14/09
Re: TComboBoxEx acoSearch AutoCompleteOption Implementation  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Sep 8, 2015 11:17 PM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Remy Lebeau (TeamB) wrote:

Hi Remy,

Thank you very much for this thorough explanation and example.
Wilbert Chua

Posts: 3
Registered: 1/14/09
Re: TComboBoxEx acoSearch AutoCompleteOption Implementation  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Sep 9, 2015 12:32 AM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Hi Remy,

By the way, I'm using Delphi 2007 and I can't seem to find the unit for the IAutoCompleteDropDown interface.

Thanks

Remy Lebeau (TeamB) wrote:

uses
  Winapi.ActiveX, Winapi.ShlObj, Winapi.Commctrl;
 
type
  TComboBoxExAccess = class(TCustomCombo)
  private
    FAutoCompleteIntf: IAutoComplete;
  end;
 
procedure TForm57.ComboBoxEx1Change(Sender: TObject);
var
  DropDownList: IAutoCompleteDropDown;
  Flags: DWORD;
  pText: PWideChar;
  SearchText: string;
begin
  if TComboBoxExAccess(ComboBoxEx1).FAutoCompleteIntf = nil then Exit;
 
  DropDownList := TComboBoxExAccess(ComboBoxEx1).FAutoCompleteIntf as IAutoCompleteDropDown;
  if DropDownList.GetDropDownStatus(Flags, pText) <> S_OK then Exit;
  if pText = nil then Exit;
 
  try
    if Flags <> ACDD_VISIBLE then Exit;
    if StrLComp(pText, '? ', 2) <> 0 then Exit;
    SearchText := pText + 2;
  finally
    CoTaskMemFree(pText);
  end;
 
  // use SearchText as needed...
end;

Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: TComboBoxEx acoSearch AutoCompleteOption Implementation  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Sep 9, 2015 10:32 AM   in response to: Wilbert Chua in response to: Wilbert Chua
Wilbert wrote:

By the way, I'm using Delphi 2007 and I can't seem to find
the unit for the IAutoCompleteDropDown interface.

It was added to the ShlObj unit in D2010. In earlier versions, you will
have to define it manually in your code:

const
  SID_IAutoCompleteDropDown = '{3CD141F4-3C6A-11D2-BCAA-00C04FD929DB}';
  IID_IAutoCompleteDropDown: TGUID = SID_IAutoCompleteDropDown;
 
const
  ACDD_VISIBLE = $0001; 
 
type
  IAutoCompleteDropDown = interface(IUnknown) 
    [SID_IAutoCompleteDropDown]
    function GetDropDownStatus(var pdwFlags: DWORD; var ppwszString: LPWSTR): 
HRESULT; stdcall;
    function ResetEnumerator: HRESULT; stdcall;
  end;


--
Remy Lebeau (TeamB)
Legend
Helpful Answer (5 pts)
Correct Answer (10 pts)

Server Response from: ETNAJIVE02