Reflection III: Haciendo un menú dinámico

ForTutorial NET

Hoy desde forCode os enseñaremos a poder crear un menú dinámico, con treeview, y a través de reflection lanzar los formularios como si fuesen links. Para ello crearemos una BDD con una tabla para poder cargarlo sin necesidad de tener que tocar el código.

¿Para que es útil?

Un menú dinámico con reflection es útil:

  • Para activar o desactivar formularios, si no queremos que se vea una parte solo tenemos que poner visible a false
  • Si pones nuevos formularios, solo tienes que rellenar esto, no es necesario que toques el código
  • Con un campo mas que se enlace a usuarios/grupos, se podría crear un sistema  de permisos, puedes hacer que el menú sea dinámico por privilegios

Requisitos

Para cargar el menú, utilizaremos una tabla en la BDD, aunque se podría poner en un xml, teniendo en cuenta que es algo que va a variar un determinado número de veces.

La tabla la llamaremos «siMenu» y tendra los siguientes campos:

  • id – un identificador único, autoincremental
  • nombreParaMostrar – el nombre que vera el usuario
  • nombreDll – el nombre del fichero que genera al compilar con extensión .dll
  • nombreFormulario – el nombre del formulario que queremos abrir
  • visible – si se va mostrar
  • idPadre – esto es para evitar hacer 2 tablas, pones el id del padre o si no tiene padre un -1(solo es una manera de hacerlo)

En el nombre de la tabla, el «si», me refiero a sistema, es para tener una organización y no tener tablas de configuración mezclado con los datos de clientes o productos.

También necesitamos 2 proyectos: uno que puede ser del tipo application, donde habrá un formulario que contendrá un control tipo TreeView; y otro proyecto del tipo librería de clases  que contendrá varios formularios.

El primer proyecto, para mi, se llamará mdi y el formulario frmTreeMenu.

El segundo, que contiene varios formularios, para mi se llamara uFrm, y creare 3 formularios frmTest( lo tenia creado del anterior post), frmTransportes y frmAeropuertos.

 

Empezamos

 

Primero hay que obtener los datos de la BDD, haremos una query que nos devuelva todo el contenido en un dataset( Si no sabéis hacer esto comentarlo y haré una entrada dedicada al acceso a datos). Para ello me he creado una función, getAll() que esta en un proyecto que se llama bSistema, la cual apunta a un proyecto que se llama dSistema y este último hace la petición en la BDD. Básicamente, tenemos una función que devuelve todo el contenido de la tabla siMenu de la BDD dentro de un dastaset.

Esto es un ejemplo de la función:

protected DataSet ejecutarQuery(String strQuery)
        {
            DataSet ds = new DataSet();
            SqlCommand cmd = new SqlCommand();
            SqlDataAdapter adapter = new SqlDataAdapter();
            try
            {
                cargarCnx();
                cnx.Open();
                adapter.SelectCommand = new SqlCommand(strQuery, cnx);
                adapter.Fill(ds);
                return ds;

            }
            catch (Exception ex)
            {
                return null;
            }
            finally
            {
                cmd.Dispose();
                adapter.Dispose();
                  cnx.Close();
            }

        }

        public DataSet getAll()
        {
            String strQuery = "Select * from " + strNombreTabla;
            return ejecutarQuery(strQuery);
        }

Después, en el load del frmTreeView, introducimos la llamada a la función ( sobre todo, comprobamos que tiene registros), y hacemos un dsMenu.Table[0].Select(«idPadre = -1»), esto nos devolverá los «Padres», es decir, los contenedores de las opciones de menú(como he comentado, uso el -1 para indicar que es un «Padre». Después llamamos a una función recursiva ( que se va llamando a si misma) para obtener los «hijos» de ese menú, y a su vez estos obtienen sus hijos, … (por si queremos hacer varios niveles de anidamiento).

 

 private void frmTreeMenu_Load(object sender, EventArgs e)
        {
            bSistema.clsBMenu bMenu = new bSistema.clsBMenu();
            DataSet dsMenu = bMenu.getAll();
            GC.SuppressFinalize(bMenu);
            if (Tools.clsVerificaciones.dsTieneRegistros(dsMenu))
            {
                foreach (DataRow drHijo in dsMenu.Tables[0].Select("idPadre = -1"))
                {
                    treeMenu.Nodes.Add(dameNodo(int.Parse(drHijo["id"].ToString()), dsMenu));
                }
            }    

   private TreeNode dameNodo(int intId, DataSet dsMenu)
        {
            TreeNode trPadre = new TreeNode();
            trPadre.Tag = intId; //esto es para después
            trPadre.Text = strNombre; //Sera lo que se muestra
            foreach (DataRow drHijo in dsMenu.Tables[0].Select("idPadre = " + intId))
            {
                trPadre.Nodes.Add(dameNodo(int.Parse(drHijo["id"].ToString()), dsMenu));

            }
            return trPadre;
        }

Para verlo mejor, insertemos varios registros en la tabla del menú.

tablaMenu

Y como resultado nos dará lo siguiente.

menu

Hasta aquí, sería mas que nada la generación del menú dinámico, pero ahora toca añadir reflection. La gracia de montar este esquema, es poder abrir los formularios a raiz de solo añadir un registro en la BDD ( o en un xml). Ahora editaremos el evento onClick o AfterSelect del treeview (el menú), y añadiremos lo siguiente.

int intId = -1;
 //Verificaciones
 if (Tools.clsVerificaciones.dsTieneRegistros(dsMenu) && treeMenu.SelectedNode.Tag != null && int.TryParse(treeMenu.SelectedNode.Tag.ToString(), out intId))
 {
    String strForm = dsMenu.Tables[0].Select(" id = " + intId)[0]["form"].ToString();
    String strDll = dsMenu.Tables[0].Select(" id = " + intId)[0]["dll"].ToString();

    //le indicamos donde esta la dll
    Assembly assCadena = Assembly.LoadFrom(AppDomain.CurrentDomain.BaseDirectory + strDll + ".dll");

    //instanciamos un formulario con el nombre de la dll(sin extensión) "." nombre formulario
    Form frm = ((Form)assCadena.CreateInstance(strDll + "." + strForm, true));
    frm.Show();
 }

Y con esta ultima parte ya podríamos abrir cualquier formulario que quisiéramos.

Para cualquier pregunta dejad un post y sera respondida cuanto antes!