Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   C++ Builder (https://www.clubdelphi.com/foros/forumdisplay.php?f=13)
-   -   Usar TListView fuera de un form (https://www.clubdelphi.com/foros/showthread.php?t=85392)

aguml 12-03-2014 15:43:04

Usar TListView fuera de un form
 
Hola amigos, tengo un problema, quiero usar un TListview que trabaje en un .h diferente del Unit.cpp de la aplicacion, o sea, que lo guardo por ejemplo en lista.h y lista.cpp y pueda usarlo con cualquier proyecto sin tener que modificarlo.
El problema es que tengo esto:

Código:

#include <ComCtrls.hpp>

BOOL SearchBPOnList(DWORD address, unsigned char original)
{
    TListView *listaBPs;
    char original = 'c';
    int valor = 1000;

    //Creo el listview
    listaBPs = new TListView(Application->MainForm);

    //le indico cual es su parent
    listaBPs->Parent = Application->MainForm;

    //Añadimos una columna por cada subitem
    listaBPs->Columns->Add();
    listaBPs->Columns->Add();

    //Añado un item y le añado dos subitems
    listaBPs->Items->Add();
    listaBPs->Items->Item[listaBPs->Items->Count]->SubItems->Add(valor);
    listaBPs->Items->Item[listaBPs->Items->Count]->SubItems->Add(original);

    //Añado un item y le añado dos subitems
    listaBPs->Items->Add();
    listaBPs->Items->Item[listaBPs->Items->Count]->SubItems->Add(valor + 1);
    listaBPs->Items->Item[listaBPs->Items->Count]->SubItems->Add(original);

    //borro el listview
    delete listaBPs;
}

y si intento añadir un item lo hace pero si intento añadir una columna no da error pero si intento añadir un item me tira el error "Control '' has no parent window"
¿como tendria que hacerlo para que trabaje bien?

escafandra 12-03-2014 17:58:21

Código:

    TListView *listaBPs;
    original = 'c';
    int valor = 1000;

    //Creo el listview
    listaBPs = new TListView(Application->MainForm);
    listaBPs->ViewStyle = vsReport; // Para visualizar con columnas
    //le indico cual es su parent
    listaBPs->Parent = Application->MainForm;

    //Añadimos una columna por cada subitem SON TRES COLUMNAS
    listaBPs->Columns->Add();  // Para Caption
    listaBPs->Columns->Add();  // Para Subitem1
    listaBPs->Columns->Add();  // Para Subitem2

    //Añado un item y le añado dos subitems
    TListItem  *ListItem = listaBPs->Items->Add();
    // El array listaBPs->Items->Item comienza en CERO !!!!
    listaBPs->Items->Item[listaBPs->Items->Count-1]->SubItems->Add(valor); 
    listaBPs->Items->Item[listaBPs->Items->Count-1]->SubItems->Add(original);

    //Añado un item y le añado dos subitems
    ListItem = listaBPs->Items->Add();
    listaBPs->Items->Item[listaBPs->Items->Count-1]->SubItems->Add(valor+1);
    listaBPs->Items->Item[listaBPs->Items->Count-1]->SubItems->Add(original);

    listaBPs->Update();  // Para visualizar los cambios
    Sleep(1000);            // Para ver que ha pasado
    //borro el listview
    delete listaBPs;


Saludos.

aguml 12-03-2014 18:57:41

Me sigue dando el mismo problema ya que el problema me lo daba en esta linea:

Código:

TListItem  *ListItem = listaBPs->Items->Add();
El problema tiene que venir por otro lado :(

Gracias por el interes.

escafandra 12-03-2014 19:20:08

Pues algo haces en tu código pues lo que te he expuesto funciona perfectamente (BCB5 y BCB6), siempre que exista un formulario principal (Application->MainForm)

Revisa los errores que he puesto comentados.

Te expongo lo que usé para probar:
Código:

#include <ComCtrls.hpp>
BOOL SearchBPOnList(DWORD address, unsigned char original)
{
    TListView *listaBPs;
    original = 'c';
    int valor = 1000;

    //Creo el listview
    listaBPs = new TListView(Application->MainForm);
    listaBPs->ViewStyle = vsReport; // Para visualizar con columnas
    //le indico cual es su parent
    listaBPs->Parent = Application->MainForm;

    //Añadimos una columna por cada subitem SON TRES COLUMNAS
    listaBPs->Columns->Add();  // Para Caption
    listaBPs->Columns->Add();  // Para Subitem1
    listaBPs->Columns->Add();  // Para Subitem2

    //Añado un item y le añado dos subitems
    listaBPs->Items->Add();
    // El array listaBPs->Items->Item comienza en CERO !!!!
    listaBPs->Items->Item[listaBPs->Items->Count-1]->SubItems->Add(valor); 
    listaBPs->Items->Item[listaBPs->Items->Count-1]->SubItems->Add(original);

    //Añado un item y le añado dos subitems
    listaBPs->Items->Add();
    listaBPs->Items->Item[listaBPs->Items->Count-1]->SubItems->Add(valor+1);
    listaBPs->Items->Item[listaBPs->Items->Count-1]->SubItems->Add(original);

    listaBPs->Update();  // Para visualizar los cambios
    Sleep(1000);            // Para ver que ha pasado
    //borro el listview
    delete listaBPs;
}

Saludos.

aguml 12-03-2014 19:46:02

Pues es raro ya que es un proyecto normal de c++builder 6 con su form llamado Form1 el cual tiene que hacer uso de esa funcion que estará en un archivo llamado busqueda.cpp y que debe ser portable hacia otros proyectos asi que no puedo poner como nombre Form1 ni nada por el estilo al crear el listview para no tener que estar pendiente de ese cambio al cambiar de proyecto.
Ya me dices lo de que no hay un MainForm y la verdad es que yo creia que el MainForm seria Form1 en este caso pero me haces dudar.
Igual el fallo viene por ahi pero no se. La idea es que coja al formulario principal del proyecto.
Ya he corregido los errores que me indicas pero tengo dudas.
1-No quiero visualizarlo por pantalla ¿tengo que hacer lo del estilo y el update entonces?
2-Solo quiero guardar dos datos por fila ¿se necesitan 3 columnas o podria usar el primer valor como caption y el segundo como subitem y solo crear dos columnas?

Gracias por la ayuda.

escafandra 12-03-2014 20:05:17

Añadirte que si creas un componente con dueño, este se encargará de su destrucción, por lo que no debes destruirlo.
Si quieres destruirlo tú mismo, entonces créalo así:
Código:

listaBPs = new TListView((TComponent*)0);
Piensa que si no asignas un Parent (ventana donde se va a visualizar) el código marcará una excepción. También fallará si lo creas antes de que el Handle de su parent exista, por ejemplo en el evento OnCreate del formulario principal.

No entiendo porqué no quieres que se vea, si es un simple "almacén", es mejor que guardes los datos de otra forma, como en un TStringList, por ejemplo.


Saludos

aguml 12-03-2014 20:22:40

¿en un TStringList se puede tener dos columnas? si es asi me vale igual.
Por cierto, he probado el codigo en un proyecto nuevo y funciona perfecto pero en el que lo tengo no funciona. El codigo pertenece a una clase y el TListView esta en el private de la clase y es creado en el constructor, añado los items desde otra funcion de la clase y lo destruyo en el destructor. ¿puede ser por algo de eso?

escafandra 12-03-2014 21:39:53

Cita:

Empezado por aguml (Mensaje 473652)
¿en un TStringList se puede tener dos columnas? si es asi me vale igual.

Un StringList no tiene dos columnas pero si las manejas de 2 en dos, las dos primeras pertenecen a la colunma 1 y 2 del primer elemento...

Puedes hacer un array de dos dimensiones o crear una estructura con dos string y hacer un array de ésta. Tambien puedes usar TList como array dinámico de tu estructura. En definitiva, tienes muchísimas soluciones a tu problema.

Cita:

Empezado por aguml (Mensaje 473652)
Por cierto, he probado el codigo en un proyecto nuevo y funciona perfecto pero en el que lo tengo no funciona. El codigo pertenece a una clase y el TListView esta en el private de la clase y es creado en el constructor, añado los items desde otra funcion de la clase y lo destruyo en el destructor. ¿puede ser por algo de eso?

Si lo vas a destruir tú mismo, el objeto no debe tener dueño, para lo que debes crearlo así:
Código:

listaBPs = new TListView((TComponent*)0);
Saludos.

aguml 12-03-2014 21:54:38

ya vi el problema. El problema es que uso "listaBPs =new TListView..." dentro del constructor de mi clase y si luego hago un add() desde otra funcion miembro de la clase falla pero si hago el new y el parent en el mismo sitio que esta el add() funciona correctamente. La cuestion es que el listview tiene que ser creado una sola vez durante la ejecucion y tiene que poderse accederse a ella desde varias funciones miembro de la clase. Con respecto al uso de otras opciones creo que esta es la que mas se adecúa y solo tengo que gestionarla bien. Otra cosa, el constructor se ejecuta al pulsar un boton del form1.

aguml 12-03-2014 21:58:55

bueno en realidad solo se accede desde 2 funciones y siempre pasará antes por la funcion de busqueda asi que se me ocurre que podria iniciar el puntero a null y con un condicional ver si es null para crearlo e iniciarlo o no.

ecfisa 12-03-2014 22:01:51

Cita:

Empezado por aguml (Mensaje 473652)
¿en un TStringList se puede tener dos columnas?

Hola aguml.

Podes aprovechar la propiedad Objects como segunda columna:
Código:

...
TStrings *TS = new TStringList;

void __fastcall TForm1::FormCreate(TObject *Sender)
{
  for(int i=0;i<10;i++)
  TS->AddObject("Item:" + IntToStr(i),((TObject*)(i*10)));
}

void __fastcall TForm1::Button1Click(TObject *Sender)
{
 for(int i=0;i<10;i++)
  ListBox1->Items->Add(TS->Strings[i]+ " - " +
    IntToStr((int)TS->Objects[i]));
}

void __fastcall TForm1::FormDestroy(TObject *Sender)
{
  TS->Clear();
  for(int i=0;i<TS->Count;i++)
    delete TS->Objects[i];
}

En el caso que quisieras almacenar otro tipo (AnsiString en el ejemplo):
Código:

...
class TClase {
public:
  AnsiString Cadena;
};

TClase *Clase;
TStrings *TS = new TStringList;

void __fastcall TForm1::FormCreate(TObject *Sender)
{
 for(int i=0;i<10;i++) {
  Clase = new TClase;
  Clase->Cadena = "Col2: "+IntToStr(i*10);
  TS->AddObject("Col1: "+IntToStr(i), ((TObject*)Clase));
 }
}

void __fastcall TForm1::Button1Click(TObject *Sender)
{
 for(int i=0;i<10;i++)
  ListBox1->Items->Add(TS->Strings[i]+ " - " +
    ((TClase*)(TS->Objects[i]))->Cadena);
}

void __fastcall TForm1::FormDestroy(TObject *Sender)
{
  TS->Clear();
  for(int i=0;i<TS->Count;i++)
    delete TS->Objects[i];
}

Saludos :)

aguml 12-03-2014 22:12:26

prefiero el listview porque ahora solo estoy probando con dos columnas pero seran 5 o 6.

aguml 12-03-2014 23:40:27

la opcion que mas me gusta seria crear una estructura y un tlist que administre las estructuras. El tlist tendria que poder hacer Add, delete, clear, y poder leer cualquiera de los miembros de cada estructura. ¿Como seria eso? No tengo ni idea de usar tlist.

ecfisa 13-03-2014 01:55:29

Hola aguml.
Cita:

Empezado por aguml (Mensaje 473663)
prefiero el listview porque ahora solo estoy probando con dos columnas pero seran 5 o 6.

Es que aquí, en el mensaje #7:
Cita:

Empezado por aguml (Mensaje 473652)
¿en un TStringList se puede tener dos columnas? si es asi me vale igual.

mencionaste que un TStringList igualmente te servía...
Cita:

Empezado por aguml (Mensaje 473669)
la opcion que mas me gusta seria crear una estructura y un tlist que administre las estructuras. El tlist tendria que poder hacer Add, delete, clear, y poder leer cualquiera de los miembros de cada estructura. ¿Como seria eso? No tengo ni idea de usar tlist.

Te hice un ejemplo que usa un TList para almacenar estructuras que a su vez contienen un TStringList, espero te sirva:
Código:

...
TList *List;

typedef struct TNodo{
  int nro;
  String cad;
  TStrings *SL;
} *PNodo;

void __fastcall TForm1::FormCreate(TObject *Sender)
{
  List = new TList;
  for(int i=0; i<50 ;i++) {
    PNodo nd = new TNodo;
    nd->nro = i;
    nd->cad = "Nodo ";
    nd->SL = new TStringList;
    for (int j=0; j<10; j++)
      nd->SL->Add("SL"+IntToStr(i+j));
    List->Add((void*)nd);
  }
}

void __fastcall TForm1::Button1Click(TObject *Sender)
{
  PNodo nd = new TNodo;
  String str;

  Memo1->Clear();
  List->Pack();
  for(int i=0; i<List->Count; i++) {
    nd = ((TNodo*)List->Items[i]);
    Memo1->Lines->Add(nd->cad+IntToStr(nd->nro));
    Memo1->Lines->Add("------------------------");
    str="";
    for(int j=0; j<nd->SL->Count;j++)
      str += nd->SL->Strings[j]+ ", ";
    str.SetLength(str.Length()-2);
    Memo1->Lines->Add(str);
    Memo1->Lines->Add("");
  }
  delete nd;
}
void __fastcall TForm1::FormDestroy(TObject *Sender)
{
  List->Clear();
  for(int i=0;i<List->Count;i++) {
    PNodo nd = new TNodo;
    nd = ((TNodo*)List->Items[i]);
    delete nd->SL;
    delete nd;
  }
}

Veras que recorro List para ir liberando manualmente los nodos, ya que una instancia de TList no es propietaria de los objetos que contiene. En el caso de SL no es necesario ya que no almacena objeto alguno.

Saludos :)

aguml 13-03-2014 08:58:19

es perfecto amigo. Solo tengo un par de preguntas. Si uso delete con la list, o sea lista->item[i]->delete, ¿Solo borro el puntero o borraria el objeto? Si borro un objeto de en medio ¿Se quedaria el hueco o se reorganiza? O sea, si tengo 3 objetos en la lista y borro el 2 ¿El 3 pasaria a ser el 2 en la lista o seguiria siendo el 3? Tambien he visto dos funciones muy interesantes que son find y sort. ¿Podria usarse el find de la lista para buscar un valor dentro de un miembro de las estructuras? Solo buscar por ejemplo en la variable entera de todas las estructuras de la lista. Y sort ¿Es para ordenar? ¿Como ordenaria? ¿Usando un elemento de la estructura? ¿Como seria eso? Gracias amigo!!

ecfisa 13-03-2014 17:02:58

Hola aguml.

Respecto a tu dudas con el método Delete (no el operador delete):
Cita:

Si uso delete con la list, o sea lista->item[i]->delete, ¿Solo borro el puntero o borraria el objeto?
Delete elimina el elemento de la lista pero no libera la memoria asociada.

Cita:

¿Se quedaria el hueco o se reorganiza?
No queda el hueco, mueve hacia arriba los demás elementos.


En cuanto al resto de las preguntas te ruego que las formules en otro hilo como indica el punto ( 8. ) de nuestra guía de estilo.

Saludos :)

aguml 13-03-2014 17:35:09

ok amigo gracias, ya lo he conseguido gracias a vuestra ayuda.
Solo una cosa mas que no me quedó claro de lo que me dices, si no libera la memoria y solo elimina el objeto ¿como seria para eliminar el objeto y liberar la memoria? ¿asi?

delete Lista->Item[i];
Lista->Delete(i);

Yo solo estoy haciendo la segunda linea y querria hacerlo bien. Solo eso amigos.

escafandra 13-03-2014 18:26:09

Sería en un bucle recorriendo todos los punteros y finalmente delete Lista.

TList es una clase que representa un array de punteros, por lo tanto tendrás que destruir cada puntero y finalmente el objeto TList.

Saludos.


La franja horaria es GMT +2. Ahora son las 11:06:12.

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