How to make a combo box with fulltext search autocomplete support?

I would like a user to be able to type in the second or third word from a TComboBoxitem and for that item to appear in the AutoSuggest dropdown options

For example, a combo box contains the items:

  • Mr John Brown
  • Mrs Amanda Brown
  • Mr Brian Jones
  • Mrs Samantha Smith

When the user types "Br" the dropdown displays:

  • Mr John Brown
  • Mrs Amanda Brown
  • Mr Brian Jones

and when the user types "Jo" the dropdown displays:

  • Mr John Brown
  • Mr Brian Jones

The problem is that the AutoSuggest functionality only includes items in the dropdown list that begin with what the user has input and so in the examples above nothing will appear in the dropdown.

Is it possible to use the IAutoComplete interface and/or other related interfaces to get around this issue?

The following example uses the interposed class of the TComboBox component. The main difference from the original class is that the items are stored in the separate StoredItems property instead of
the Items as usually (used because of simplicity).

The StoredItems are being watched by the OnChange event and whenever you change them (for instance by adding or deleting from this string list), the current filter will reflect it even when the combo
list is dropped down.

The main point here is to catch the WM_COMMAND message notification CBN_EDITUPDATE which is being sent whenever the combo edit text is changed but not rendered yet. When it arrives, you just search through the StoredItems list for what you have typed in your combo edit and fill the Items property with matches.

For text searching is used the ContainsText so the search is case insensitive. Forgot to mention,
the AutoComplete feature has to be turned off because it has its own, unwelcomed, logic for this purpose.

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, StrUtils, ExtCtrls;

type
  TComboBox = class(StdCtrls.TComboBox)
  private
    FStoredItems: TStringList;
    procedure FilterItems;
    procedure StoredItemsChange(Sender: TObject);
    procedure SetStoredItems(const Value: TStringList);
    procedure CNCommand(var AMessage: TWMCommand); message CN_COMMAND;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    property StoredItems: TStringList read FStoredItems write SetStoredItems;
  end;

type
  TForm1 = class(TForm)
    ComboBox1: TComboBox;
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

constructor TComboBox.Create(AOwner: TComponent);
begin
  inherited;
  AutoComplete := False;
  FStoredItems := TStringList.Create;
  FStoredItems.OnChange := StoredItemsChange;
end;

destructor TComboBox.Destroy;
begin
  FStoredItems.Free;
  inherited;
end;

procedure TComboBox.CNCommand(var AMessage: TWMCommand);
begin
  // we have to process everything from our ancestor
  inherited;
  // if we received the CBN_EDITUPDATE notification
  if AMessage.NotifyCode = CBN_EDITUPDATE then
    // fill the items with the matches
    FilterItems;
end;

procedure TComboBox.FilterItems;
var
  I: Integer;
  Selection: TSelection;
begin
  // store the current combo edit selection
  SendMessage(Handle, CB_GETEDITSEL, WPARAM(@Selection.StartPos),
    LPARAM(@Selection.EndPos));
  // begin with the items update
  Items.BeginUpdate;
  try
    // if the combo edit is not empty, then clear the items
    // and search through the FStoredItems
    if Text <> ‘‘ then
    begin
      // clear all items
      Items.Clear;
      // iterate through all of them
      for I := 0 to FStoredItems.Count - 1 do
        // check if the current one contains the text in edit
        if ContainsText(FStoredItems[I], Text) then
          // and if so, then add it to the items
          Items.Add(FStoredItems[I]);
    end
    // else the combo edit is empty
    else
      // so then we‘ll use all what we have in the FStoredItems
      Items.Assign(FStoredItems)
  finally
    // finish the items update
    Items.EndUpdate;
  end;
  // and restore the last combo edit selection
  SendMessage(Handle, CB_SETEDITSEL, 0, MakeLParam(Selection.StartPos,
    Selection.EndPos));
end;

procedure TComboBox.StoredItemsChange(Sender: TObject);
begin
  if Assigned(FStoredItems) then
    FilterItems;
end;

procedure TComboBox.SetStoredItems(const Value: TStringList);
begin
  if Assigned(FStoredItems) then
    FStoredItems.Assign(Value)
  else
    FStoredItems := Value;
end;

procedure TForm1.FormCreate(Sender: TObject);
var
  ComboBox: TComboBox;
begin
  // here‘s one combo created dynamically
  ComboBox := TComboBox.Create(Self);
  ComboBox.Parent := Self;
  ComboBox.Left := 10;
  ComboBox.Top := 10;
  ComboBox.Text := ‘Br‘;

  // here‘s how to fill the StoredItems
  ComboBox.StoredItems.BeginUpdate;
  try
    ComboBox.StoredItems.Add(‘Mr John Brown‘);
    ComboBox.StoredItems.Add(‘Mrs Amanda Brown‘);
    ComboBox.StoredItems.Add(‘Mr Brian Jones‘);
    ComboBox.StoredItems.Add(‘Mrs Samantha Smith‘);
  finally
    ComboBox.StoredItems.EndUpdate;
  end;

  // and here‘s how to assign the Items of the combo box from the form
  // to the StoredItems; note that if you‘ll use this, you have to do
  // it before you type something into the combo‘s edit, because typing
  // may filter the Items, so they would get modified
  ComboBox1.StoredItems.Assign(ComboBox1.Items);
end;    

end.

Thanks for the heart! With a little reworking, I think that is quite right.

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, StrUtils, ExtCtrls;

type
  TComboBox = class(StdCtrls.TComboBox)
  private
    FStoredItems: TStringList;
    procedure FilterItems;
    procedure StoredItemsChange(Sender: TObject);
    procedure SetStoredItems(const Value: TStringList);
    procedure CNCommand(var AMessage: TWMCommand); message CN_COMMAND;
  protected
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    property StoredItems: TStringList read FStoredItems write SetStoredItems;
  end;

type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
  private
  public
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

{}constructor TComboBox.Create(AOwner: TComponent);
    begin
      inherited;
      AutoComplete := False;
      FStoredItems := TStringList.Create;
      FStoredItems.OnChange := StoredItemsChange;
    end;

{}destructor TComboBox.Destroy;
    begin
      FStoredItems.Free;
      inherited;
    end;

{}procedure TComboBox.CNCommand(var AMessage: TWMCommand);
    begin
      // we have to process everything from our ancestor
      inherited;
      // if we received the CBN_EDITUPDATE notification
      if AMessage.NotifyCode = CBN_EDITUPDATE then begin
        // fill the items with the matches
        FilterItems;
      end;
    end;

{}procedure TComboBox.FilterItems;
    type
      TSelection = record
        StartPos, EndPos: Integer;
      end;
    var
      I: Integer;
      Selection: TSelection;
      xText: string;
    begin
      // store the current combo edit selection
      SendMessage(Handle, CB_GETEDITSEL, WPARAM(@Selection.StartPos), LPARAM(@Selection.EndPos));

      // begin with the items update
      Items.BeginUpdate;
      try
        // if the combo edit is not empty, then clear the items
        // and search through the FStoredItems
        if Text <> ‘‘ then begin
          // clear all items
          Items.Clear;
          // iterate through all of them
          for I := 0 to FStoredItems.Count - 1 do begin
            // check if the current one contains the text in edit
    //      if ContainsText(FStoredItems[I], Text) then
            if Pos( Text, FStoredItems[I])>0 then begin
              // and if so, then add it to the items
              Items.Add(FStoredItems[I]);
            end;
          end;
        end else begin
          // else the combo edit is empty
          // so then we‘ll use all what we have in the FStoredItems
          Items.Assign(FStoredItems)
        end;
      finally
        // finish the items update
        Items.EndUpdate;
      end;

      // and restore the last combo edit selection
      xText := Text;
      SendMessage(Handle, CB_SHOWDROPDOWN, Integer(True), 0);
      if (Items<>nil) and (Items.Count>0) then begin
        ItemIndex := 0;
      end else begin
        ItemIndex := -1;
      end;
      Text := xText;
      SendMessage(Handle, CB_SETEDITSEL, 0, MakeLParam(Selection.StartPos, Selection.EndPos));

    end;

{}procedure TComboBox.StoredItemsChange(Sender: TObject);
    begin
      if Assigned(FStoredItems) then
        FilterItems;
    end;

{}procedure TComboBox.SetStoredItems(const Value: TStringList);
    begin
      if Assigned(FStoredItems) then
        FStoredItems.Assign(Value)
      else
        FStoredItems := Value;
    end;

//=====================================================================

{}procedure TForm1.FormCreate(Sender: TObject);
    var
      ComboBox: TComboBox;
      xList:TStringList;
    begin

      // here‘s one combo created dynamically
      ComboBox := TComboBox.Create(Self);
      ComboBox.Parent := Self;
      ComboBox.Left := 8;
      ComboBox.Top := 8;
      ComboBox.Width := Width-16;
//    ComboBox.Style := csDropDownList;

      // here‘s how to fill the StoredItems
      ComboBox.StoredItems.BeginUpdate;
      try
        xList:=TStringList.Create;
        xList.LoadFromFile(‘list.txt‘);
        ComboBox.StoredItems.Assign( xList);
      finally
        ComboBox.StoredItems.EndUpdate;
      end;

      ComboBox.DropDownCount := 24;

      // and here‘s how to assign the Items of the combo box from the form
      // to the StoredItems; note that if you‘ll use this, you have to do
      // it before you type something into the combo‘s edit, because typing
      // may filter the Items, so they would get modified
      ComboBox.StoredItems.Assign(ComboBox.Items);
    end;

end.
时间: 2024-10-29 19:06:35

How to make a combo box with fulltext search autocomplete support?的相关文章

Win32程序中使用 Combo box控件

  SendMessage函数向窗口发送消息 LRESULT SendMessage( HWND hWnd,     // handle to destination window UINT Msg,      // message WPARAM wParam, // first message parameter LPARAM lParam   // second message parameter ); 1 向Combo Box添加数据 HWND hWndComboBox = GetDlgI

【转】VS2010/MFC编程入门之二十五(常用控件:组合框控件Combo Box)

原文网址:http://www.jizhuomi.com/software/189.html 上一节鸡啄米讲了列表框控件ListBox的使用,本节主要讲解组合框控件Combo Box.组合框同样相当常见,例如,在Windows系统的控制面板上设置语言或位置时,有很多选项,用来进行选择的控件就是组合框控件.它为我们的日常操作提供了很多方便. 组合框控件简介 组合框其实就是把一个编辑框和一个列表框组合到了一起,分为三种:简易(Simple)组合框.下拉式(Dropdown)组合框和下拉列表式(Dro

Missing styles. Is the correct theme chosen for this layout? Use the Theme combo box above the layou

android无法静态显示ui效果. Missing styles. Is the correct theme chosen for this layout? Use the Theme combo box above the layout to choose a different layout, or fix the theme style references. Failed to find style 'textViewStyle' in current theme 採用的解决方法例如以

Combo Box的简单使用(Win32)

1 SendMessage函数向窗口发送消息 LRESULT SendMessage( HWND hWnd,     // handle to destination window UINT Msg,      // message WPARAM wParam, // first message parameter LPARAM lParam   // second message parameter ); 2 向Combo Box添加数据 HWND hWndComboBox = GetDlgI

下拉列表框Combo Box

Combo Box/Combo Box Ex 组合窗口是由一个输入框和一个列表框组成.创建一个组合窗口可以使用成员函数: BOOL CListBox::Create( LPCTSTR lpszText, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID = 0xffff ); 其中dwStyle将指明该窗口的风格,除了子窗口常用的风格WS_CHILD,WS_VISIBLE外,你可以针对列表控件指明专门的风格. · CBS_DR

WPF Combo box 获取选择的Tag

string str1 = ((ComboBoxItem)this.cboBoxRate1553B.Items[this.cboBoxRate1553B.SelectedIndex]).Tag.ToString(); string str2 = (this.cboBoxRate1553B.SelectedItem as ComboBoxItem).Tag.ToString(); 不知道为什么只能先转换为var 或者string类型,再转化为其他类型. I have a combo box lik

Check Box、Radio Button、Combo Box控件使用

Check Box.Radio Button.Combo Box控件使用 使用控件的方法 1.拖动控件到对话框 2. 定义控件对应的变量(值变量或者控件变量) 3.响应控件各种消息 Check Box(复选框) 设定几个复选框,绑定变量分别是:m_bProgram(编程).m_bFriend(交友).m_bRead(阅读).m_bSwim(游泳) 默认选择 m_bProgram=TRUE; m_bFriend=TRUE; 选择判断项是否被选中 Cstring result; if(m_bFrie

MFC 获取Combo Box控件 当前选定项的序号和文本内容

代码如下: CString text; // 选定项的文本内容 Combobox m_combobox; // 控件变量 int cindex= m_combobox.GetCurSel(); // 获取选定项的序号 m_combobox.GetLBText(cindex,text); // 获取选定项的文本内容 其中,m_combobox为Combo Box控件变量,可由右键控件添加变量.序号cindex是从0开始的,也就是获取Combox Box控件的第一个项的序号为0. 原文地址:http

VC++ COMBO BOX控件的使用

1.你在编辑状态下点那个控件的向下的三角形,就出冒出来一个可以调高度的东东.将高度调高,否则在执行时会不能显示下拉选项. 2.为combo box添加选项,在编辑状态下选combo box控件的属性,选Data标签,在编辑框中添加选项,按Ctrl-Enter来添加下一个选项. 3.为combo box添加变量 combo box有两个变量,CComboBox类变量和CString变量. CComboBox变量用来设置combo box的属性,一般在cdialog类中的oninitdialog()