Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   Varios (https://www.clubdelphi.com/foros/forumdisplay.php?f=11)
-   -   Campo clave y campo valor en un ComboBox (https://www.clubdelphi.com/foros/showthread.php?t=84542)

santiago14 01-11-2013 13:22:31

Campo clave y campo valor en un ComboBox
 
Buenas, tengo que resolver algo mas o menos "sencillo". Necesito traer desde una Tabla (Firebird 2.1) dos campos: cod_cliente, nombre_cliente. Una lista con un Select...

Quiero poner nombre_cliente en un comboBox para que se le despliegue al usuario pero, al momento de guardar en la BD, uso solamente cod_cliente.

La cosa es así, en un TComboBox no puedo traer ambos valores, mostrar uno y guardarme el otro para usarlo cuando quiera. En un TDBComboBox si es posible (keyValue, KeyField, KeyValue)

Ahora bien; si NO quisiera usar TDBComboBox, ¿qué otra opción tengo? ¿Hay algún componente que me permita guardar la estructura (clave, valor) y poder hacer lo que comento (exceptuando el TDBComboBox claro)?

Espero haber sido claro, gracias y buen fin de semana.

Santiago.

ecfisa 01-11-2013 14:28:57

Hola santiago14.

Cita:

Empezado por santiago14 (Mensaje 469178)
...
La cosa es así, en un TComboBox no puedo traer ambos valores, mostrar uno y guardarme el otro para usarlo cuando quiera. En un TDBComboBox si es posible (keyValue, KeyField, KeyValue)
...

No es complicado hacer lo que queres usando un TComboBox:
Código Delphi [-]
procedure TForm1.FormCreate(Sender: TObject);
begin
  while not IBQuery.Eof do
  begin
    ComboBox1.AddItem(IBQuery.FieldByName('NOMBRE_CLIENTE').AsString,
      TObject(IBQuery.FieldByName('COD_CLIENTE').AsInteger));
    IBQuery.Next;
  end;
end;

procedure TForm1.ComboBox1Change(Sender: TObject);
begin
  with ComboBox1 do
    ShowMessage(Format('%s %d',[Items[ItemIndex], // NOMBRE_CLIENTE
      Integer(Items.Objects[ItemIndex])]));       // COD_CLIENTE
end;

(Creo haber contestado esto en un hilo anterior pero no pude encontrarlo. :()

Saludos :)

santiago14 01-11-2013 15:21:48

¡¡Genial!!
 
Gracias compañero, la verdad que me sacaste de un lío bárbaro.

santiago14 04-11-2013 02:41:39

Eliminar los objetos asociados a los elementos del combo.
 
Hasta ahora con la gran ayuda que obtuve aquí me fue muy bien.
Resulta que necesito eliminar los elementos del combo, los de la lista son fáciles (combo.clear) pero los objetos asociados no se como hacerlo. No estoy seguro que al hacer .clear de la lista se eliminen los objetos asociados, si es así, mi problema está resuelto sino, pido ayuda.

Necesito eliminar todos los objetos asociados de una, algo así como el .clear del combo para ponerlo en limpio y volver a cargarlo. La ayuda de Delphi, en el objeto Items, dice:

The TStrings object does not own the objects in the Objects array. Objects added to the Objects array still exist even if the TStrings object is destroyed. They must be explicitly destroyed by the application.

En alusión a esto es que escribo.

Espero haber sido claro.

Gracias.

ecfisa 04-11-2013 14:55:25

Hola santiago14.

Correcto, una instancia de TStrings no es propietaria de los objetos asociados, por lo que es tu tarea liberar la memoria ocupada. Si bien lo podes realizar directamente sobre el combobox, por motivos de reusabilidad tal vez te convenga hacerte una función que lo haga para cualquier componente que utilice TStrings.

Código Delphi [-]
...
procedure FreeObjects(TS: TStrings);
var
  i: Integer;
begin
  TS.Clear;
  for i := TS.Count-1 downto 0 do
  begin
    TS.Objects[i].Free;
    TS.Objects[i] := nil
  end;
end;

Ejemplo de uso:
Código Delphi [-]
procedure TForm1.FormDestroy(Sender: TObject);
begin
  FreeObjects(ComboBox1.Items);
  FreeObjects(ListBox1.Items);
  FreeObjects(Memo1.Lines);  
  // etc
end;

Saludos :)

santiago14 04-11-2013 22:25:52

He estado conversando con gente del equipo donde trabajo respecto a la solución que plantea ecfisa; la verdad que a mí me parece muy buena y pone punto final a un problem que es recurrente.

Uno de los chicos del grupo argumenta que "podría ser una solución algo lenta" cuando se trate de llenar, en este caso un Combo, con muchos datos que vienen de la BD. Digamos 5.000 registros.

Yo la verdad que no creo, pero... ¿Alguien a hecho esa medición?

Gracias.

Santiago.

nlsgarcia 05-11-2013 06:10:09

santiago14,

Cita:

Empezado por santiago14
..."podría ser una solución algo lenta" cuando se trate de llenar, en este caso un Combo, con muchos datos que vienen de la BD. Digamos 5.000 registros...

Pregunto: ¿Es práctico llenar un ComboBox con 5000 registros de una BD?, quizás en estos casos otras soluciones deban ser exploradas.

Espero sea útil :)

Nelson.

ecfisa 05-11-2013 13:53:09

Cita:

Empezado por santiago14 (Mensaje 469305)
Yo la verdad que no creo, pero... ¿Alguien a hecho esa medición?

Hola santiago14.

La verdad es que no, por que no utilizo esa solución. Pero no nos vamos a quedar con la duda... :)
Código Delphi [-]
function LoadComboBox(QY: TIBQuery; CB: TComboBox): string;
var
  freq, start, stop: Int64;
  i: Integer;
begin
  QY.Close;
  QY.SQL.Text := 'SELECT ID, NOMBRE FROM PACIENTES';
  QY.DisableControls;
  QueryPerformanceFrequency(freq);
  QueryPerformanceCounter(start);
  QY.Open;
  while not QY.Eof do
  begin
    CB.AddItem(QY.FieldByName('NOMBRE').AsString,
      TObject(QY.FieldByName('ID').AsInteger));
    QY.Next;
  end;
  QueryPerformanceCounter(stop);
  Result := FormatFloat('0,', (stop - start) * 1000000 div freq) + ' µs.';
  QY.EnableControls;
end;
Resultados:
Código:

Registros |  Tiempo
----------+--------------
 5000    |  426225 µs
10000    |  869611 µs
15000    |  1311832 µs
20000    |  1826615 µs
25000    |  2220191 µs
30000    |  2634683 µs

Saludos :)

Casimiro Notevi 05-11-2013 14:57:16

Cita:

Empezado por nlsgarcia (Mensaje 469312)
Pregunto: ¿Es práctico llenar un ComboBox con 5000 registros de una BD?

NO .

santiago14 05-11-2013 23:57:21

Coincido con Uds. no tiene sentido llenar un combo con 5000 registros. En realidad son menos, pero no quería ir a discutir sin saber bien el tema.

La cosa es así, se selecciona una localidad de un combo (habrá mas o menos 30); con esto se habilita otro con los barrios de esa localidad (algo así como 100 a 200); luego se habilita el combo con las calles de ese barrio, que si es grande tiene muchas (ahí podrían ser mas o menos 800 a 1000).

Estuve viendo los registros y para llenar 30000 registro se necesitan 2634683 !microsegundos!, o sea, poco mas de 2 segundos. No hay problema...

santiago14 06-11-2013 00:24:44

Siguiendo con el tema de los combos...
Cuando quiero recuperar el ItemIndex de uno a través del texto que está en .text hago:
Código Delphi [-]
 indice_combo:=combo1.items.IndexOf('hola');
Ahora bien, tengo datos en el Object de este combo, el cual se asocia con el combo a través de justamente el itemIndex.
¿Cómo hago para recuperar el índice asociado, en este caso al objeto, teniendo a la mano el dato que está dentro del object?

Yo hice algo, pero quisiera que lo vean:
Código Delphi [-]
function obtener_ItemIndex_segun_codigo(codigo:string; TS: TStrings):integer ;
var
  i: Integer;
  objeto:string;
begin
  Result:=-1;
  for i := (TS.Count - 1) downto 0 do
  begin
    objeto:=string(ts[i]);
    if codigo = objeto then
    begin
      Result:=i;
    end;
  end;
end;

Y se invoca:
Código Delphi [-]
indice:=obtener_ItemIndex_segun_codigo('ES001', cbxTipoSolicitud.Items);

¿Estoy mas o menos encaminado o no?

Gracias.

ecfisa 06-11-2013 00:48:13

Hola santiago14.

Una buena alternativa es usar un TDBLookupComboBox.

Tomando el caso anterior quedaría:
Código Delphi [-]
...
procedure TForm1.FormCreate(Sender: TObject);
begin
  with DBLookupComboBox1 do
  begin
    ListSource := DataSource1;
    ListField  := 'NOMBRE';
    KeyField   := 'ID';
    ListSource.DataSet.Last;
    ListSource.DataSet.First;
  end;
end;

procedure TForm1.DBLookupComboBox1Click(Sender: TObject);
begin
  with DBLookupComboBox1.ListSource.DataSet do
    Caption := Format('%s %d',[FieldByName('NOMBRE').AsString,
      FieldByName('ID').AsInteger])
end;

Saludos :)

ecfisa 06-11-2013 15:14:55

Hola santiago14.

Según interpreto de tu mensaje #11:
Código Delphi [-]
indice:=obtener_ItemIndex_segun_codigo('ES001', cbxTipoSolicitud.Items);
El campo código es de tipo string.

Si es así, a diferencia de un valor entero, no es posible hacerlo en forma directa mediante el método AddItem (o AddObject), tendrías que hacer algo como:
Código Delphi [-]
...
implementation

type
  TClase = Class
    Codigo: string;
  end;

var
  Cl : TClase;

function GetIndexObjFromStr(LB: TComboBox; const Value:string): Integer;
var
  i: Integer;
begin
  Result := -1;
  for i:= 0 to LB.Items.Count-1 do
    if Value = TClase(LB.Items.Objects[i]).Codigo then
      Result := i;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  while not IBQuery.Eof do
  begin
    Cl := TClase.Create;
    Cl.Codigo := IBQuery.FieldByName('COD_CLIENTE').AsString;
    ComboBox1.AddItem(IBQuery.FieldByName('NOMBRE_CLIENTE').AsString,
      TObject(Cl));
    IBQuery.Next;
  end;
end;

procedure TForm1.ComboBox1Change(Sender: TObject);
begin
  with ComboBox1 do
    ShowMessage(Format('%s %s',[Items[ItemIndex],
      TClase(Items.Objects[ItemIndex]).Codigo]));
end;


{ Obtener el índice a partir de la cadena almacenada 
   en la propiedad vectorial Objects }
procedure TForm1.Button1Click(Sender: TObject);
begin
  ComboBox1.ItemIndex := GetIndexObjFromStr(ComboBox1, 'ES001');
end;
...

Saludos :)


La franja horaria es GMT +2. Ahora son las 15:44:03.

Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi