quinta-feira, 29 de julho de 2010

Menu dinâmico buscando itens da base de dados (ASP.Net)

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 UTILIZADAS

Estou 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 EXEMPLO


Eu 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.html

CREATE 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 MENU

Utilizarei 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 MenuItem
MenuItem[] 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 menu
private 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 filhos
private 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.

4 comentários:

Si disse...

Eeee ele voltou! =)

Ainda não tentei criar um menu dinâmico usando esse componente, mas vou fazer uns testes com teu tutorial.

Mas poxa, se não fosse pedir muito, tu poderia disponibilizar um zip com o código, né? Ia ficar ainda melhor!

Vou dar uma estudada no teu código e vou tentar fazer!

Beijão!

Si disse...

Acabei de testar teu código, mas pra mim seria melhor utilizar datatable carregado com os dados do banco, aí adaptei e funcionou certinho.

Dei uma pesquisada agora e se liga no que encontrei:
http://aspalliance.com/822

Maurício Vinicius de O. Santos disse...

E aew doida...heheheh
então, eu utilizei o ADO.Net..mas a logica é a mesma com DataSet tipado ou por DataTable/CommandText

Eu fiz um menu dinâmico em Delphi também. quero ver se posto depois.

Vou ver se compacto o exemplo e deixo o link disponivel no site.

bejao =*

Maurício Vinicius de O. Santos disse...

Ah...acabei de ver o site. Legal...
ele exporta os dados para XML e faz com que o menu acesse esse XML.

Eu não tinha pensado nessa forma. Legal ter uma logica diferente...e acaba sendo até mais simples também.