DotNetcr.com
Si deseas hacer un intercambio de links con DotNetcr, escríbenos aquí
Recibe las actualizaciones vía RSS
Te invitamos a unirte en nuestras redes
   http://www.facebook.com/DotNetcr
   http://www.twitter.com/dotnetcr

Llenar un Control TreeView dinámicamente desde una Base de Datos, con ASP.NET 2005

Por h@nz | 12/28/2006 | Visitas 46,157 | Voto 4.00
En este artículo vamos a demostrar como se manipula un control TreeView, cargando los registros dinámicamente desde la base de datos.
Categorías : C#, ASP.NET
Descargar ejemplo LlenarTreeViewASPX.zip

El Control TreeView es un control jerárquico que normalmente se puede llenar desde un archivo XML o de forma manual, pero en ocasiones no se pueden tener los datos en un XML y necesitamos recuperarlos desde la Base de Datos. En este artículo veremos una forma de hacer este trabajo.

Hace poco tuve el siguiente problema, necesitaba crear un menú en un control TreeView para una aplicación web, las opciones del menú varían según el usuario que ingrese al sistema. La idea básica era esa, bien, tener un archivo XML no me servía de nada, las opciones no eran fijas, sino que variaban, si creaba varios archivos XML no los podría controlar si se decide cambiar las opciones puesto que el Admin del sistema podía quitarle opciones a un usuario y mi Servidor Web no me da permiso de escribir archivos, en fin.... el análisis del problema demandaba mucho mas tiempo y más condiciones que solo las nombradas, ¿la solución? llenar el control TreeView directamente desde la Base de Datos.

Empezamos la aplicación creando la siguiente estructura de directorios, tal como muestra la imágen siguiente.


Ahora, no mostraré como crear las tablas ni como relacionarlas porque eso varía dependiendo de cada caso particular, es decir que la lógica de negocios de cada caso es muy diferente, lo que sí haré será mostrar como debería quedar la consulta del Stored Procedure para poder crear la estructura jerárquica del TreeView de forma correcta. Yo he considerado (en resúmen) una tabla de Módulos para los módulos de sistema (ejemplo: Módulo de ventas, módulo de compras, módulo de control de almacenes, etc.), una tabla de Grupos para las Opciones generales de menú (por ejemplo: Menú Pocesos, menú Consultas, menú Configuración, etc.) y una tabla de Opciones que contendrá las opciones del Sistema, es decir, los Casos de Uso o requerimientos funcionales según UML, además, esta tabla de Opciones debe tener un campo en particular que contenga el url de la página web que cada opción llamará (por ejemplo: la opción Generar Factura llamará al a página GenerarFactura.aspx). De este modo, la consulta que haga mi Stored Procedure debe quedar como muestra el siguiente gráfico.



Ahora, en el archivo adjunto que contiene el código solo hay una tabla con esa misma estructura, eso se debe como ya les dije a que los casos son diferentes y si coloco un SP que invoque los registros de la tabla es para dejar en claro que el acceso a datos SIEMPRE se debe realizar a través de un SP y JAMAS directamente desde la página web o alguna clase. En mi caso y solo como ejemplo, he tenido que utilizar aproximadamente 12 tablas para poder generar la consulta resultante, así que las relaciones de su BD las deben ver Uds.

Ahora, lo primero que haremos será crear la Clase que invocará al Stored Procedure desde la Base de Datos. Crearemos una Clase (obviamente en la carpeta App_Code y si no hubiera, creamos esta carpeta primero) llamada Usuario_DAL (el sufijo DAL es por Data Access Layer) y en esta clase escribiremos el siguiente código:

using System;
using System.Data;
using System.Configuration;
using System.Data.SqlClient;

public class Usuario_DAL
{
private int _idUsuario;

public Usuario_DAL(int idUsuario)
{_idUsuario = idUsuario;}

public DataTable leerOpciones()
{
SqlConnection cnn = new SqlConnection(ConfigurationManager.ConnectionStrings["miBaseDatos"].ConnectionString);
SqlCommand cmd = new SqlCommand("uspListarOpcionesUsuario", cnn);

cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("@idUsuario", SqlDbType.Int).Value = _idUsuario;

cnn.Open();
DataTable dtt = new DataTable();
dtt.Load(cmd.ExecuteReader(), LoadOption.OverwriteChanges);
cnn.Close();

return dtt;
}
}


Bien, expliquemos el código. El constructor de esta clase recibe un parámetro llamado idUsuario, que debe ser el código del usuario que ingresa al Sistema y que servirá para hacer los filtros en las tablas para conseguir mostrar solo las opciones de cada usuario. El método leerOpciones como se darán cuenta retorna un objeto DataTable. Se ejecuta el Stored procedure que devuelve la consulta que se vió en el gráfico anterior como un SqlDataReader, esto para cortar la conexión con la Base de Datos en cuanto se ejecute esta acción, así liberamos la conexión con la Base de Datos. Este SqlDataReader se puede convertir fácilmente en un objeto DataTable usando el método Load de la instancia del DataTable, el parámetro LoadOption.OverwriteChanges solo indica que se deben sobreescribir los datos si es que hubiesen en el DataTable, como nuestro DataTable (dtt) esta vacío no nos preocupamos por eso. Cerramos la conexión que utilizó nuestro objeto SqlCommand y devolvemos nuestro DataTable.

Ahora, este proceso solo nos ha servido para poder devolver el DataTable, ahora, falta el proceso que carga las opciones en el TreeView y esto lo haremos en otra clase que vamos a crear (en la misma carpeta App_Code) y le llamaremos Usuario_BLL (el sufijo BLL se debe a Bussiness Logic Layer). En dicha clase escribiremos el siguiente código:

using System;
using System.Data;
using System.Web.UI.WebControls;

public class Usuario_BLL
{
public Usuario_BLL(){}

public void cargarOpcionesUsuario(int idUsuario, TreeView tvw)
{
string grupo = "", modulo = "";

TreeNode nodoG = new TreeNode();
TreeNode nodoM = new TreeNode();

Usuario_DAL user = new Usuario_DAL(idUsuario);
DataTable dtt = user.leerOpciones();

for (int i = 0; i < dtt.Rows.Count; i++)
{
DataRow filaM = dtt.Rows[i];

if (modulo != filaM[1].ToString())
{
grupo = filaM[3].ToString();
nodoG = new TreeNode(grupo, filaM[2].ToString());

modulo = filaM[1].ToString();
nodoM = new TreeNode(modulo, filaM[0].ToString());

nodoG.ChildNodes.Add(new TreeNode(filaM[5].ToString(), filaM[4].ToString(), "", filaM[6].ToString(), "_self"));
nodoM.ChildNodes.Add(nodoG);
tvw.Nodes.Add(nodoM);
}
else
{
if (grupo != filaM[3].ToString())
{
grupo = filaM[3].ToString();
nodoG = new TreeNode(grupo, filaM[2].ToString());
nodoM.ChildNodes.Add(nodoG);
}
nodoG.ChildNodes.Add(new TreeNode(filaM[5].ToString(), filaM[4].ToString(), "", filaM[6].ToString(), "_self"));
}
}
dtt.Dispose();
dtt = null;
}
}


Analicemos. Esta clase no recibe parámetros en su constructor. Posee un único método llamado cargarOpcionesUsuario que recibe dos parámetros, el primero es el código del Usuario por el que haremos el filtro y el segundo es el nombre del control TreeView que llenaremos con las opciones.

Instanciamos un obejto de la clase Usuario_DAL pasandole como dato el id de usuario para que nos devuelva el objeto DataTable que necesitamos para hacer este trabajo.

La lógica para este último paso es bastante sencilla, en realidad se trata de recorrer la tabla e ir agregando las opciones a un nuevo objeto nodo (TreeNode), luego este nodo lo agregaremos como un nodo hijo a un nodo de nivel superior, en este caso el que contendrá los grupos y finalmente este nodo de grupos lo agregaremos a un nodo principal que será el que contenga los módulos. La idea es que por cada cambio en el campo de módulos, agreguemos un nuevo módulo, y por cada cambio en el campo grupos, agreguemos un nuevo grupo, en el caso de las opciones no es necesario hacer esta validación pues cada opción será diferente.

Un objeto TreeNode tiene 5 sobrecargas, yo he utilizado dos de ellas:
* nodoG = new TreeNode(grupo, filaM[2].ToString()); - Esta pide el texto que se mostrará al usuario y el valor que tendrá dicho nodo, en este caso el código del propio grupo.
* nodoG.ChildNodes.Add(new TreeNode(filaM[5].ToString(), filaM[4].ToString(), "", filaM[6].ToString(), "_self")); - Esta segunda forma ademas, pide una imágen para mostrar, que no hemos considerado, un URL para usarlo como Hipervínculo y en último lugar, la ventana de destino del enlace, que será nuestra propia ventana.

Ahora, en una página web agregamos un control TreeView y escribimos el siguiente código:

protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
Usuario_BLL user = new Usuario_BLL();
user.cargarOpcionesUsuario(1, TreeView1);
}
}


Obviamente en el Load de la página. el primer parámetro yo lo he considerado como fijo, pero en el caso de Uds. puede ser una variable de Session, o un dato recuperado del URL o de un form, etc. que será el que se envíe para el filtro en la clase de acceso a datos. El resultado final debe ser el que se muestra en esta imágen.



La recomendación que haría sería que colocanse su TreeView en un MasterPage y a partir de él hiciesen sus páginas. Espero que este artículo les sea de utilidad y que lo apliquen, si le hacen alguna mejora, compartanla!!!

Saludos, hasta el próximo artículo.

Descargar ejemplo LlenarTreeViewASPX.zip
Area de Comentarios
Por royrojas - Fecha: 2006/12/28 05:13 PM
Hanz, gracias por el articulo, esta muy bueno.
Por Anónimo - Fecha: 2007/01/10 06:41 PM
HOLA COMO ESTAN ME GUSTARIA Q MUESTREN UNA CONEXION SQL SERVER CON ASP.NET UN MANTENIMIENTO COMPLETO CON TRES TABLAS PROGRAMADAS EN CAPAS
Por Anónimo - Fecha: 2007/01/26 11:02 AM
Felicitaciones brother, esta muy bueno el artículo sería más excelente si le agregaras algo más como cuando seleccione un item despliegue otra página...
Por h@nz - Fecha: 2007/01/27 09:18 AM
Respecto al mantenimiento completo con ASP .NET con tres tablas en capas, trataré de darme un tiempo para hacerlo y poder publicarlo.

En cuanto a lo de seleccionar un Item y que se despliegue una página se puede conseguir usando los 5 parámetros para agregar un nodo. Ejemplo: Usaría el constructor de 5 parámetros para el Item Almacén si quisiera que al hacerle clic se muestre una pantalla de almacén.

Como ves, no es algo muy complicado de implementar.

Saludos.
Por Anónimo - Fecha: 2007/01/30 02:41 PM
que pasa si al Tree queremos agregar un nivel de profundidad mayor? el modelo no lo soporta

ejemplo:

ventas->procesos->factura->imprimir factura
Por h@nz - Fecha: 2007/01/31 09:54 AM
No amigo, este modelo, planteado para una alternativa con 3 niveles, no soportaría de forma correcta una mayor profundidad, para esos casos se tendría que hacer la modificación correspondiente al ejemplo.

Saludos.
Por Anónimo - Fecha: 2007/03/04 10:50 PM
hola que tal muy buen articulo, te importaria si pones o me mandas el codigo de tu sp ya que no entiendo muy bien la consulta y como es que le asignas el hijo a casa uno de los padres, gracias y saludos, ahernandez@sanimex.com.mx
Por Anónimo - Fecha: 2007/03/27 01:51 AM
El articulo esta super, lastimosamente no lo he podido hacer porque estoy aprendiendo este lenguaje y no puedo agregar la data, tambien queria preguntar si puedo omitir el codigo del usuario
Por Anónimo - Fecha: 2007/04/04 03:15 AM
El articulo esta genial, pero como se podria hacer para añadir a cada hijo por ejemplo 2 links o 2 imagenes si quieres gestionar que desde cada hijo haga 2 acciones diferentes.
Muchas graicas y ole
Por Anónimo - Fecha: 2007/04/13 10:46 AM
Bien Hamz, muy bien.
Por Anónimo - Fecha: 2007/05/31 03:52 PM
Hola. Me llamo Ariadna.
Esta bueno el articulo.
Yo ando buscando como crear un treeview a partir de un archivo XML.
El treeview tiene 5 niveles en ciertos casos, y tiene varios parametros.

Si alguien me puede decir como le hago para crear el arbol poniendole sus parametros.

Mi correo es andaira_nena@hotmail.com
Saludos.
Por Anónimo - Fecha: 2007/07/22 02:21 PM
Excelente... seria bueno que lo tengas tambien en VB, si la tienes porfavor enviamela alfredzaro@hotmail.com. gracias
Por Anónimo - Fecha: 2007/08/02 03:36 PM
Muy buen articulo gracias.
Una duda : cuales son las ventajas y desvetajas entre generar el treeview como esta planteado en este arituclo y hacerlo mediante un archvo xml ?
Por Anónimo - Fecha: 2007/10/01 12:07 PM
ESTA DE LUJO ME GUSTARIA COMPARTIR CONCEPTOS CON USTED ESTOY EN UN NIVEL DE PROGRAMACION MEDIO PERO CONOSCO VARIOS TRUCOS MI CORREO ES nacho_870@hotmail.com
Por Anónimo - Fecha: 2007/10/12 09:12 AM
Usa Nhibernate
Por Anónimo - Fecha: 2007/10/12 09:13 AM
Usa Nhibernate para hascer tus manteniientox en NCapas con Aspx.net 2.0 and Ajax
Por Anónimo - Fecha: 2008/03/31 10:28 AM
Oki, gracias me sirvio de mucho.. Sale vale..
Por Anónimo - Fecha: 2008/06/02 04:54 PM
Puedes hacerlo pero sin ASP tengo un sistema hecho en C# pero es una aplicacion de escritorio.
Necesito llenar el treeview con una BD :S ayudaaaaaaaaa
Por Anónimo - Fecha: 2008/06/06 11:17 AM
hola, se podria hacer este mismo ejemplo pero llenarlo desde una carpeta? que si esta carpeta tiene subcarpetas y archivos los muestre respetando la gerarquia de niveles, los cuales podrian ser N niveles

muy buen articulo!!
Por Anónimo - Fecha: 2008/06/07 11:44 PM
muy buen articulo me ayudo mucho de verdad gracias..!
solo me gustaria saber si se le puede agregar algo como asi como dandole click a uno de los articulos me puede llevar a una pagina nueva..!
Por Anónimo - Fecha: 2008/07/02 02:33 PM
en el momento de ejecutar el programa necesito que el treeview carga datos desde sql server 2005 y si escojo otro tema carguen otros datos datos en el mismo treview en el momento de la ejecucion.

MUY BUENO EL ARTICULO PERO NECESITOESTO PORFAVOR
Por Anónimo - Fecha: 2008/09/08 05:17 PM
hola estoy trabajando en un treeview de 6 niveles y me interesa mucho ver tus tablas y el sp, te lo agradeceria mucho... mmimos@gmail.com
Por Anónimo - Fecha: 2008/10/20 11:52 AM
hola estimado,
estoy haciendo mi tesis y tenia el mismo
problema.
Su codigo lo estoy tratando de usar en un masterpage.
pero al hacer postback pierde el orden de los nodos.
Me explico: solo esta abierto el nodo procesos y haglo clik en emitir factura
y carga otra la pagina factura, pero el nodo al cargar la nueva pagina no queda abierto solo el nodo de procesos, si no que se abren todos.

me puedes ayudar?

ellempen@hotmail.com
Por Anónimo - Fecha: 2008/11/11 09:30 AM
Oye Doctor la Cadena de conexion esta en el archivo web.config
Por Anónimo - Fecha: 2010/01/31 06:23 PM
Excelente
Por Anónimo - Fecha: 2010/08/02 10:15 PM
ya no se puede descargar el ejemplo. Se podrá actualizar este tema?
Por Anónimo - Fecha: 2010/08/23 03:26 PM
te amo me alegraste el dia
Por Anónimo - Fecha: 2010/08/24 10:24 AM
SELECT G.PADRE,PA.NOMBRE AS NOMBREPADRE, G.CODGRUPO AS CODGRUPO,G.NOMBRE as GRUPONOMBRE,P.CODPRODUCTO,P.NOMBRE PRODUCTONOMBRE,P.CODPRODUCTO FROM PRODUCTO P, GRUPO G , GRUPO PA WHERE P.CODGRUPO=G.CODGRUPO AND PA.CODGRUPO=G.PADRE ORDER BY GRUPONOMBRE ASC
Por Anónimo - Fecha: 2010/10/04 05:44 PM
Muy, pero muy bueno
Por Anónimo - Fecha: 2010/10/04 05:44 PM
Muy, pero muy bueno
Por Anónimo - Fecha: 2010/10/22 06:33 AM
muchisimas gracias, me ha servido de mucho
Por Anónimo - Fecha: 2010/11/29 05:17 AM
Excelente, me sirvio de mucho el código que muestras.

Un aporte.

Observen el siguiente link:

http://www.highoncoding.com/Articles/714_Populating_ASP_NET_TreeView_Control_Recursively.aspx

Creo que en este artículo se toman todas las variantes posibles con el TreeView.
Por Anónimo - Fecha: 2010/11/29 05:55 PM
buenisimo
Por Anónimo - Fecha: 2011/02/04 10:03 AM
lo ultimo esta bueno gracias es lo que buscaba :)
Por Anónimo - Fecha: 2011/06/21 11:30 AM
este ejemplo solo sirve para tu ejercicio jejeje ta muy chafa jjejejejejejejej
Por Anónimo - Fecha: 2011/09/12 01:52 PM
Puedo hacer esto en mi aplicacino de excritorio?
Por Anónimo - Fecha: 2012/01/13 01:21 PM
Hola seria genial poder conseguir la data de este ejemplo

http://www.highoncoding.com/Articles/714_Populating_ASP_NET_TreeView_Control_Recursively.aspx

Saludos.
Por Anónimo - Fecha: 2012/02/22 12:16 PM
De este tipo de ayuda deben ser en los programadores no los payaitos que todo se ocultan muy bien me ayudo mucho Saludos.
Por Anónimo - Fecha: 2012/04/26 05:06 AM
There is an option to turn it off, in the pfrcerenees pane. So why should I add yet another option to turn it off?I am trying to remove unused code, even code that is once written needs to be maintained and as nobody is stepping up to help with that, I am going to get rid of unused/unwanted code.
Por Anónimo - Fecha: 2012/04/27 08:45 AM
k2uPbn , [url=http://katlbvteowat.com/]katlbvteowat[/url], [link=http://juphqxvsfrko.com/]juphqxvsfrko[/link], http://iqbrruwypooq.com/
Por Anónimo - Fecha: 2012/04/29 01:45 AM
RUCwIY , [url=http://jdoctwnfroow.com/]jdoctwnfroow[/url], [link=http://hrqkfisotbxz.com/]hrqkfisotbxz[/link], http://qftmpmjvcazp.com/
Por Anónimo - Fecha: 2012/05/19 11:48 AM
genial, criterio y conocimiento son dos grandes virtudes que vos tenes y no dudas en compartirlas
Por Anónimo - Fecha: 2012/07/18 05:30 PM
Pardon me, but I attempted to email you rrdnegiag something on your blog but the given email address didn't work. Is there some other place I could contact you?
Por Anónimo - Fecha: 2012/07/19 03:54 AM
Hi, I am curious how do we acvhiee Horizontal Menu with Horizontal SubMenu using CSS Control Adapter Toolkit? That means when user clicks on the top layer Menu, the SubMenu will be displayed horizontally as well which i can't find any tutorial of how to acvhiee that using CSS Control Adapter Toolkit. Thank you very much.
Por Anónimo - Fecha: 2012/07/19 10:36 AM
jiUKzw , [url=http://xwvvrfdnagsg.com/]xwvvrfdnagsg[/url], [link=http://qpzrjahqsuxx.com/]qpzrjahqsuxx[/link], http://smsivpohcspn.com/
Por Anónimo - Fecha: 2012/07/20 09:28 PM
2rAUxO , [url=http://otrcwyjefnhx.com/]otrcwyjefnhx[/url], [link=http://ulzbimbftfvy.com/]ulzbimbftfvy[/link], http://wgmtibahrehl.com/
Por Anónimo - Fecha: 2012/07/20 10:59 PM
That is the one weakness with our aorappch, in that the cell factory produces a cell that needs to work with every row rather than providing, say, a row index and having the developer return a different cell based on the index or item at that index. However, the reason we didn't do that is because then you'd have to maintain a cache of cells and be notified whenever they are in use or released and manage that pool yourself, which is really complicated.So instead, what you have to do is return a single Cell which is capable of rendering any of the items. If you have, say, 2 different types of cells you want to display, then each Cell has to have 2 different sub-scenegraphs and then choose which one to use as the node based on the current index. SO for example:cellFactory: function() { def cell:ListCell = ListCell { node: bind if (cell.item instanceof Customer) { /* some node to represent it*/ } else if (cell.item instanceof Order) { /* some node to represent the Order */ } }}Or you can just have your custom cell have a variable for each type of sub-scene and lazily create them on demand, and then in the onUpdate callback do the switch there instead of using bind as the above code would create throw away the subscenes pretty frequently.My calculation here was that it was better to provide a very simple cell factory interface rather than force developers to manage a pool of cells manually, with the assumption that there would typically not be a very large number of different renderings in a single list. If you find you have a use case where you really do have a lot of different cell types in a single list, then you could go all the way if you wanted to by doing something like a ListCellComposite subclass of ListCell that would take a sequence of ListCells, or have a ListCellCreator subclass that would have cell-pool functions built into it and a shared HashMap or something of cells.For example, suppose you had 100 different cell types to render. Under the basic API we provide, if the ListView needed 20 cells, it would end up creating 20 cells which would potentially have 100 different sub-scenes and that 2000 different subscenes could be quite taxing on memory and such. Instead, suppose your implementation of the ListCell could refer to a pool of subscenes. When it needs one, it checks it out and assigns it to its node. When it is done with it, it puts it back. In this way you have 20 cells and only another 20 sub-scenes at first. Over time if every variation was used you could get up to all 2000 different subscenes, but it is unlikely, so you would probably save memory. Of course they could be housed in soft references so they are free'd if memory gets tight.Anyway, something to stew on
Ingrese su Comentario
Comentario
Para poder votar debe estar registrado en DotNetcr.com
Solo queda registrado el primer voto enviado
Voto


Últimos Recursos
ibarra
dsevic
Eur
jota
juanjoguardiola
Víctor Carbajal
PER 237
MEX 236
CRI 185
COL 116
ESP 104
ARG 87