Menu dinâmico buscando itens da base de dados (ASP.Net)
Autor: Maurício Vinicius de O. Santos | Publicado: 2010-07-29T20:18:00-07:00

Fala pessoaL!Nossa, faz muito tempo que não posto nada e para falar a verdade, faz tempo que eu nem acesso meu blog. Estou sem tempo e com muitas preocupações. Viajarei para a Australia no fim desse ano e isso esta me deixando ansioso e muito nervoso. Correria total!!Bom, como o blog não é sobre mim mas sim sobre análise e desenvolvimento de sistemas, vamos ao que interessa.Devido a um novo projeto da empresa, comecei a programar em asp.net C# há poucos dias. Precisei fazer um controle de login dos usuários e claro, um menu contendo todos os menus que o usuário tem acesso.Só que esses menus estão cadastrados na base de dados. E agora?Não achei nada na internet, nenhum blog de pessoas que já fizeram isso em asp.net e disponibilizaram.TECNOLOGIAS E FERRAMENTAS UTILIZADASEstou utilizando o modelo entidade relacional Entity Framework da Microsoft, acessando uma base de dados MySQL (the best database ever! ;) )Não irei tratar aqui sobre como fazer os relacionamentos das tabelas ou criação e associação entre as classes do nosso modelo objeto relacional, mas sim como criar o menu para o site e seus respectivos filhos.TABELAS USADAS NO EXEMPLOEu utilizei uma tabela com relacionamento Self One to Many. Para quem não conhece esse tipo de relacionamento, vale a pena dar uma olhada nesse outro post em meu blog:http://devutils.blogspot.com/2008/12/sql-relacionamentos-self-to-self.htmlCREATE DATABASE `exemplomenu`USE `exemplomenu`DROP TABLE IF EXISTS `menus_web`;CREATE TABLE `menus_web` (`idmenus` int(7) unsigned NOT NULL AUTO_INCREMENT,`nome` varchar(45) NOT NULL COMMENT 'nome do item de menu',`ordem` int(7) unsigned DEFAULT NULL COMMENT 'para ordenação dos itens de menu',`pai` int(7) unsigned DEFAULT NULL,`hint` varchar(255) DEFAULT NULL,`bitmap` int(7) DEFAULT NULL COMMENT 'icone do item de menu',`url` varchar(50) DEFAULT NULL,PRIMARY KEY (`idmenus`),KEY `fk_menus_menus1` (`pai`)) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=latin1;/*Data for the table `menus_web` */insert  into `menus_web`(`idmenus`,`nome`,`ordem`,`pai`,`hint`,`bitmap`,`url`) values (1,'Veículos',50,NULL,NULL,NULL,''),(2,'Relatórios',NULL,1,NULL,NULL,''),(3,'Por responsabilidade',NULL,2,NULL,NULL,'/veiculos/relatorios/responsabilidade.aspx'),(4,'Avarias por montadora',NULL,7,NULL,NULL,''),(5,'Avarias por modelo',NULL,7,NULL,NULL,''),(6,'Avarias por peça',NULL,7,NULL,NULL,''),(7,'Gráficos',NULL,1,NULL,NULL,''),(8,'Por responsabilidade e período',NULL,2,NULL,NULL,''),(9,'Algodão',NULL,NULL,NULL,NULL,''),(10,'Relatórios',NULL,9,NULL,NULL,''),(11,'Sair',NULL,NULL,NULL,NULL,'sair.aspx');CRIAÇÃO DO MENUUtilizarei o componente Menu, que se encontra na aba Navigation da ToolBox. Vamos deixar o ID dele como Menu1.Repare que o menu possui uma propriedade chamada MaximumDynamicDisplayLevels. Essa propriedade diz quantos nodes filhos o menu poderá ter.    //Objeto do tipo MenuItem (node de um menu)MenuItem vMenu;//Objeto array, do tipo MenuItemMenuItem[] vMenu2 = new MenuItem[5];int vCont = 0;Primeriamente vamos gerar um procedimento que nos traz todos os menus pais, ou seja, que  contenha o valor pai igual a vazio.private void GerarMenu(){   using (kerpModel.kerpModelEntities dm = new kerpModel.kerpModelEntities())   {       //SELECIONA OS DADOS DO MENU POR MEIO DO LINQ       var consMenu = (from m in dm.menus_web                       where m.pai == null                       select m);        //CORRE OS REGISTROS DO MENU       foreach(kerpModel.menus_web row in consMenu)       {          //Cria um novo node para o menu           vMenu = new MenuItem(row.nome.ToString());           vMenu.NavigateUrl = row.url.ToString();           Menu1.Items.Add(vMenu);                    //Chama o procedimento que verifica se o menu possui filhos, passando o id do menu como parametro           this.GerarMenuFilho(int.Parse(row.idmenus.ToString()));       }   }}//Função que adiciona os filhos do item de menuprivate void GerarMenuFilho(int idpai){   using (kerpModel.kerpModelEntities dm = new kerpModel.kerpModelEntities())   {       //SELECIONA OS DADOS DO MENU, QUE TEVE O ID PASSADO POR PARAMETRO       var consMenu = (from m in dm.menus_web                       where m.pai == idpai                       select m);       //CORRE OS REGISTROS DO MENU       foreach (kerpModel.menus_web row in consMenu)       {                     //Chama a função que verifica se o menu atual também possui filhos           if (totalFilhos(int.Parse(row.idmenus.ToString())) > 0)           {               //Adiciona o menu ao array de menus, para que ele possa ser recuperado posteriormente               vMenu2[vCont] = vMenu;                            //Variavel que conta quantos nodes principais o menu possui               vCont++;               //Adiciona o filho atual ao menu e ao mesmo tempo diz que agora o novo menu é ele mesmo               vMenu.ChildItems.Add(vMenu = new MenuItem(row.nome.ToString(), null, null, row.url.ToString(), null));               //Chama novamente a função, e adiciona seus filhos               this.GerarMenuFilho(int.Parse(row.idmenus.ToString()));           }           else           {              //Caso o item não possua filos, apenas o adiciona como um novo node               vMenu.ChildItems.Add(new MenuItem(row.nome.ToString(), null, null, "~"+row.url.ToString(), null));           }                 }        //Aqui esta o segredinho. Foi para isso que eu criei o contador. Cada vez que o node e seus filhos sao criados, ainda é necessário criar os outros nodes faltantes, mas esses nodes precisam ser inseridos em seus pais. Cada vez que o cursor passa por aqui, dizemos que o menu atual é o menu pai anterior. Dificil de entender, né? para entender, debuge a aplicação.       vCont--;       if(vCont >= 0)           vMenu = vMenu2[vCont];    }} //Função que verifica se o menu possui filhosprivate int totalFilhos(int idmenu){   int count = 0;   using (kerpModel.kerpModelEntities dm = new kerpModel.kerpModelEntities())   {       count = (from m in dm.menus_web                where m.pai == idmenu                select m).Count();   }   return count;}//Para finalizar, no nosso Page_Load nós chamamos o procedimento protected void Page_Load(object sender, EventArgs e){   if (!Page.IsPostBack)   {       this.GerarMenu();   }}Pessoal, peço desculpas por estar ausente do blog. Esta muito dificil de acessá-lo. Mas assim que for possível, responderei a seus comentários. Obrigado.