Ir al contenido


Foto

Autocompletar en TEdit


  • Por favor identifícate para responder
8 respuestas en este tema

#1 santiago14

santiago14

    Advanced Member

  • Miembros
  • PipPipPip
  • 334 mensajes
  • LocationCerrillos - Salta - Argentina

Escrito 09 septiembre 2013 - 04:05

Buenas, estoy buscando la forma de hacer el autocompletado de un TEdit.
Lo cuento en detalle.
Ya he logrado usar la librería de Windows "Shlwapi.dll" pero a medias.

Cuando estoy en la caja de texto y presiono algunas teclas me hace el autocompletado, pero de cosas que ya hay guardadas en algún lugar del S.O. Esto está bien y es una parte de lo que quiero hacer. Vale recordar que se va llenando con textos que se le cargan, digamos, desde el IE.

Lo que quiero ahora es que también "memorice" lo que yo le pongo en mi caja de texto para que en una próxima intervención también sea parte de la lista.

Un poco de código:



delphi
  1. //...
  2. function SHAutoComplete(hwndEdit: HWnd; dwFlags: DWORD): HResult; stdcall; external 'Shlwapi.dll';
  3.  
  4. //...
  5.  
  6. const
  7.   SHACF_AUTOSUGGEST_FORCE_ON = $10000000;
  8.   SHACF_AUTOSUGGEST_FORCE_OFF = $20000000;
  9.   SHACF_AUTOAPPEND_FORCE_ON = $40000000;
  10.   SHACF_AUTOAPPEND_FORCE_OFF = $80000000;
  11.   SHACF_DEFAULT = $0;
  12.   SHACF_FILESYSTEM = $1;
  13.   SHACF_URLHISTORY = $2;
  14.   SHACF_URLMRU = $4;
  15.  
  16. //...
  17.  
  18. procedure TfrmPrincipal.FormCreate(Sender: TObject);
  19. var
  20.   Options: dWord;
  21. begin
  22. //...
  23.  
  24.   Options := SHACF_FILESYSTEM or SHACF_URLHISTORY or SHACF_URLMRU or
  25.     SHACF_AUTOSUGGEST_FORCE_ON or SHACF_AUTOAPPEND_FORCE_ON;
  26.   SHAutoComplete(txtAuto.Handle, Options);
  27.  
  28. end;
  29.  
  30. //...



Mi caja de texto "txtAuto" funciona como lo he mencionado, mientras escribo me va haciendo sugerencias de lo que tiene guardado en algún lugar.
Si pongo ms ya me sugiere msconfig, este comportamiento es conocido.
Ahora pongo "santiago", esta palabra no está registrada aún por lo cual no me hace sugerencia. Lo que quiero es registrarla para que la próxima vez ya me la ponga como parte de las sugerencias. O sea, pongo "sa" y ya tenga "santiago" en la lista que aparece.

Espero haber sido claro.

Gracias.
  • 0

#2 Wilson

Wilson

    Advanced Member

  • Moderadores
  • PipPipPip
  • 2.137 mensajes

Escrito 09 septiembre 2013 - 08:26

Yo hago algo parecido al Code Insight de Delphi sobre un TRichEdit, a medida que voy escribiendo me va mostrando las opciones filtradas, nada impide hacerlo sobre un TEdit.  Solo que yo no utilizo la dll que mencionas, en cambio tengo mi propia implementación, que es muy sencilla. Una tabla en una base de datos para almacenar las frases o palabras, en este punto  podrías usar tan solo un archivo de texto si no quieres complicarte con una DB; luego basta interceptar el evento ONKeyPress del control para obtener las letras digitadas y la posición del cursor, posteriormente lanzar una consulta contra la  DB o contra las líneas del archivo, pasando como parámetro el texto digitado, luego mostrar el resultado de la consulta sobre un form (stayOnTop) justo en la posición del control. Para guardar las frases o palabras que no estén en la DB, basta una rutina sencilla que al salir del control haga las comparaciones del caso y grabe si es necesario.

Saludos
  • 0

#3 santiago14

santiago14

    Advanced Member

  • Miembros
  • PipPipPip
  • 334 mensajes
  • LocationCerrillos - Salta - Argentina

Escrito 10 septiembre 2013 - 05:44

Me han propuesto lo siguiente, ¿qué les parece?
Gracias ecfisa.


delphi
  1. ...
  2. const
  3.   HISTORIAL = 'C:\CARPETA\HISTORIAL.TXT';
  4.  
  5. procedure TForm1.FormCreate(Sender: TObject);
  6. begin
  7.   with ComboBox1 do
  8.   begin
  9.     Clear;
  10.     if FileExists(HISTORIAL) then
  11.       Items.LoadFromFile(HISTORIAL);
  12.     Sorted := True;
  13.     Style  := csDropDown;
  14.   end;
  15. end;
  16.  
  17. procedure TForm1.ComboBox1Exit(Sender: TObject);
  18. begin
  19.   with ComboBox1.Items do
  20.   begin
  21.     Add(ComboBox1.Text);
  22.     SaveToFile(HISTORIAL);
  23.   end;
  24. end;
  25. ...


Haciendo unos arreglitos me parece que va bien. Habría que pensar que para los lugares donde quiero autocompletado, que no es en todos lados, en lugar de poner un TEdit poner un Combo. No es tan difícil.

Santiago.
  • 0

#4 Wilson

Wilson

    Advanced Member

  • Moderadores
  • PipPipPip
  • 2.137 mensajes

Escrito 10 septiembre 2013 - 07:40

Es una muy buena opción, como dices: "Haciéndole unos arreglitos" .  En mi caso no puedo usar un combobox porque la tabla de frases y palabras supera los 200000 registros, luego no me resulta práctico cargar un combobox con ese número de items, por eso tengo que filtrar con base en lo que el usuario va escribiendo y mostrar las coincidencias en un TlistBox alineado al cliente en otro form, para que el usuario pueda escoger la opción conveniente, tal cual el Code Insight de Delphi.

Saludos.
  • 0

#5 cram

cram

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 832 mensajes
  • LocationMisiones, Argentina

Escrito 11 septiembre 2013 - 02:55

No se muy bien a que uso se destina ese procedimiento, pero se me ocurre como práctico lo siguiente

Si te encuentras creando un componente. Te recomiento que heches un vistazo a los procedimientos SetSelStart y SetSelLenght.
Así luego de capturar la primer coincidencia puedes agregar el resto del texo (autocompletar) directamente en el cuadro de edición seleccionándolo automáticamente, así si el usuario desea sobreescribir, eliminará la sugerencia. Al agregar un caracter más la coincidencia puede cambiar, etc.

Funcionaría capturando el evento OnKeyDown

Por ejemplo: Si existen en el "búfer" las palabras: samaniego y santiago (estarán en ese orden),
la primer coincidencia será samaniego
sa[maniego]

el cursor se sitúa entre la a y la m los corchetes indican selección.
al incluir la n la primer coincidencia será santiago:
san[tiago]

Cuando no haya coincidencia, no habrá texto seleccionado.
If KeyChar = 13 then LimpiarSelección


  • 0

#6 santiago14

santiago14

    Advanced Member

  • Miembros
  • PipPipPip
  • 334 mensajes
  • LocationCerrillos - Salta - Argentina

Escrito 17 marzo 2014 - 08:26

Este código me lo pasó nlsGarcía, al cual agradezco un montón.
Sirve para que un combo tenga autocompletado, ahora bien, él lo puso en un form, yo quiero ponerlo en una unit como una clase. Resulta que al hacerlo y compilar me dice:

[error] File not found: oAutocompletarCombo.dfm

Sin duda algo no está bien, mi clase está mas abajo ¿una ayuda por ahí?

Gracias.



delphi
  1. unit Unit1;
  2.  
  3. interface
  4.  
  5. uses
  6.   Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  7.   Dialogs, StdCtrls, StrUtils, ExtCtrls;
  8.  
  9. type
  10.   TComboBox = class(StdCtrls.TComboBox)
  11.   private
  12.     CtrlLoad : Boolean;
  13.     UnFlicker : Boolean;
  14.     UpDown : Boolean;
  15.     AuxText : String;
  16.     HistoryName : String;
  17.     StoredItems : TStringList;
  18.     CountItems : Byte;
  19.     procedure FilterItems;
  20.     procedure StoredItemsChange(Sender: TObject);
  21.     procedure LoadHistory(FileHistory : String = ''; CountHistory : Byte = 10);
  22.     procedure ComboExit(Sender: TObject);
  23.     procedure ComboCloseUp(Sender: TObject);
  24.     procedure ComboKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
  25.     procedure CNCommand(var Message: TWMCommand); Message CN_COMMAND;
  26.   protected
  27.   public
  28.     constructor Create(Owner: TComponent); override;
  29.     destructor Destroy; override;
  30.   end;
  31.  
  32. type
  33.   TForm1 = class(TForm)
  34.     ComboBox1 : TComboBox;
  35.     ComboBox2: TComboBox;
  36.     ComboBox3: TComboBox;
  37.     ComboBox4: TComboBox;
  38.     Button1 : TButton;
  39.     ComboBox5: TComboBox;
  40.     procedure Button1Click(Sender: TObject);
  41.     procedure FormCreate(Sender: TObject);
  42.   private
  43.   public
  44.   end;
  45.  
  46. var
  47.   Form1: TForm1;
  48.  
  49. implementation
  50.  
  51. {$R *.dfm}
  52.  
  53. constructor TComboBox.Create(Owner: TComponent);
  54. begin
  55.  
  56.   inherited;
  57.  
  58.   AutoComplete := False;
  59.   StoredItems := TStringList.Create;
  60.   StoredItems.OnChange := StoredItemsChange;
  61.   Self.OnExit := ComboExit;
  62.   Self.OnCloseUp := ComboCloseUp;
  63.   Self.OnKeyDown := ComboKeyDown;
  64.   UnFlicker := False;
  65.   CtrlLoad := False;
  66.  
  67. end;
  68.  
  69. destructor TComboBox.Destroy;
  70. begin
  71.   StoredItems.Free;
  72.   inherited;
  73. end;
  74.  
  75. procedure TComboBox.LoadHistory(FileHistory : String = ''; CountHistory : Byte = 10);
  76. begin
  77.  
  78.   CtrlLoad := True;
  79.  
  80.   Self.DropDownCount := CountHistory;
  81.   CountItems := CountHistory;
  82.  
  83.   if FileHistory = EmptyStr then
  84.       HistoryName := 'History_' + Self.Name + '.txt'
  85.   else
  86.       HistoryName := FileHistory;
  87.  
  88.   if FileExists(HistoryName) then
  89.       Self.StoredItems.LoadFromFile(HistoryName);
  90.  
  91. end;
  92.  
  93. procedure TComboBox.CNCommand(var Message: TWMCommand);
  94. begin
  95.  
  96.   inherited;
  97.  
  98.   if Message.NotifyCode = CBN_EDITUPDATE then
  99.       FilterItems;
  100.  
  101. end;
  102.  
  103. procedure TComboBox.FilterItems;
  104. var
  105.   i : Integer;
  106.   StartPos, EndPos : Integer;
  107.  
  108. begin
  109.  
  110.   SendMessage(Handle, CB_GETEDITSEL, WPARAM(@StartPos), LPARAM(@EndPos));
  111.  
  112.   AuxText := Text;
  113.  
  114.   if Text <> EmptyStr then
  115.   begin
  116.  
  117.       Items.Clear;
  118.  
  119.       for i := 0 to StoredItems.Count - 1 do
  120.       begin
  121.         if PosEx(LowerCase(Text), LowerCase(StoredItems[i])) > 0 then
  122.             Items.Add(StoredItems[i]);
  123.       end;
  124.  
  125.   end
  126.   else
  127.       Items.Assign(StoredItems);
  128.  
  129.   if UnFlicker then
  130.       SendMessage(Handle, CB_SHOWDROPDOWN, Integer(True), 0);
  131.  
  132.   Text := AuxText;
  133.  
  134.   SendMessage(Handle, CB_SETEDITSEL, 0, MakeLParam(StartPos, EndPos));
  135.  
  136.   UnFlicker := True;
  137.  
  138. end;
  139.  
  140. procedure TComboBox.StoredItemsChange(Sender: TObject);
  141. begin
  142.  
  143.   if Assigned(StoredItems) then
  144.       FilterItems;
  145.  
  146. end;
  147.  
  148. procedure TComboBox.ComboExit(Sender: TObject);
  149. var
  150.   i : Integer;
  151.  
  152. begin
  153.  
  154.   if (Items.IndexOf(Text) = -1) and (Text <> EmptyStr) and CtrlLoad then
  155.   begin
  156.       StoredItems.Insert(0,Text);
  157.       for i := StoredItems.Count - 1 downto CountItems do
  158.         StoredItems.Delete(i);
  159.       StoredItems.SaveToFile(HistoryName);
  160.       Self.ItemIndex := 0;
  161.   end;
  162.  
  163. end;
  164.  
  165. procedure TComboBox.ComboCloseUp(Sender: TObject);
  166. begin
  167.  
  168.   If (AuxText <> EmptyStr) and (UpDown = False) then
  169.   begin
  170.       Text := AuxText;
  171.   end;
  172.  
  173.   UpDown := False;
  174.  
  175. end;
  176.  
  177. procedure TComboBox.ComboKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
  178. begin
  179.   if (Key = VK_UP) or (Key = VK_DOWN) then
  180.       UpDown := True;
  181. end;
  182.  
  183. procedure TForm1.Button1Click(Sender: TObject);
  184. var
  185.   i : Integer;
  186.  
  187. begin
  188.  
  189.   for i := 0 to ComponentCount - 1 do
  190.   begin
  191.       if Components[i] is TComboBox then
  192.       begin
  193.         if TComboBox(Components[i]).Text <> EmptyStr then
  194.             ShowMessage(TComboBox(Components[i]).Text);
  195.       end;
  196.   end;
  197.  
  198. end;
  199.  
  200. procedure TForm1.FormCreate(Sender: TObject);
  201. begin
  202.  
  203.   // Archivo Histórico definido por el usuario y por defecto 10 items por archivo
  204.   ComboBox1.LoadHistory('History_1.txt');
  205.  
  206.   // Archivo Histórico definido por el usuario y 12 items por archivo
  207.   ComboBox2.LoadHistory('History_2.txt',12);
  208.  
  209.   // Archivo Histórico definido por defecto ('History_' + Self.Name + '.txt') y 10 items por archivo
  210.   ComboBox3.LoadHistory;
  211.  
  212.   // Archivo Histórico definido por defecto ('History_' + Self.Name + '.txt') y 5 items por archivo
  213.   ComboBox4.LoadHistory('',5);
  214.  
  215.   // ComboBox5 no usa el método LoadHistory y por lo tanto se comporta como un Combobox Estándar
  216.  
  217. end;
  218.  
  219. end.



Qué hice yo:



delphi
  1. unit oAutocompletarCombo;
  2.  
  3. interface
  4.  
  5. uses
  6.   StrUtils, Classes, Messages, Controls, SysUtils, Windows, StdCtrls;
  7.  
  8. type
  9.   TComboBoxAutocomplete = class(TComboBox)
  10.   private
  11.     CtrlLoad : Boolean;
  12.     UnFlicker : Boolean;
  13.     UpDown : Boolean;
  14.     AuxText : String;
  15.     HistoryName : String;
  16.     StoredItems : TStringList;
  17.     CountItems : Byte;
  18.     procedure FilterItems;
  19.     procedure CNCommand(var Message: TWMCommand); Message CN_COMMAND;
  20.   public
  21.     procedure StoredItemsChange(Sender: TObject);
  22.     procedure LoadHistory(FileHistory : String = ''; CountHistory : Byte = 10);
  23.       procedure ComboExit(Sender: TObject);
  24.     procedure ComboCloseUp(Sender: TObject);
  25.     procedure ComboKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
  26.     constructor Create(Owner: TComponent); override;
  27.     destructor Destroy; override;
  28.   end;
  29.  
  30. implementation
  31.  
  32. {$R *.dfm}
  33.  
  34. constructor TComboBoxAutocomplete.Create(Owner: TComponent);
  35. begin
  36.   inherited;
  37.   AutoComplete := False;
  38.   StoredItems := TStringList.Create;
  39.   StoredItems.OnChange := StoredItemsChange;
  40.   Self.OnExit := ComboExit;
  41.   Self.OnCloseUp := ComboCloseUp;
  42.   Self.OnKeyDown := ComboKeyDown;
  43.   UnFlicker := False;
  44.   CtrlLoad := False;
  45. end;
  46.  
  47. destructor TComboBoxAutocomplete.Destroy;
  48. begin
  49.   StoredItems.Free;
  50.   inherited;
  51. end;
  52.  
  53. procedure TComboBoxAutocomplete.LoadHistory(FileHistory : String = ''; CountHistory : Byte = 10);
  54. begin
  55.   CtrlLoad := True;
  56.   Self.DropDownCount := CountHistory;
  57.   CountItems := CountHistory;
  58.   if FileHistory = EmptyStr then
  59.       HistoryName := 'History_' + Self.Name + '.txt'
  60.   else
  61.       HistoryName := FileHistory;
  62.   if FileExists(HistoryName) then
  63.       Self.StoredItems.LoadFromFile(HistoryName);
  64. end;
  65.  
  66. procedure TComboBoxAutocomplete.CNCommand(var Message: TWMCommand);
  67. begin
  68.   inherited;
  69.   if Message.NotifyCode = CBN_EDITUPDATE then
  70.       FilterItems;
  71. end;
  72.  
  73. procedure TComboBoxAutocomplete.FilterItems;
  74. var
  75.   i : Integer;
  76.   StartPos, EndPos : Integer;
  77. begin
  78.   SendMessage(Handle, CB_GETEDITSEL, WPARAM(@StartPos), LPARAM(@EndPos));
  79.   AuxText := Text;
  80.   if Text <> EmptyStr then
  81.   begin
  82.       Items.Clear;
  83.       for i := 0 to StoredItems.Count - 1 do
  84.       begin
  85.         if PosEx(LowerCase(Text), LowerCase(StoredItems[i])) > 0 then
  86.             Items.Add(StoredItems[i]);
  87.       end;
  88.   end
  89.   else
  90.       Items.Assign(StoredItems);
  91.   if UnFlicker then
  92.       SendMessage(Handle, CB_SHOWDROPDOWN, Integer(True), 0);
  93.   Text := AuxText;
  94.   SendMessage(Handle, CB_SETEDITSEL, 0, MakeLParam(StartPos, EndPos));
  95.   UnFlicker := True;
  96. end;
  97.  
  98. procedure TComboBoxAutocomplete.StoredItemsChange(Sender: TObject);
  99. begin
  100.   if Assigned(StoredItems) then
  101.       FilterItems;
  102. end;
  103.  
  104. procedure TComboBoxAutocomplete.ComboExit(Sender: TObject);
  105. var
  106.   i : Integer;
  107. begin
  108.   if (Items.IndexOf(Text) = -1) and (Text <> EmptyStr) and CtrlLoad then
  109.   begin
  110.       StoredItems.Insert(0,Text);
  111.       for i := StoredItems.Count - 1 downto CountItems do
  112.         StoredItems.Delete(i);
  113.       StoredItems.SaveToFile(HistoryName);
  114.       Self.ItemIndex := 0;
  115.   end;
  116. end;
  117.  
  118. procedure TComboBoxAutocomplete.ComboCloseUp(Sender: TObject);
  119. begin
  120.   If (AuxText <> EmptyStr) and (UpDown = False) then
  121.   begin
  122.       Text := AuxText;
  123.   end;
  124.   UpDown := False;
  125. end;
  126.  
  127. procedure TComboBoxAutocomplete.ComboKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
  128. begin
  129.   if (Key = VK_UP) or (Key = VK_DOWN) then
  130.       UpDown := True;
  131. end;
  132.  
  133. end.


  • 0

#7 bigleaguer

bigleaguer

    Advanced Member

  • Miembros
  • PipPipPip
  • 66 mensajes

Escrito 17 marzo 2014 - 01:34

Saludos, santiago14 tienes que borrar la línea que dice {$R *.dfm} justo debajo de implementation.
  • 0

#8 santiago14

santiago14

    Advanced Member

  • Miembros
  • PipPipPip
  • 334 mensajes
  • LocationCerrillos - Salta - Argentina

Escrito 18 marzo 2014 - 05:27

Saludos, santiago14 tienes que borrar la línea que dice {$R *.dfm} justo debajo de implementation.

Ahh, jajaja. Perdón, tenés razón.

Gracias.
  • 0

#9 santiago14

santiago14

    Advanced Member

  • Miembros
  • PipPipPip
  • 334 mensajes
  • LocationCerrillos - Salta - Argentina

Escrito 19 marzo 2014 - 10:08

Funciona muy bien el comboBox con el autocompletado.
Resulta que ahora, mientras estoy escribiendo dentro del Combo "desaparece" la flecha del mouse. La única forma de recuperarlo es haciendo click en algún lugar del formulario que contiene el combo.
Si bien no es un gran defecto, para el usuario final que usa esto, le es molesto.

Espero poder encontrarle la vuelta.

Santiago.
  • 0




IP.Board spam blocked by CleanTalk.