validación una texto sugerencias predictivo listas lista desplegables desplegable datos condicionadas con búsqueda autorrelleno automatica autocompletar autoajuste delphi autocomplete autosuggest

delphi - una - ¿Cómo hacer un cuadro combinado con el soporte de autocompletar de búsqueda de texto completo?



lista desplegable con sugerencias de búsqueda excel (4)

Me gustaría que un usuario pueda escribir la segunda o tercera palabra de un elemento TComboBox y que ese elemento aparezca en las opciones desplegables AutoSuggest

Por ejemplo, un cuadro combinado contiene los elementos:

  • Mr John Brown
  • Sra. Amanda Brown
  • Sr. Brian Jones
  • Sra. Samantha Smith

Cuando el usuario escribe "Br", aparece el menú desplegable:

  • Mr John Brown
  • Sra. Amanda Brown
  • Sr. Brian Jones

y cuando el usuario escribe "Jo", aparece el menú desplegable:

  • Mr John Brown
  • Sr. Brian Jones

El problema es que la funcionalidad AutoSuggest solo incluye elementos en la lista desplegable que comienzan con lo que el usuario ha ingresado y, por lo tanto, en los ejemplos anteriores, nada aparecerá en el menú desplegable.

¿Es posible usar la interfaz IAutoComplete y / u otras interfaces relacionadas para evitar este problema?


El siguiente ejemplo usa la clase interpuesta del componente TComboBox . La principal diferencia con respecto a la clase original es que los elementos se almacenan en la propiedad separada StoredItems lugar de
los Items como siempre (usados ​​debido a la simplicidad).

StoredItems está siendo observado por el evento OnChange y cada vez que los cambie (por ejemplo, agregando o eliminando de esta lista de cadenas), el filtro actual lo reflejará incluso cuando el combo
la lista se deja caer

El punto principal aquí es capturar la notificación de mensaje CBN_EDITUPDATE que se envía cada vez que se modifica el texto de edición del combo pero no se procesa todavía. Cuando llega, solo busca en la lista StoredItems lo que ha escrito en la edición combinada y completa la propiedad Items con coincidencias.

Para la búsqueda de texto se usa ContainsText para que la búsqueda no distinga entre mayúsculas y minúsculas. Olvide mencionar,
la función AutoComplete debe desactivarse porque tiene su propia lógica no bienvenida para este propósito.

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.


En la configuración del evento OnDropDown manipulado, los elementos TComboBox filtrados por:

desde una lista de cadenas completa externa. O mejor, escribe tu propio descendiente TComboBox (TCustomComboBox).


Este código era bastante bueno en realidad, solo solucioné el error al manejar los mensajes cuando se desplegaba el combo, algunas interacciones menores con el comportamiento de TComboBox y lo hacía un poco más amigable para el usuario. Para usarlo solo invoque InitSmartCombo después de completar la lista de elementos.

TSmartComboBox reemplaza a TComboBox, si invoca InitSmartCombo se comporta como un combo inteligente, de lo contrario, actúa como TComboBox estándar

unit SmartCombo; interface uses stdctrls,classes,messages,controls,windows,sysutils; type TSmartComboBox = class(TComboBox) // Usage: // Same as TComboBox, just invoke InitSmartCombo after Items list is filled with data. // After InitSmartCombo is invoked, StoredItems is assigned and combo starts to behave as a smart combo. // If InitSmartCombo is not invoked it acts as standard TComboBox, it is safe to bulk replace all TComboBox in application with TSmartComboBox private FStoredItems: TStringList; dofilter:boolean; storeditemindex:integer; procedure FilterItems; procedure StoredItemsChange(Sender: TObject); procedure SetStoredItems(const Value: TStringList); procedure CNCommand(var AMessage: TWMCommand); message CN_COMMAND; protected procedure KeyPress(var Key: Char); override; procedure CloseUp; override; procedure Click; override; public constructor Create(AOwner: TComponent); override; destructor Destroy; override; property StoredItems: TStringList read FStoredItems write SetStoredItems; procedure InitSmartCombo; end; implementation procedure TSmartComboBox.KeyPress(var Key: Char); // combo dropdown must be done in keypress, if its done on CBN_EDITUPDATE it messes up whole message processing mumbo-jumbo begin inherited; if dofilter and not (ord(key) in [13,27]) then begin if (items.Count<>0) and not droppeddown then SendMessage(Handle, CB_SHOWDROPDOWN, 1, 0) // something matched -> dropdown combo to display results end; end; procedure TSmartComboBox.CloseUp; // ugly workaround for some wierd combobox/modified code interactions var x:string; begin if dofilter then begin if (items.count=1) and (itemindex=0) then text:=items[itemindex] else if ((text<>'''') and (itemindex<>-1) and (text<>items[itemindex])) or ((text='''') and(itemindex=0)) then begin storeditemindex:=itemindex; x:=text; itemindex:=items.indexof(text); if itemindex=-1 then text:=x; end else storeditemindex:=-1; end; inherited; end; procedure TSmartComboBox.Click; // ugly workaround for some weird combobox/modified code interactions begin if dofilter then begin if storeditemindex<>-1 then itemindex:=storeditemindex; storeditemindex:=-1; end; inherited; end; procedure TSmartComboBox.InitSmartCombo; begin FStoredItems.OnChange:=nil; StoredItems.Assign(Items); AutoComplete := False; FStoredItems.OnChange := StoredItemsChange; dofilter:=true; storeditemindex:=-1; end; constructor TSmartComboBox.Create(AOwner: TComponent); begin inherited; FStoredItems := TStringList.Create; dofilter:=false; end; destructor TSmartComboBox.Destroy; begin FStoredItems.Free; inherited; end; procedure TSmartComboBox.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) and dofilter then begin // fill the items with the matches FilterItems; end; end; procedure TSmartComboBox.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 begin // check if the current one contains the text in edit, case insensitive if (Pos( uppercase(Text), uppercase(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 SendMessage(Handle, CB_SETEDITSEL, 0, MakeLParam(Selection.StartPos, Selection.EndPos)); end; procedure TSmartComboBox.StoredItemsChange(Sender: TObject); begin if Assigned(FStoredItems) then FilterItems; end; procedure TSmartComboBox.SetStoredItems(const Value: TStringList); begin if Assigned(FStoredItems) then FStoredItems.Assign(Value) else FStoredItems := Value; end; procedure Register; begin RegisterComponents(''Standard'', [TSmartComboBox]); end; end.


Gracias por el corazón! Con un poco de retrabajo, creo que es bastante correcto.

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.