Jump to content


Photo

[RESUELTO] Treeview como explorer con checkboxes


  • Please log in to reply
52 replies to this topic

#21 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4111 posts
  • LocationMadrid - España

Posted 02 March 2010 - 05:53 AM

no creo que se le pueda pedirm mas al codigo que haz dejado


Si te parece, entonces, podemos poner el tema como resuelto.  :D


Saludos.
  • 0

#22 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 14460 posts
  • LocationMéxico

Posted 02 March 2010 - 10:11 AM

........ no creo que se le pueda pedirm mas al codigo que haz dejado .....


Conociendo a nuestro amigo escafandra seguro que si le va a pedir más a ese código ;)

Salud OS
  • 0

#23 ifrit

ifrit

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 105 posts
  • LocationLa Habana, Cuba

Posted 21 April 2010 - 06:14 PM

Hola amigos, un calido saludo a todos.Solo una sugerencia
Cuando se selecciona MiPC da un error si el treeview no esta expandido (es decir el unico elemento del treeview es MiPC) sugiero que tanto MiPC como Panel de control no tengan checkbox
Atentamente ifrit

  • 0

#24 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4111 posts
  • LocationMadrid - España

Posted 22 April 2010 - 04:29 AM

Hola amigos, un calido saludo a todos.Solo una sugerencia
Cuando se selecciona MiPC da un error si el treeview no esta expandido (es decir el unico elemento del treeview es MiPC) sugiero que tanto MiPC como Panel de control no tengan checkbox
Atentamente ifrit


Ciertamente es un bug, lo he corregido. He optado por deshabilitar el check de Panel de Control. Al seleccionar Mi PC se marca como parcial, ya que el Panel de Control no puede ser seleccionado.

Con esto creo que está arreglado. Si encuentras alguna pega me lo dices.  ;)

Saludos.

Attached Files


  • 0

#25 ifrit

ifrit

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 105 posts
  • LocationLa Habana, Cuba

Posted 11 May 2010 - 05:27 AM

Hola scafandra, hasta ahora todo excelente con el codigo.
Pero me surge una duda y es la siguiente: Tengo en el Memolas direcciones(path) de x cantidad de ficheros y/o carpetas. Necesito que al clickear en un boton se vaya rellenando (marcando con checkbox) el Treeview con los valores del Memo. Es hacer toda la operacion inversa a como lo haz hecho hasta el momento.
Espero haberme hecho entender.
Muchas gracias
  • 0

#26 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4111 posts
  • LocationMadrid - España

Posted 11 May 2010 - 04:43 PM

Quizás la forma mas sencilla de conseguir lo que pretentes es expandir todo el árbol de directorios y luego buscar en éste los archivos a marcar. El problema es que el proceso puede ser lentísimo, pues supone explorar todos los discos del sistema. Esta idea queda descartada.

Lo razonable será expandir sólo las partes del árbol que se vean afectadas para marcar, entonces, lo que interese. Un ejemplo práctico de como conseguirlo:
 

cpp
  1. void __fastcall TForm1::Button2Click(TObject *Sender)
  2. {
  3.   Button2->Enabled = false;
  4.   Stop = false;
  5.   char Path[MAX_PATH];
  6.   char File[MAX_PATH];
  7.   TDir* Dir;
  8.   TTreeNode* Node;
  9.   char* fin;
  10.   int L, Len;
  11.   Node = TreeView->Items->Item[0];
  12.   // Expando el primer elemento (Mi PC)
  13.   Node->Expand(false);
  14.  
  15.   for(int n=0; n<Memo1->Lines->Count; n++){
  16.     if(Trim(Memo1->Lines->Strings[n])=="") continue;
  17.     strcpy(File, Trim(Memo1->Lines->Strings[n]).c_str());
  18.     fin = File;
  19.     int i = 0;
  20.     while(fin){
  21.       // Rompemos la ruta del archivo en carpetas y las buscamos en el arbol
  22.       Len = strlen(File);
  23.       fin = strchr(fin+1, '\\');
  24.       if(!fin) L = Len;
  25.       else L = (DWORD)fin-(DWORD)File;
  26.       // Buscamos en el arbol
  27.       for(; i<TreeView->Items->Count; i++){
  28.         Application->ProcessMessages();
  29.         Node = TreeView->Items->Item[i];
  30.         if(!Node->Text.IsEmpty()){
  31.           Dir = (TDir*)(Node->Data);
  32.           SHGetPathFromIDList(Dir->AbsoluteIdl, Path);
  33.           if(!strnicmp(File, Path, L) && L+1 >= strlen(Path)){
  34.             if(L<Len)  // Si es carpeta la expando
  35.               Node->Expand(false);
  36.             else      // Si es archivo lo marco
  37.               SetState(Node, Checked);
  38.             break;
  39.           }
  40.         }
  41.       }
  42.     }
  43.   }
  44.   Button2->Enabled = true;
  45. }

Saludos.
  • 0

#27 ifrit

ifrit

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 105 posts
  • LocationLa Habana, Cuba

Posted 12 May 2010 - 10:59 AM

Scafandra muchas gracias por el codigo, era exactamente lo que me propuse hacer pero no pude. Muchas gracias de veras por toda la ayuda.
  • 0

#28 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4111 posts
  • LocationMadrid - España

Posted 12 May 2010 - 12:27 PM

...era exactamente lo que me propuse hacer...


Me alegro de que se ajuste a tu pregunta. He de decirte que ese código tiene un pequeño bug que he solucionado. El nuevo código es prácticamente el mismo pero cambio donde dice:


cpp
  1. if(!strnicmp(File, Path, L))


debe decir:


cpp
  1. if(!strnicmp(File, Path, L) && L+1 >= strlen(Path))



Esto de debe a que la carpeta de Mis Documentos tiene duplicada su entrada en el árbol y altera la búsqueda. En un principio no caí en esto pero luego me di cuenta.

Saludos.
  • 0

#29 ifrit

ifrit

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 105 posts
  • LocationLa Habana, Cuba

Posted 01 September 2010 - 05:00 AM

hola amigos del foro.
escafandra una vez mas estoy acá con necesidades en el componente en el que tanto me has ayudado, esta vez no es por un bug es porque necesito lo siguiente, necesito identificar cuando se selecciona una carpeta y un fichero. Te explico, cuando seleccionas un fichero en un programa de backups se sobreentiende que lo que deseas es copiar dicho fichero, pero si selecciones una carpeta se entiende que deseas copiar el contenido de dicha carpeta sin importar si este contenido varia con el tiempo. De la forma actual que esta el treeview enviando los datos a el Memo no me sirve porque obtiene el contenido de una carpeta y si en el futuro se le agrega un fichero a dicha carpeta ese fichero nuevo no sera copiado, espero haberme hecho entender y que me puedas ayudar por favor, saludos.
  • 0

#30 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4111 posts
  • LocationMadrid - España

Posted 02 September 2010 - 06:08 AM

Entiendo lo que expones. Para conseguir lo que quieres tenemos que realizar algunos cambios en la forma de trabajar. Básicamente debemos realizar:

1.- Si está elegida una carpeta, guardar su nombre solamente en el StringList, no los archivos.
2.- Modificar la rutina de copia para que sea capaz de copiar carpetas.

Lo primero es modificar SaveFilesFormStrigns, quedará de esta forma:


cpp
  1. void TForm1::SaveFilesFormStrigns(TStrings *Strings)
  2. {
  3.   Stop = false;
  4.   Strings->Clear();
  5.   char Path[MAX_PATH];
  6.   TDir* Dir;
  7.   TTreeNode* Node;
  8.   for(int i=0 ; i<TreeView->Items->Count; i++){
  9.     Application->ProcessMessages();
  10.     Node = TreeView->Items->Item;
  11.     if(Node->StateIndex == Disable || !Node->Parent) continue;
  12.     if(Stop){
  13.       Strings->Clear();
  14.       break;
  15.     }
  16.     if(!Node->Text.IsEmpty()){
  17.       Dir = (TDir*)(Node->Data);
  18.       SHGetPathFromIDList(Dir->AbsoluteIdl, Path);
  19.       if(Node->StateIndex == Checked && Node->Parent->StateIndex != Checked && *Path){
  20.         if(!Dir->File) strcat(Path, "\\");
  21.         Strings->Add(String(Path));
  22.       }
  23.     }
  24.   }
  25. }

Al guardar el nombre de la carpeta le añade un carácter '\' para que el sistema de copia distinga con facilidad que debe copiar una carpeta y no un archivo.

LoadFilesFormStrigns debe ser modificado también para quitar ese carácter '\' que hemos añadido y cargue los archivos y carpetas en el árbol del TreeView.

He añadido una nueva función a la clase para desmarcar todos los elementos del árbol,  ResetState

Lo siguiente es diseñar el sistema de copia. Puesto que te interesaba hacerlo manejando Threads así lo he diseñado. La copia de carpetas puede hacerse de varias formas con la API. Yo he implementado una forma recursiva que termina llamando a FileCopy. Puedes cambiar esto para llamar a FileCopyEx si quieres. Esto te puede dar el control en una barra de progreso que te indicaría la copia de cada archivo.

Te expongo el sistema de copia:

CreatePath se encarga de crear las carpetas destino necesarias:


cpp
  1. void CreatePath(char* path)
  2. {
  3.   if(DirectoryExists(path)) return;
  4.  
  5.   char Path[MAX_PATH];
  6.   char* fin = strchr(path, '\\');
  7.   while((fin = strchr(fin+1, '\\'))!=0){
  8.       *Path = 0;
  9.       strncat(Path, path, (DWORD)fin-(DWORD)path);
  10.       CreateDirectory(Path, 0);
  11.   }
  12. }

[i]CopyFolder Copia una carpeta y sus subcarpetas en un destino de forma recursiva:


cpp
  1. void CopyFolder(String Folder, String Destination)
  2. {
  3.     if(Folder=="" || Destination =="") return;
  4.     TSearchRec sr;
  5.     if(!FindFirst(Folder + "\\*.*", faAnyFile, sr))
  6.     do{
  7.       if(sr.Name != "." && sr.Name != ".."){
  8.           if(sr.Attr & faDirectory){
  9.             CreatePath((Destination + sr.Name + "\\").c_str());
  10.             CopyFolder(Folder + sr.Name + "\\", Destination + sr.Name + "\\");
  11.           }else
  12.             CopyFile((Folder + sr.Name).c_str(), (Destination + sr.Name).c_str(), false);
  13.       }
  14.     } while (!FindNext(sr));
  15.     FindClose(sr);
  16. }

Por último te muestro la función principal del hilo de copiado:


cpp
  1. void __fastcall ThCopyFiles::Execute()
  2. {
  3.   String FileName;
  4.   for(int n=0; n < Strings->Count; n++){
  5.       FileName = Destino + String(Strings->Strings[n].c_str()+3);
  6.       CreatePath(ExtractFilePath(FileName).c_str());
  7.       // Detectamos si es una carpeta...
  8.       if(Strings->Strings[n][Strings->Strings[n].Length()]=='\\')
  9.         CopyFolder(Strings->Strings[n], FileName);
  10.       else
  11.         CopyFile(Strings->Strings[n].c_str(), FileName.c_str(), false);
  12.       if(Terminated) return;
  13.   }
  14. }

Y eso es todo. Adjunto un ejemplo de todo el código tal y como quedaría y he probado.


Saludos.

 

Attached Files


  • 0

#31 ifrit

ifrit

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 105 posts
  • LocationLa Habana, Cuba

Posted 02 September 2010 - 09:57 AM

scafandra, un millon de gracias por la respuesta tan rapida, gracias amigo, ya pruebo el codigo y te respondo.

editado:
scafandra: ya probé el ejemplo que mandaste, sencillamente genial, funciona a la perfección. mil gracias nuevamente.
  • 0

#32 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4111 posts
  • LocationMadrid - España

Posted 02 September 2010 - 10:29 AM

Me alegra de que sea lo que querías.

El sistema de copia lo he realizado conforme a lo previamente diseñado. De esta forma sirve de ejemplo pedagógico de programación con Threads y funciones de copia recursiva.

Otra forma de proceder a la copia consiste en el uso de las Shell Functions de la API de Windows, en concreto de la potente función SHFileOperation. La ventaja de este método es que trabaja en un hilo distinto, muestra una ventana con el progreso y tiene control de errores, ya que es el propio shell de Windows el que trabaja por nosotros.


Saludos.
  • 0

#33 ifrit

ifrit

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 105 posts
  • LocationLa Habana, Cuba

Posted 03 September 2010 - 02:24 PM

gracias scafandra, aveces no entiendo mucho de lo que me dices, porque mi nivel no esta ni cerca del tuyo pero bueno igual se que quieres ayudar, como esta hasta ahora lo veo perfecto. Aunque no dejare de explorar los links que mandas
muchas gracias y mis respetos
  • 0

#34 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4111 posts
  • LocationMadrid - España

Posted 04 September 2010 - 12:02 PM

Si quieres profundizar en ese tema, visita este truco;)

Saludos.


  • 0

#35 ifrit

ifrit

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 105 posts
  • LocationLa Habana, Cuba

Posted 08 September 2010 - 05:52 AM

hola escafandra, todo va viento en popa, pero me ha sucedido lo siguiente. Yo llamo al formulario que contiene al treeview explorer desde otro formulario que es el que contiene el memo donde se ven los ficheros y carpetas
qudaria asi: form1(memo con source), form2(contiene al treeview explorer)
cuando intento utilizar en form1 la funcion LoadFileFormStrings necesito declarar esta junto con SetState(TTreeNode *ANode, int State)
void ResetState();
#include <shlobj.h>
y otras dependencias que están en el form2, pero aun así no me compila y te dejo aca los errores que me arroja :

[ILINK32 Error] Error: Unresolved external 'TfrmTask::SetState(Comctrls::TTreeNode *, int)' referenced from D:\TRABAJO\CSA ORIGINS\DEBUG\UTASK.OBJ

Estoy intentando afregar muchas cosas que estan en el form2 pero ya me asuste me parace que estoy trasladando todo el form2 para el form1, me podrias ayudar porfavor

  • 0

#36 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4111 posts
  • LocationMadrid - España

Posted 08 September 2010 - 06:09 AM

No tienes que trasladar código de unos formularios a otros. Sólo tienes que incluir los archivos cabecera de una unit en otra para que puedas usar su código, siempre desde un objeto ya creado:

Form2 es el TreeView.
quiero acceder desde Form1 a una función del Form2:



cpp
  1. Form2->Función_lo_que_sea(Parámetros...);




Saludos.
  • 0

#37 ifrit

ifrit

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 105 posts
  • LocationLa Habana, Cuba

Posted 08 September 2010 - 09:43 AM

:(que pena scafandra era muy obvio, muchas gracias es cierto funciona 100%
pero aprovecho para preguntarte algo:

en esta linea:



cpp
  1. if(!strnicmp(File, Path, L) && L+1 >= strlen(Path)){



de la funcion LoadFilesFormStrigns me da un warning

[BCC32 Warning] uSource.cpp(667): W8012 Comparing signed and unsigned values

y lo otro es que cuando yo despliego el treeview y selecciono una carpeta o fichero y luego no la agrego al memo, cierro el form y luego lo vuelvo a abrir se mantienen seleccionados los ficheros o carpeta que había marcado.Espero que me entiendas lo que te quise decir..Es decir no es un error ni nada solo que seria bueno que al cerrar el form los ficheros seleccionados (se hallan pasado o no al memo) se des seleccionen

muchas gracias

  • 0

#38 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4111 posts
  • LocationMadrid - España

Posted 08 September 2010 - 11:40 AM



cpp
  1. if(!strnicmp(File, Path, L) && L+1 >= strlen(Path)){



de la funcion LoadFilesFormStrigns me da un warning

[BCC32 Warning] uSource.cpp(667): W8012 Comparing signed and unsigned values


Es porque L está declarado como un int, si lo declaras como un unsigned int no protestará.
tampoco así:


cpp
  1. if(!strnicmp(File, Path, L) && L+1 >= (int)strlen(Path)){



En cualquier caso no es un error y no afecta al comportamiento del programa.

y lo otro es que cuando yo despliego el treeview y selecciono una carpeta o fichero y luego no la agrego al memo, cierro el form y luego lo vuelvo a abrir se mantienen seleccionados los ficheros o carpeta que había marcado.Espero que me entiendas lo que te quise decir..Es decir no es un error ni nada solo que seria bueno que al cerrar el form los ficheros seleccionados (se hallan pasado o no al memo) se des seleccionen


Si creas el Form de forma dinámica y no como precreado, no pasará. También puedes usar la función ResetState que diseñé para desmarcar todo.

Saludos.
  • 0

#39 ifrit

ifrit

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 105 posts
  • LocationLa Habana, Cuba

Posted 09 September 2010 - 05:04 AM

Nuevamente mil gracias scafandra, aveces me da pena preguntarte tanto...te agradezco la paciencia. Todo lo que me dices funciona ok.
Saludos
  • 0

#40 ifrit

ifrit

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 105 posts
  • LocationLa Habana, Cuba

Posted 09 September 2010 - 08:34 AM

escafandra decidí trabajar con el formulario para no cargar tantos recursos al inicio y lo hice así, dime por favor si esta bien implementado

llamada desde otro form



cpp
  1. if (formulario_dinamico == NULL){
  2.     formulario_dinamico = new formulario_dinamico(this);
  3.     formulario_dinamico->ShowModal();
  4. }




FormClose del dinamico



cpp
  1. formulario_dinamico = NULL;
  2. Action = caFree; 


  • 0




IP.Board spam blocked by CleanTalk.