WPF - Cargar Treeview con varias tablas C#

Llenado de items en un WPF Treeview Control con el contenido de datos en varias tablas.

Introducción

Normalmento desarrollo en Winform pero recientemente me encargaron un desarrollo usando WPF, muchos que empezamos a usarlo, nos hemos complicado la existencia y roto mucho de nuestros propios paradigmas al empezar a desarrollar con WPF.

Bueno resulta que con pequeñas diferencias, muy sutiles, que tienen Winform y WPF; necesité usar un Treeview Control para presentar la información e interactuar con el usuario. Revisando muchas fuentes de información (libros, páginas, vídeo tutoriales, experiencias con otros desarrolladores, etc.), no encontraba algo claro y que se ajustará a mis necesidades (todas muy buenas por cierto). Hasta que uniendo fragmentos de conocimiento de una y otra fuente, encontré mi solución, que en honor a lo complejo de hallar una solución que me resulta sencilla de adaptar a cualquier otra necesidad, decidí aportar algo a la comunidad y esperando les sea práctica y útil.

Aquí no encontrarás la conexión a la fuente de datos de las tablas que se usan pues se asume que ya sabes conectar a una fuente de datos (base de datos, archivo o cualquier otra), también se da por sentado que las tablas contienen una dato que puede usarse para relacionar con alguna otra de las usadas.

Tampoco encontrarás, como cargar la información al objeto DataSet usado pues ya están cargados previamente en alguna parte de tu código, se asume también que esté DataSet contiene las tablas que se usarán; sin embargo, sí encontrarás como crear la relación entre las tablas del DataSet.

Lo que sí encontrarás es cómo asignar un evento a un objeto TreeViewItem creado en el código, como asignarle valores a algunas de sus propiedades.

Lo principal es mostrar como usar el último item de la colección del TreeView para interactuar con el usuario y obtener los datos contenidos en él.


Antecedentes

Para muchos Winform es su primer API de programación visual y WPF es un gran reto, más cuando llevas mucho tiempo usando el primero. Los controles usados en el primero tienen algunas coincidencias con los controles en el segundo, no sólo el nombre.

Resulta que ambos, Winform y WPF, contienen un objeto llamado TreeView (vista de árbol) pero las propiedades, métodos e implementaciones son un tanto diferentes. Debo decir, que poco use este control en Winform, así que para aprender a usarlo en WPF la primer búsqueda de información que hice fue para el objeto Winform. Y al querer implementarlo en WPF me fue complicado en un inicio, después, fue otra historia que trataré de resumir en este artículo.

Caí en la cuenta que el control en WPF no tiene nodos, se me complicó aún más la solución al problema; esté control TreeView usa elementos o items, llamados TreeViewItem.

Con esto comencé nuevamente la búsqueda de información pero está vez, enfocada a WPF, y aprendí un poco de esté nuevo control para mi y encontré (como ya lo mencione) la solución simple.

Usando el código


Implemento el código con el uso de un TreeView, un DataSet que contiene 3 tablas y se relacionan entre sí con al menos una columna, los TreeViewItem necesarios para mostrar el árbol en pantalla o ventana, un DataTable extra para copiar los valores que se puede usar para DataGrid u otro objeto que use una tabla como fuente de datos.

Las tablas usadas están definidas como:

Tablas:
AÑOS
MESES
DIAS




Y por último, la que contiene los detalles del elemento que nos interesa:


FOLIOS

Los nombres de los objetos son:

XAML


<TreeView x:Name="EventoView" >
      <!-- Aquí puedes establecer la configuración que requieras para el control, 
              no es necesaria en este artículo-->
</TreeView>

C#


//Globales
private DataSet dsEventos = null;
private DataTable eventos = null;


//Locales
TreeViewItem añoItem = new TreeViewItem();
TreeViewItem mesItem = new TreeViewItem();
TreeViewItem diaItem = new TreeViewItem();
TreeViewItem folioItem = new TreeViewItem();


Para el código C#, aún no empieces a copiar el código pues se mostrará su implementación más adelante. Para el código XAML creo que no es necesario copiarlo pues puedes codificarlo rápidamente.

Bueno, pues creo que empezaré a explicarme. Se tienen 3 tablas como parte del DataSet dsEventos, siendo usual llenar este tipo de objetos con un DataAdapter, denominado como da, del origen de datos que designemos.

Las tablas las mapearemos con los nombres de "AÑOS", "MESES", "DIAS" y "FOLIOS".

Y creamos las relaciones de las tablas.

C#
   
   //Siendo da un objeto DataAdapter
   //Mapeo de tablas requeridas
   da.TableMappings.Add("Table", "AÑOS");
   da.TableMappings.Add("Table1", "MESES");
   da.TableMappings.Add("Table2", "DIAS");
   da.TableMappings.Add("Table3", "FOLIOS");
   //Creamos la instancia del DataSet
   dsEventos = new DataSet();
   //Llenado de DataSet
   da.Fill(dsEventos);
   //Relacionando tablas
   if (dsEventos.Tables.Count > 0)
   {
      //Relación de tablas
      dsEventos.Relations.Add("añoRel", dsEventos.Tables["AÑOS"].Columns["AÑO"], dsEventos.Tables["MESES"].Columns["AÑO"], false);
      dsEventos.Relations.Add("mesRel", dsEventos.Tables["MESES"].Columns["MES"], dsEventos.Tables["DIAS"].Columns["MES"], false);
      dsEventos.Relations.Add("diaRel", dsEventos.Tables["DIAS"].Columns["DÍA"], dsEventos.Tables["FOLIOS"].Columns["DÍA"], false);
   }
   else
   {
      //Cualquier bloque de sentencias para indicar que no hay tablas en el DataSet

   }

Con la información contenida en dsEventos podemos empezar a crear los items de EventoView con la implementación de varios ciclos foreach anidados, mientras vamos configurando cada item del árbol y agregamos el item inferior a su correspondiente superior. Recuerda hacer las validaciones necesarias para manejar las Excepciones.

C#

   try
   {
      if (dsEventos.Tables["Folios"].Rows.Count > 0)
      {
            // Se agregan elementos al árbol según relaciones establecidas.
            foreach (DataRow rwAño in dsEventos.Tables["AÑOS"].Rows)
            {
               TreeViewItem añoItem = new TreeViewItem()
               {
                  Header = rwAño["AÑO"],
                  Tag = rwAño["AÑO"],
                  Name = "AÑO",
                  ToolTip = "Año del folio existente."
               };
               //Agrega evento al elemento.
               añoItem.MouseDoubleClick += OtroItem_MouseDoubleClick;
               foreach (DataRow rwMes in rwAño.GetChildRows("añoRel"))
               {
                  TreeViewItem mesItem = new TreeViewItem()
                  {
                     Header = rwMes["MES"].ToString(),
                     Tag = rwMes["MES"].ToString(),
                     Name = "MES",
                     ToolTip = "Mes del folio existente."
                  };
                  //Agrega evento al elemento.
                  mesItem.MouseDoubleClick += OtroItem_MouseDoubleClick;
                  foreach (DataRow rwDia in rwMes.GetChildRows("mesRel"))
                  {
                     TreeViewItem diaItem = new TreeViewItem()
                     {
                        Header = rwDia["DÍA"],
                        Tag = rwDia["DÍA"],
                        Name = "DIA",
                        ToolTip = "Día del folio existente."
                     };
                     //Agrega evento al elemento.
                     diaItem.MouseDoubleClick += OtroItem_MouseDoubleClick;
                     foreach (DataRow rwFolio in rwDia.GetChildRows("diaRel"))
                     {
                        TreeViewItem folioItem = new TreeViewItem()
                        {
                           Header = rwFolio["FOLIO"],
                           Tag = rwFolio["FOLIO"],
                           Name = "FOLIO",
                           ToolTip = "Folio existente."
                        };
                        //Agrega evento al elemento.
                        folioItem.MouseDoubleClick += FolioItem_MouseDoubleClick;
                        //Agrega elemento folio a elemento
                        diadiaItem.Items.Add(folioItem);
                     }
                     //Agrega elemento dia a elemento mes
                     mesItem.Items.Add(diaItem);
                  }
                  //Agrega elemento mes a elemento año
                  añoItem.Items.Add(mesItem);
               }
               //Agrega elemento año a elemento árbol
               EventoView.Items.Add(añoItem);
         }
      }
   }
   catch (Exception ex)
   {
      MessageBox.Show("Error:" + Environment.NewLine + ex.ToString(),
 Application.ResourceAssembly.GetName().Name.ToUpper(),
 MessageBoxButton.OK, MessageBoxImage.Error);

   }

Los eventos asignados a cada uno de los elementos del árbol se muestran a continuación, aclarando que las acciones específicas se dejan a las necesidades del lector.

Evento OtroItem_MouseDoubleClick

C#

        private void OtroItem_MouseDoubleClick(object sender, MouseButtonEventArgs e)
        {
            var item = (TreeViewItem)sender;

            if (item.IsSelected)
            {
                // Aquí debes incluir el código de acciones cuando está seleccionado el item que NO REQUIERES
            }

        }

Evento FolioItem_MouseDoubleClick

C#

        private void FolioItem_MouseDoubleClick(object sender, MouseButtonEventArgs e)
        {
            #region Revisiones iniciales

            // Elemento seleccionado.
            var item = (TreeViewItem)sender;

            // Variables de evaluación
            var etiqueta = (string)item.Tag;
            var nombre = item.Name;

            //Borramos contenido previo de la tabla
            if (eventos != null)
            {
                eventos.Clear();
            }

            #endregion

            #region Folio - Evento
            try
            {
                foreach (DataRow numFolio in dsEventos.Tables["FOLIOS"].Rows)
                {
                    if ((etiqueta == numFolio["FOLIO"].ToString()) && (nombre == "FOLIO"))
                    {
                        // Aquí debes colocar el código para las acciones del item que REQUIERES
                        
                        // Copias o importas el renglón de la tabla FOLIOS, con esto tienes el resto del conjunto de datos en la tabla.
                        eventos.ImportRow(numFolio);
                    }
                }
                //Si la tabla tiene reglones puedes usarla en un Grid u otras acciones.
                if (eventos.Rows.Count > 0)
                {
                    //Aquí aseguras el contenido de los renglones.
                }

            }
            catch (Exception ex)
            {
                MessageBox.Show("Error:" + Environment.NewLine + ex.ToString(),
                                Application.ResourceAssembly.GetName().Name.ToUpper(), MessageBoxButton.OK, MessageBoxImage.Error);
            }

            #endregion


        }


Hasta aquí considero que el artículo cumple con el objetivo, llenar un TreeView con varias tablas, codificando en lenguaje C#, sino es así hazlo saber, por favor.



Puntos de interés


Observarás que sin complicar el uso del lenguaje C# y los objetos, codificas todo en C#, uso lo necesario de XAML, pues aún sigo aprendiendo sobre esté. Se muestra como uso necesario de  TreeView, TreeViewItem, DataSet, DataTable, DataRow y si te agrada el código del MessageBox para mostrar en el título del cuadro de diálogo el nombre de tu aplicación, también hazlo saber.

Y recuerda, esté código es para usar en aplicaciones WPF.

Comentarios

Entradas más populares de este blog

Mis Memorias en dotNet