Ir al contenido


Foto

Mostrar información de un listado de objetos y referenciarlo con un control visual


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

#1 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.295 mensajes
  • LocationArgentina

Escrito 31 marzo 2016 - 02:50

Buenas,

La verdad es que no se como titular bien mi duda, y en como describir lo mejor posible el problema.

 

Les explico mi duda, tengo una clase que posee N propiedades. De esta clase voy a necesitar crear una cantidad no definida de objetos, por lo que para mantenerlos dipondré de una lista. O bien, definiré una nueva clase "List" que los administre. No interesa por el momento ni los tipos de esas propieades, ni si tendré una clase propia "List" o si simplemente utilizo un ObjectList que almacene el listado de objetos.

 

Lo más importante es que necesito mostrar al usuario del listado algunas de las N propiedades, digamos un M << N. El objetivo es informarle al usuario lo más importante, y la idea es disponer de un PopupMenu con un "Ver más". Este menú abrirá un cuadro de diálogo y detallará el resto de la información adicional.

 

Lo más fácil, que se me ha ocurrido, es tener un StringGrid, disponer de tantas columnas como M de estas propieades de interés y tantas filas como objetos a crear y mantenidos en la lista.

 

Ahora bien, yo quisiera asociar la fila con el objeto en cuestión. O que de algún modo pueda identificar la fila seleccionada con el objeto... Algo como el poder del TComboBox.Items.AddItem() que permite añadir no sólo un item sino también asociarlo con un objeto:


delphi
  1. MiCombo.Items.AddItem(MiInstancia.Propiedad1, TObject(MiInstancia));

Y luego poder "recuperarlo" con algo como:


delphi
  1. InstanciaSeleccionada := MiCombo.Items.Objects[MiCombo.ItemIndex];

Y listo, ya podemos hacer lo que se quiera con este objeto.

Pero llevado hacia el poder del TStringGrid.

 

El problema es que el TStringGrid, ofrece la referencia a los objetos por celda y no por fila:


delphi
  1. TMiClase(MiGrid.Objects[Col, Fila])

La posibilidad que he considerado es que ponga una columna fija y haga de ésta como una especie de ID. Es decir, mostrar en la columna fija el índice de la instancia en cuestión dentro de la lista. De este modo con leer la fila seleccionada (aunque no le encuentro una propiedad SelectedRow, aunque si un evento OnSelectedRow) y al leer el contenido de la columna fija puedo saber a que instancia hace referencia y de esa forma presentar su correspondiente información adicional.

El tema es que no me convence demasiado tener que mostrar ese "ID" al usuario. Es casi una analogía a los IDs que uno tiene en la base de datos, ¿tiene utilidad mostrarlos?

 

Si, admito que a estos objetos luego se almacenan de forma persistente en una base de datos. Y, naturalmente, tendrán una propiedad "OID" que contendrá una referencia hacia el valor asignado por el motor como clave primaria.

Es un mal menor, pero estuve pensando en que si se puede evitar mostrarlo, mejor.

 

¿Que otras opciones considerarían ustedes?


  • 1

#2 Agustin Ortu

Agustin Ortu

    Advanced Member

  • Moderadores
  • PipPipPip
  • 831 mensajes
  • LocationArgentina

Escrito 31 marzo 2016 - 04:03

No uses una lista, usa un diccionario y establece una relacion Numero de Fila del StringGrid -> Objeto

 

En el StringGrid, hay un truco muy sucio que es ponerle un ancho de 0 o 1, no recuerdo ahora exactamente con cual de los dos, pero de esa forma tenes una columna con un "valor que no queres mostrar, pero que podes consultar"


Editado por Agustin Ortu, 31 marzo 2016 - 04:04 .

  • 1

#3 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.295 mensajes
  • LocationArgentina

Escrito 31 marzo 2016 - 05:46

No uses una lista, usa un diccionario y establece una relacion Numero de Fila del StringGrid -> Objeto

 

En el StringGrid, hay un truco muy sucio que es ponerle un ancho de 0 o 1, no recuerdo ahora exactamente con cual de los dos, pero de esa forma tenes una columna con un "valor que no queres mostrar, pero que podes consultar"

 

Será que ya tengo la memoria hecha percha... Voy a hacer la preguntonta ¿Cómo era el TAD Diccionario?  :( 

 

Acabo de hacer una prueba rápida y simple con el truco sucio. Pues si, podría resultar... podría poner en esa columna oculta el OID/indice hacia el objeto en cuestión dentro de la lista al que referencia e implementar métodos como FindObject(FilaSeleccionada): TMiObjeto y su análogo FindFila(MiObjeto): integer para operar.

No es la salida más elegante pero toma el camino alterno.

 

Saludos,


  • 1

#4 Agustin Ortu

Agustin Ortu

    Advanced Member

  • Moderadores
  • PipPipPip
  • 831 mensajes
  • LocationArgentina

Escrito 31 marzo 2016 - 05:51

Hay otras formas tambien muy sucias, por ejemplo pintando con tinta invisible en el Grid  (y)

 

Yo tambien estoy hecho pelota, no me doy cuenta si lo del diccionario lo decis en joda o no jajaja

 

En teoria podrias guadar el objeto en el StringGrid, pero vas a tener que llevar un orden: guarda siempre en la misma interseccion celda/columna de cada fila

 

Osea, mete los TTuClase siempre en la primer celda de cada fila y los sacas siempre de ahi (te ahorras el diccionario y la columna invisible)


Editado por Agustin Ortu, 31 marzo 2016 - 05:53 .

  • 0

#5 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.295 mensajes
  • LocationArgentina

Escrito 31 marzo 2016 - 07:07

Hay otras formas tambien muy sucias, por ejemplo pintando con tinta invisible en el Grid  (y)

 

Yo tambien estoy hecho pelota, no me doy cuenta si lo del diccionario lo decis en joda o no jajaja

 

En teoria podrias guadar el objeto en el StringGrid, pero vas a tener que llevar un orden: guarda siempre en la misma interseccion celda/columna de cada fila

 

Osea, mete los TTuClase siempre en la primer celda de cada fila y los sacas siempre de ahi (te ahorras el diccionario y la columna invisible)

 

Algo así estaba pensando justamente. ¡Donde lo meto! Lo que tengo que ver es que pasa cuando se agrega.

Aparentemente se podría hacer un:


delphi
  1. MiGrid.Rows[indice].AddObject(MiNombre, TObject(MiObjeto));

Y también se la puede hacer por columnas, ya que Rows y Cols son TStrings. Lo que voy a tener que comprobar más adelante que efectos produce esto. Si lo hago por la vía Rows ¿Que hace con MiNombre? ¿Lo pone en la celda [0, indice]? ¿Agrega una columna más a la cuenta?

No está del todo claro eso.

 

Tengo el cerebro muy apagado ahora y voy a poder activarlo recién el lunes. :p

 

Saludos,

PD: No lo pregunté en joda. Ya no recuerdo mucho los detalles de ese TAD. La falta de práctica y su uso... :(


  • 0

#6 Agustin Ortu

Agustin Ortu

    Advanced Member

  • Moderadores
  • PipPipPip
  • 831 mensajes
  • LocationArgentina

Escrito 31 marzo 2016 - 08:11

No estoy muy enchufado con las colecciones de FPC
 
Estuve usando hace poco las colecciones genericas de dathox
 
Andas con suerte, justo use un diccionario
 
La clase TDictionary te permite establecer una coleccion en la que los elementos son pares de la forma Clave : Valor; es un indice, tabla de hash, llamalo como quieras, el principio es el mismo
 
La "Clave" es el Indice, lo que usas para buscar/identificar, el "Valor" es lo que almacenas. El diccionario no te va a dejar tener claves repetidas logicamente, aunque es posible sobreescribir valores (metodo AddOrSetValue)
 
Es una clase bastante linda para jugar, porque es mas rapida que una lista, no tenes que recorrerla para obtener un valor y encima esta te soporta genericos
 
El par Clave:Valor esta representado por un tipo record de Pascal:
 
La definicion es mas o menos asi:

delphi
  1. type
  2. TKeyValue<TKey, TValue> = record
  3. Key: TKey;
  4. Value: TValue;
  5. end;

Y el TDictionary:

delphi
  1. type
  2. TDictionary<TKey, TValue>

Por ejemplo podes escribir este codigo:

delphi
  1. var
  2. Par: TPair<Integer, TPersona>;
  3. FDictionary: TDictionary<Integer, TPersona>;
  4. Persona: TPersona;
  5. begin
  6. FDictionary := TDictionary<Integer, TPersona>.Create;
  7. try
  8. Persona.Id := 33;
  9. Persona.Nombre := 'Agustin';
  10. FDictionary.Add(Persona.Id, Persona);
  11.  
  12. Persona.Id := 74;
  13. Persona.Nombre := 'Juan';
  14. FDictionary.Add(Persona.Id, Persona);
  15.  
  16. Persona.Id := 12;
  17. Persona.Nombre := 'Marcelo';
  18. FDictionary.Add(Persona.Id, Persona);
  19.  
  20. // enumerador
  21. Memo1.Clear;
  22. for Par in FDictionary do
  23. Memo1.Lines.Add('%d - %s', [Par.Key, Par.Value.Nombre]);
  24.  
  25. Persona.Id := 12;
  26. Persona.Nombre := 'Pepe';
  27. // excepcion
  28. try
  29. FDictionary.Add(Persona.Id, Persona);
  30. except on E: Exception do
  31. Memo1.Lines.Add(E.Message);
  32. end;
  33.  
  34. ShowMessage('Enter para seguir');
  35.  
  36. // sobreescribe si existe
  37. FDictionary.AddOrSetValue(Persona.Id, Persona);
  38.  
  39. Memo1.Clear;
  40. for Par in FDictionary do
  41. Memo1.Lines.Add('%d - %s', [Par.Key, Par.Value.Nombre]);
  42.  
  43.  
  44. // como buscar?
  45. Persona := FDictionary.Items[12]; // pepe
  46. ShowMessageFmt('%d - %s', [Persona.Id, Persona.Nombre]);
  47.  
  48. // si no existe eleva excepcion
  49. try
  50. FDictionary.Items[123];
  51. except on E: Exception do
  52. ShowMessage(E.Message);
  53. end;
  54.  
  55. // pero tenes el try get
  56. if FDictionary.TryGetValue(123, Persona) then
  57. ShowMessageFmt('%d - %s', [Persona.Id, Persona.Nombre])
  58. else
  59. ShowMessage('No encontre al 123');
  60.  
  61. // tambien podes preguntar si contiene una clave
  62. if FDictionary.ContainsKey(12) then
  63. ShowMessage('Tengo la persona con id 12');
  64. finally
  65. FDictionary.Free;
  66. end;

Es interesante notar que para que la unidad Generics.Collections funcione hay tener el archivo jedi.inc (????) sino no te compila
 
Tiene que estar antes del uses de interface

delphi
  1. {$I jedi.inc}

Si no tenes el jedi.inc, lo podes bajar de aca
 
Igual no se, me parece raro, quiza tengo algo yo mal configurado o hay algun switch del compilador que altera ese archivo y por eso el otro compila (da error en sintaxis de genericos?)

Editado por Agustin Ortu, 31 marzo 2016 - 08:15 .

  • 1

#7 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.295 mensajes
  • LocationArgentina

Escrito 31 marzo 2016 - 08:33

Ufff. Ahora si que me dejaste en menos infinito. Mi cerebro ya se jubiló. Tengo que reconocer que hasta el día de hoy no he usado genéricos. Le conozco más o menos la teoría, en la práctica nada.

Voy a ver como va la cosa con ese enfoque, y si es que Lazarus lo aguanta. Ahora toca dormir, o mejor dicho: tratar de dormir. Porque no volveré a tocar mi almohada hasta el domingo.

Te agradezco muchísimo la enorme ayuda que estás dando.

 

Saludos,


  • 0

#8 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4.107 mensajes
  • LocationMadrid - España

Escrito 02 abril 2016 - 12:19

A mi visualmente me gusta más un ListView con estilo vsReport. La propiedad Data de cada Item es un puntero donde puedes guardar tu objeto con lo que la lista de Items (TListItems) te sirve como lista de tus objetos. Las columnas son menos directas de trabajar (SubItems) que en un StringGrid que va por celdas.

 

Siempre que quiero mostrar datos en forma de tabla visual, lo hago con un TListView que permite, además, ordenar los datos de las columnas "clickando" la cabecera y arrastrar cabeceras a una nueva posición.

 

Saludos.


  • 1




IP.Board spam blocked by CleanTalk.