none
Actualizar cambios en un treeview RRS feed

  • Pregunta

  • Tengo desde hace tiempo un "custom Control" basado en un TreeView de Windows.Forms. Lo uso en diversas aplicaciones y utilidades que necesitan recorrer el arbol de directorios.

    Funciona de forma estatica, cargando los nodos a medida que se necesitan. Lo que ahora necesito es incorporar cualquier modificacion dinamicamente. En definitiva emular , en funciones, lo que hace Windows Explorer.

    La idea es sencilla y en absoluto nueva. Con el evento de un Timer creo un nuevo treeview , reviso los nodos que el antiguo tenía expandidos y voy expandiendo los mismos en el nuevo, de tal manera que visualmente no se percibe nada , salvo los cambios: Un nuevo driver, un nuevo fichero o directorio o por el contrario su eliminacion.

    He probado dos formas:

    1) crear una copia del antiguo , limpiar el nuevo y empezar a construirlo en base a los datos del antiguo.

    2) Crear uno nuevo, basandome en el antiguo y al final copiarlo sobre el viejo.

    Ninguna de las dos funciona puesto que los nodos del arbol se colapsan.

    Creo que el problema lo tengo en la copia puesto que la propiedad del nodo copiado siempre dice Expanded=false. Utilizo el siguiente metodo:

     this.Nodes.Clear()     
    foreach (TreeNode node in newTreeView.Nodes)
      {
       this.Nodes.Add((TreeNode)node.Clone());
      }

    Donde "This" es el TreeView principal que hereda directamente de Windows.Forms.

    He navegado por internet, pero me ha causado mas lio que aclaraciones puesto que existen varios espacios de nombres con la misma clase y funcionamiento ligeramente diferente.

    El proceso de generacion del nuevo arbol,(cuando funcione, lo hare mas eficiente) es:

     private void this_ActualizaTree(object sender, EventArgs e)
                {
                TreeView newTreeView = new TreeView();
                CargarArbol(newTreeView);
                foreach (TreeNode nodoViejo in this.Nodes)
                    {
                    
                        foreach (TreeNode nodoNuevo in newTreeView.Nodes)
                        {
                        if(nodoNuevo.Name ==nodoViejo .Name )
                            {
                                //if (nodoViejo == this.SelectedNode) 
                                 //if (nodoViejo.IsSelected ) 
                                     //newTreeView.SelectedNode = nodoNuevo;
                            if (nodoViejo.IsExpanded)
                                {
                                ExpandNodos(nodoNuevo, nodoViejo);
                                break;
                                }
                            }
                        }
                    }
                //---------------- clonar el nuevo TreeView alviejo -----------------------
                this.Nodes.Clear();
                //oldTreeView = CloneUsingXaml(treeView) as TreeView;
                foreach (TreeNode node in newTreeView.Nodes)
                {
                    this.Nodes.Add((TreeNode)node.Clone());
                }
                    // --------------------------------------------------------------------
                }
            private void ExpandNodos(TreeNode nuevo, TreeNode viejo)
            {
                NodosHijo(nuevo);
                nuevo.Expand();
                //nuevo.ExpandAll();
                foreach (TreeNode nodoNuevo in nuevo.Nodes)
                {
                    //if(nodo.)
                    foreach (TreeNode nodoViejo in viejo.Nodes)
                    {
                        if (nodoNuevo.Name == nodoViejo.Name)
                        {
                            if (nodoViejo.IsSelected) nodoNuevo.TreeView.SelectedNode = nodoNuevo;
    
                            if (nodoViejo.IsExpanded)
                            {
                                ExpandNodos(nodoNuevo, nodoViejo);
                                break;
                            }
                        }
                    }
                }
            }

    Los metodos cargarArbol y NodosHijo

      private void CargarArbol(TreeView treeview)
            {
                foreach (DriveInfo DRV in DriveInfo.GetDrives())
                {
                    TreeNode NodoDrive = treeview.Nodes.Add(DRV.Name);
                    NodoDrive.Name = DRV.Name;
                    if (DRV.IsReady)
                    {
                        NodoDrive.Text = String.Format("{0} ({1}:)", DRV.VolumeLabel, DRV.Name[0]);
                        NodoDrive.Tag = DRV.VolumeLabel; //solo en la raiz se asigna label
                    }
                    else
                    {
                        NodoDrive.Text = String.Format("{0} ({1}:)", DRV.DriveType, DRV.Name[0]);
                        //   NodoDrive.Name = DRV.DriveType.ToString();
                        NodoDrive.Tag = DRV.DriveType.ToString(); //solo en la raiz se asigna label
                        break;
                    }
    
                    NodosHijo(NodoDrive);
    
                }
            }
            /// <summary>
            /// Partiendo de un nodo padre, se crean tantos nodos hijo como subdirectorios tenga
            /// ademas se crea un nodo fantasma para que salga el icono de que tiene cosas debajo
            /// </summary>
            /// <param name="tnPadre"></param>
            private void NodosHijo(TreeNode tnPadre)
            {
                string sPath = tnPadre.Name;
                try
                {
                    DirectoryInfo rootDir = new DirectoryInfo(sPath); ;
                    foreach (DirectoryInfo dir in rootDir.GetDirectories())
                    {
                        // TreeNode subfolder = tnode.Nodes.Add(sDirectorio); 
                        TreeNode sSubDirectorio = tnPadre.Nodes.Add(dir.Name);
                        sSubDirectorio.Tag = dir.Name; //salvo en el raiz que se asigna label, se pone el nombre del nodo sin separadores, ni nada
                        sSubDirectorio.Name = dir.FullName;
                        sSubDirectorio.Nodes.Add(CARGANDO);
                    }
                }
                catch (System.UnauthorizedAccessException ex)
                {
                    MessageBox.Show(ex.Message, "Problemas de autorizaciones", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    Cursor.Current = Cursors.Default;
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message, "Error Inesperado", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    Cursor.Current = Cursors.Default;
                }
    
            }

    • Editado Ra.món lunes, 4 de mayo de 2015 13:27 Faltaban datos
    lunes, 4 de mayo de 2015 13:22

Respuestas

  • He estado probando lo de "Filesystemwatcher". sin duda esta solución es la mas elegante, sin embargo me complica mucho las cosas en este momento ya que tengo que replantear todo el programa.

    Ademas alejarme un poco del problema me ha ayudado a encontrar una solución mas sencilla, acorde con lo que venía haciendo. En realidad tenía el problema resuelto, me faltaba tan solo un pequeño detalle que consiste en dejar la pantalla para el usuario tal como la dejó , mas los cambios. Esto lo consigo con un metodo recursivo que partiendo de la copia actualizada expande los nodos del antiguo.

    O sea , el proceso, el mismo planteado al principio:

    1) Creo un nuevo arbol , con los mismos nodos que el antiguo.

    2) Limpio el antiguo y copio mediante el metodo Treenode.Clone() todos los nodos raiz (yo parto desde los drivers, no desde "Mi Equipo" que sería mas sencillo.

    3) Exploro todos los nodos y los expando.

    Por cierto: En su día estuve investigando como obtener las carpetas especiales y algunas son sencillas pero por ejemplo la de "favoritos" que era la mas interesante, no conseguí averiguar como obtenerla. ¿alguien sabe como hacer esto?.

    Saludos. y gracias de nuevo por la ayuda.


    Ramón

    • Marcado como respuesta Ra.món jueves, 7 de mayo de 2015 6:25
    jueves, 7 de mayo de 2015 6:22

Todas las respuestas

  • Tercera forma:

    Dado que el mecanismo de copia, o lo que sea, no me funciona se me ha ocurrido una tercera vía. consiste en lo siguiente:

    El Treeview que visualiza el usuario sería un mero puntero y luego utilizaría dos treeview mas que funcionarían en modo "switch" .Esto es; cargo el primero, refresco con el segundo y viceversa. Al finalizar la carga, lo asignaría al que está en pantalla. Esto tampoco me funciona, pero si alguien me orienta sobre este problema , tendría el asunto resuelto.  La instrucción:

    TreeViewPantalla = TreeViewSwitchA;

    Da el error de que no se puede convertir de forma implícita, tiene que hacerse de forma explicita. ¿Como se hace esto?. No conozco ningún: " Treeview.Parse()" o similar ¿existe?. Tendría que utilizar código no administrado?. En fin: múltiples incógnitas y múltiples caminos que me cuestan "Dios y ayuda" y no me llevan a ningún sitio.


    Ramón

    martes, 5 de mayo de 2015 6:41
  • Hola:

    Entiendo que TreeViewPantalla es del tipo "myCustomTreeView" (el que tu están creando) y los TreeViewSwitch los has creado como TreeViews normales, por eso no te deja convertir uno en otro.

    Podrías probar de crear los TreeViewSwitch del mismo tipo que TreeViewPantalla y entonces te dejará asignarlos directamente.

    Dicho esto, todas tus propuestas se basan en crear 'otro' treeview cada vez que se ejecuta un timer... también podrías trabajar siempre con el mismo treeview, y 'controlar' cuando es necesario modificarlo. Para ello tienes la clase FileSystemWatcher:

    https://msdn.microsoft.com/es-es/library/system.io.filesystemwatcher(v=vs.110).aspx?cs-save-lang=1&cs-lang=csharp#code-snippet-1

    (como idea), cada vez que se expande un nodo (un directorio), asignar un 'filesystemwatcher' a ese nodo, y "si y solo si" el directorio cambia por alguna razón (onChanged/onRenamed), actualizar el nodo.

    Espero que te sirva.

    Un saludo

    martes, 5 de mayo de 2015 9:44
  • Hola: muchas gracias por tu ayuda. Me parecen propuestas geniales.

    Fijate que hace poco utilice el "Filesystemwatcher" para otro proyecto, pero estoy tan "encebollado" con el "triviu" , el "trinode", el "espand" y el "selezted"  que no se me había ni pasado por la imaginación.

    el TreeViewPantalla es una clase que hereda de TreeView , mientras que las otras son instancias de TreeView, tampoco se me paso por la cabeza que hubiese sutiles diferencias en esto. Tampoco lo había hecho nunca.

    Creo que me has alegrado el día, que digo el día ... la semana. Esta tarde lo pruebo TODO y te digo.

    Un abrazo.


    Ramón

    martes, 5 de mayo de 2015 11:45
  • He estado probando lo de "Filesystemwatcher". sin duda esta solución es la mas elegante, sin embargo me complica mucho las cosas en este momento ya que tengo que replantear todo el programa.

    Ademas alejarme un poco del problema me ha ayudado a encontrar una solución mas sencilla, acorde con lo que venía haciendo. En realidad tenía el problema resuelto, me faltaba tan solo un pequeño detalle que consiste en dejar la pantalla para el usuario tal como la dejó , mas los cambios. Esto lo consigo con un metodo recursivo que partiendo de la copia actualizada expande los nodos del antiguo.

    O sea , el proceso, el mismo planteado al principio:

    1) Creo un nuevo arbol , con los mismos nodos que el antiguo.

    2) Limpio el antiguo y copio mediante el metodo Treenode.Clone() todos los nodos raiz (yo parto desde los drivers, no desde "Mi Equipo" que sería mas sencillo.

    3) Exploro todos los nodos y los expando.

    Por cierto: En su día estuve investigando como obtener las carpetas especiales y algunas son sencillas pero por ejemplo la de "favoritos" que era la mas interesante, no conseguí averiguar como obtenerla. ¿alguien sabe como hacer esto?.

    Saludos. y gracias de nuevo por la ayuda.


    Ramón

    • Marcado como respuesta Ra.món jueves, 7 de mayo de 2015 6:25
    jueves, 7 de mayo de 2015 6:22