Carregar menu Dinamico e jogar na tela conforme modelo

18/05/2009

Boa noite

Sou iniciante em Delphi, porem tenho grande conhecimento em Firebird, ja com alguns anos de experiencia.

Eu assisti uma video aula no site da Devmedia, postada pelo Renato Matos, onde ele da um exemplo de carregar um Menu dinamico e tambêm estou seguindo uma video aula do mesmo autor com o assunto "Delphi OOP", dentro destes conceitos, eu gostatia de carregar um Menu de Forma dinamica (buscando de uma tabela no Firebird) baseado na empresa que o usuario irá logar.
Por exemplo:


Tenho 3 tipos de sistema, 1 basico, 1 intermediário e 1 completo, a diferença entre as 3 opções, é a quantida de menu disponivel no sistema.

Exemplificando:

Na opção 1 (Básico) o meu cliente terá apenas o Modúlo Contas a Receber que este contem o cadastro de Clientes, Lançamentos das parcelas e o Modulo Vendas que contem a Tela de Vendas e Cadastros de Produtos.

Na opção 2 (Intermediário) terá alem dos modulos contidos na opção 1, mais os modulos do Contas a Pagar com suas opções, o Modulo de Bancos, com suas opções e assim por diante na opção 3.

O tipo da opção do sistema contratado esta informado na tabela de Empresas no firebird.






Fiz um exemplo simbolico da Tela Inicial e abaixo a tela do Menu conforme eu gostaria de disponibilizar ao cliente.
No exemplo abaixo eu usei o ListView, não sei se é esta a melhor forma, mas pelo menos a disponização é importante para o produto que estou disponibilizando ao meu cliente.
Abaixo a tela do Menu:



Com esta configuração, ficará facil o acesso as opções, pois ele irá escolher o Modulo que se Encontra a Esquerda (Vendas, Caixa, etc) e do lado direito as opções do modulo (Cadastros, Relatórios, Movimentações, etc) que ele ira selecionar e escolher a opção que irá se abrir.



Sendo assim, ele irá escolher o modulo do lado esquerdo e do lado direito, ele irá mostrar a opção disponivel relacionada ao modulo.

Estes dados (Modulos e opções) eu gostaria de carregar conforme a opção informada no cadastro de empresa (tabela firebird), conforme o usuário logar e selecionar a empresa, ele irá trazer os modulos do menu e as opções do modulo.


Espero não ter abusado do meu primeiro chamado :D

Abraços,

Rogério.

Obs: Citei o listview, mas não precisa ser o mesmo, o importante para mim, é a flexibilidade do menu, ja que todas as opções estarão em apenas um banco de dados, disponibilizarei a opção conforme o cliente contratar (Basico, Intermediario e Completo) e a vizualização do mesmo para facilitar o manuseio do cliente, ja que eles reclamam muito dos MainMenus tradicionais.

Irei utilizar o Delphi 2009 e o Firebird 2.1.


Rogério Nascimento

Rogério Nascimento

Curtidas 0

Respostas

Rodrigo Mourão

Rodrigo Mourão

18/05/2009

Olá Rogério, fica tranquilo que não abusou não. Eu gosto de desafios. rsrsrsrs

Bem, não sei se você já assistiu alguma de minhas videos aulas. Eu sou bem prático, mas apesar disso não gosto de sair do padrão.
No seu caso temos duas opções: Ficar aqui durante dias desenvolvendo uma rotina que faça Exatamente o que você quer e com ListView. Digo dias pois a parte de montar os items do ListView e a parte mais simples, o mais complicado e linkar dinamicamente os eventos. O que também não é nada impossível de ser feito.

A segunda opção, e aí entra a praticidade, é criar manualmente todos os itens de menu conforme você quer na tela. E depois de criados apenas criamos uma rotina para exibir aqueles que o seu cliente tem acesso. Simples, prático, fácil e objetivo. O que acha ?

Isso irá lhe atender perfeitamente. Se estiver de acordo sinalize para que possamos dar início.

Abs!!!!!

P.S.: Gostei a ideia do ListView para trabalhar como menu. :-)


Atenciosamente,
Rodrigo Carreiro Mourão
Borland Instructor Certified
Coordenador da Consultoria em Delphi

GOSTEI 0
Rogério Nascimento

Rogério Nascimento

18/05/2009

Fala Rodrigo, tudo na paz?!!!

Rapaz, estou acompanhando a sua video aula sobre Delphi 2007 e estou ansioso para ver o final. A sua proposta didatica e conceituosa sobre o assunto é muito boa.

Podemos fazer da forma que vc sugeriu, vindo de você a sugestão, ta feito.

Vamos lá, pode fazer da forma que você achou melhor.
GOSTEI 0
Rodrigo Mourão

Rodrigo Mourão

18/05/2009

Ok!

Começo a trabalhar nisto hoje mesmo. Em breve posto a solução.

Atenciosamente,
Rodrigo Carreiro Mourão
Borland Instructor Certified
Coordenador da Consultoria em Delphi


 
GOSTEI 0
Rodrigo Mourão

Rodrigo Mourão

18/05/2009

Olá Rogério, segue solução:

Vá no seu Form Principal e crie a seguinte procedure:     procedure CarregaMenu(Nivel: Byte);
A implementação deste metodo fica assim:

procedure TFrmPrincipal.CarregaMenu(Nivel: Byte);
var
  i: Integer;
  J: Integer;
begin
 for I := 0 to Pred(ComponentCount) do
    if Components[I] is TListView then
    begin
      J := Pred(TListView(Components[I]).Items.Count);
      while J >= 0 do
      begin
        if TListView(Components[I]).Items[J].StateIndex > Nivel then
        begin
          TListView(Components[I]).Items.Delete(J);
        end;
        Dec(J);
      end;
    end;
end;

O que faço ai na verdade é o seguinte. Eu varro o form e procuro por componentes TListView, pois pode haver mais de um. Quando localizo um TListView então percorro todos seus items e comparo a propriedade StateIndex com o Nivel passado, ou seja, cada item que você criar nos ListView você terá que alterar a propriedade StateIndex e colocar ali o nivel necessario para ele ser mostrado. Exemplo: 1, 2, 3, 4 Etc.

Assim quando você passar CarregaMenu(2) ele vai mostrar so os Items com StateIndex 1 e 2. No link abaixo te envio um exemplo feito em Delphi. Claro que o nivel voce traz do banco de dados.


http://video.devmedia.com.br/RodrigoCarreiro/Consultoria/MenuDinamico.zip


Abs.

Atenciosamente,
Rodrigo Carreiro Mourão
Borland Instructor Certified
Coordenador da Consultoria em Delphi
GOSTEI 0
Rodrigo Mourão

Rodrigo Mourão

18/05/2009

Olá Rogério !

Há mais alguma dúvida referente a este chamado?
Estamos aguardando para podermos encerrá-lo.

Abs.

Atenciosamente,
Rodrigo Carreiro Mourão
Borland Instructor Certified
Coordenador da Consultoria em Delphi

 
GOSTEI 0
Rogério Nascimento

Rogério Nascimento

18/05/2009

Olá Rodrigo, tudo bem?  Perfeito, é isto mesmo, porem vou precisar da sua ajuda um pouco mais.
Vejamos pela imagem anexada, temos dois Listview, o da Esquerda é o Menu na qual eu irei carregar pela procedure, eu preciso que vc me ajude a chamar e passar o resultado da procedure para o listView da Esquerda.

Eu irei chamar a procedure que carrega os Menus e as Opçoes do Menu, apos o usuario logar no sistema e em seguida escolher a empresa que vai conectar, pois o mesmo pode ter mais de uma empresa no banco. 

Abaixo da imagem, eu mandei 3 tabelas e 1 procedure exemplificando como seria.





Fiz um pequeno exemplo de como seria as tabelas:

CREATE TABLE EMPRESA (
    CODIGO         INTEGER NOT NULL,
    RAZAOSOCIAL    VARCHAR(60),
    PACOTESISTEMA  INTEGER DEFAULT 0
);


/* Fields descriptions */

COMMENT ON COLUMN EMPRESA.PACOTESISTEMA IS
'1 = versao basica
2 = versao intermediaria
3 = versao completa';



INSERT INTO EMPRESA (CODIGO, RAZAOSOCIAL, PACOTESISTEMA) VALUES (1, 'BRUNI QUIMICA LTDA', 1);
INSERT INTO EMPRESA (CODIGO, RAZAOSOCIAL, PACOTESISTEMA) VALUES (20, 'BRUNI QUIMICA LTDA MATRIZ', 2);
INSERT INTO EMPRESA (CODIGO, RAZAOSOCIAL, PACOTESISTEMA) VALUES (50, 'PEQUIMIK SOLVENTES', 3);

COMMIT WORK;



=====================================================================


CREATE TABLE MENU (
    MENU_ID    INTEGER NOT NULL,
    MENUOPCAO  VARCHAR(20) NOT NULL,
    IMAGE       INTEGER DEFAULT 0
);

INSERT INTO MENU (MENU_ID, MENUOPCAO, IMAGE) VALUES (1, 'Contas a Receber', 0);
INSERT INTO MENU (MENU_ID, MENUOPCAO, IMAGE) VALUES (2, 'Contas a Pagar', 1);
INSERT INTO MENU (MENU_ID, MENUOPCAO, IMAGE) VALUES (3, 'Vendas', 2);
INSERT INTO MENU (MENU_ID, MENUOPCAO, IMAGE) VALUES (4, 'PCP', 3);
INSERT INTO MENU (MENU_ID, MENUOPCAO, IMAGE) VALUES (5, 'Compras', 4);
INSERT INTO MENU (MENU_ID, MENUOPCAO, IMAGE) VALUES (6, 'Cheques', 5);
INSERT INTO MENU (MENU_ID, MENUOPCAO, IMAGE) VALUES (7, 'Faturamento', 6);

COMMIT WORK;


=====================================================================

CREATE TABLE MENU_ITENS (
    MENU_ID             INTEGER NOT NULL,
    DESCRICAOITENSMENU  VARCHAR(30) NOT NULL,
    ABAOPCAO            INTEGER NOT NULL,
    IMAGEM              INTEGER DEFAULT 0 ,
    OPCAOCONTRATADA     INTEGER DEFAULT 0
);


COMMENT ON COLUMN MENU_ITENS.ABAOPCAO IS
' SE PERTENCE A CADASTROS, RELATORIOS, MOVIMENTACOES, ETC
 1 = CADASTROS
 2 = MOVIMENTACOES
 3 = RELATORIOS';



CREATE PROCEDURE SP_MENUS (
    cod_empresa integer)
returns (
    menu varchar(30),
    menuindex integer,
    item_menu varchar(30),
    menu_imagem integer,
    item_menu_imagem integer,
    abacontrole integer)
as
begin
   for select menu.menu_id, menu.menuopcao, menu.image,
              menu_itens.abaopcao, menu_itens.descricaoitensmenu, menu_itens.imagem
   from menu
   left join menu_itens on menu.menu_id = menu_itens.menu_id
   where menu_itens.opcaocontratada <= (select empresa.pacotesistema from Empresa Where Empresa.codigo = :cod_empresa)
   into menuindex, menu, menu_imagem,
        abacontrole, item_menu, item_menu_imagem do
  suspend;
end




GOSTEI 0
Rodrigo Mourão

Rodrigo Mourão

18/05/2009

Olá Rogério, é como eu lhe expliquei no início. O que você quer fazer e Montar todos os menus dinamicamente para isso ficar flexivel não é? Mas é uma false Flexibilidade. Repare que na tabela menu vc passa o Caption do Menu e o Index da imagem.

Mas onde está essa imagem?
 - No imageList dentro do Delphi.

Então se você quiser alterar o nome de um menu altera no banco, mas trocar o ícone tem que abrir a aplicação e trocar a imagem no ImageList. Com isso você tem parte da rotina no banco e parte na aplicação. Isso é extremamente ruim.

Você chegou  a rodar o código que lhe falei? Pois você pode ter 1,2,3 quantas listview quiser na aplicação com os itens que ele vai ocultar ou não.

Faça o seguinte em cada item que de cada ListView que você tem ai configura a propriedade StateIndex com 1, 2 ou 3 de acordo com o PACOTESISTEMA da empresa. Ex. menu Contas a Pagar coloca state Index 3 isso fara com que ele so aparceça na 3;

Agora como isso vai funcionar? Simples. Ao se logra você vai no banco e pega o PACOTESISTEMA da empresa e passar para a rotina CarregaMenu(Nivel: Byte); E mais.

Essa listView lateral tambem pode ter seus itens ocultados. Basta configurar a propriedade StateIndex dos itens também.

Rógerio esta é a melhor solução pois montar o menu e tranquilo mas se for feito dinamicamente como vai "injetar" o Onclick.

Por isso acho mais vantajoso deixar todos os itens criados em Design Time, com os onClick's ja programados e apenas ocultar os itens que a empresa não tenha acesso !!

Se quiser manda a sua aplicacao com o banco que implemento a rotina pra voce ver !!!

Abs !!!


GOSTEI 0
Rogério Nascimento

Rogério Nascimento

18/05/2009

Olá Rodrigo, tdo bem?   Você tem razão !!!! Vamos fazer assim, eu vou testar conforme vc me orientou hoje a noite e caso eu tiver alguma duvida te passo ou encerro o chamado, combinado !!!?   Abraços.
GOSTEI 0
Rodrigo Mourão

Rodrigo Mourão

18/05/2009

Olá Amigo !!

Por falta de retonro encerramos o seu chamado. Caso ainda possua alguma dúvida sobre esse assunto, por favor, post a mesma que o consultor voltará a lhe atender e o chamado será reaberto.
Abs !!


GOSTEI 0
Rogério Nascimento

Rogério Nascimento

18/05/2009

Olá Rodrigo, tudo bem? Olha eu aqui novamente !!!

Esbarrei com um problema não previsto na rotina implementada e preciso de sua ajuda.

Conforme a rotina que vc me passou, esta acontecendo o seguinte:

//////////  Rotina implementada //////////////


procedure TfrmPrincipal.CarregaMenu(Nivel: Byte);
var
  i: Integer;
  J: Integer;
begin
// Filtra as opções do ListView conforme o Modulo Relacionado (Rodrigo Carreiro / DevMedia);
 for I := 0 to Pred(ComponentCount) do
    if Components[I] is TListView then
    begin
      J := Pred(TListView(Components[I]).Items.Count);
      while J >= 0 do
      begin
        if TListView(Components[I]).Items[J].StateIndex > Nivel then
        begin
          TListView(Components[I]).Items.Delete(J);
        end;
        Dec(J);
      end;
    end;
end;


O Menu, tem vários modulos a esquerda e do lado direito tem o listview que aparece as opções do modulo selecionado, conforme vc tinha me orientado, adicionei todos os itens no listview e depois conforme clico no modulo, ele apaga os itens diferentes do modulo selecionado, porem acontece o seguinte:

Entrei no modulo Contas a Receber, beleza, os itens que estão no listview, são relacionados a ele, qdo vou para outro modulo, Vendas por exemplo, ja não aparece mais as opções no listview, ja que qdo eu entrei no Contas a Receber, ele apagou o que foi diferente dele, qdo entrei no vendas, ele tbm apagou o que foi diferente dele, porem não carregou as opções relacionadas a ele, assim o listview fica vazio.


Pensei na seguinte situação, como uso Tabsheet, adicionar uma Tab que não ficará visivel ao usuário e nesta adiciono um listView, com todas as opções do sistema ja inseridas nele e conforme eu clicar no menu na tela inicial, ele capta as opçoes relacionadas ao menu pelo State Index (conforme sugerido por você) e adiciona no listview principal. Se esta for a forma mais facil, gostaria que vc me ajudasse a implementar isto, ou, sugerir algo para isto.

Abaixo a tela principal de como esta.



Abraços,

Rogério Nascimento.
GOSTEI 0
Rodrigo Mourão

Rodrigo Mourão

18/05/2009

Olá Rogério,


Bem vamos lá, eu acredito que a melhor maneira de resolver o problema seja restaurar a condição inicial ao mudar o módulo. Por exemplo, quando vc entra no Contas a Pagar vc oculta alguns itens. Quando vc trocar de modulo faça um loop e coloque todos os itens visíveis. Assim volta ao estado original e ai vc poderá chamar a rotina para outro modulo.

Assim é mais simples de implementar.

Abs!!


Atenciosamente,
Rodrigo Carreiro Mourão
Borland Instructor Certified
Coordenador da Consultoria em Delphi
GOSTEI 0
Rogério Nascimento

Rogério Nascimento

18/05/2009

Olá Rodrigo

Como eu faço para restaurar as opções que foram deletadas?
GOSTEI 0
Rodrigo Mourão

Rodrigo Mourão

18/05/2009

Rogério agora você me colocou em check rsrsrsrs!


Não me atentei ao detalhe que estavamos deletando o item. Mas deixar eu ver aqui. Se não tiver a opção visible no Item (E acho que não tem) então vamos tentar reconstruir o controle. Aqui no momento estou sem Delphi mas assim que chegar no cliente eu vou fazer os teste e te enviar.

Abs!!

Atenciosamente,
Rodrigo Carreiro Mourão
Borland Instructor Certified
Coordenador da Consultoria em Delphi
GOSTEI 0
Rogério Nascimento

Rogério Nascimento

18/05/2009

Olá Rodrigo, sem problemas !!!

Você tem alguma sugestão para substituir-mos o listview por outro componente que seja mais facil a manutenção, mas que de a mesma aparencia, pois o meu foco é dar facilidade ao acesso ao menu, ficando bem visivel todas opções ao entrar no modulo, evitando o incoveniente de ficar navegando pelo menu até achar a outra opção.
GOSTEI 0
Rodrigo Mourão

Rodrigo Mourão

18/05/2009

Rogério, não se atreva rsrsrsrsrsrsrs

Este disposição e esta ideia do ListView foi uma grande sacada você está de parabéns.
Eu fiquei enrolado durante o dia mas ja estou fazendo o teste com o repaint e o CreateWND acho que vai nos ajudar. 

Peço que aguardo mais um pouco !!

Abs!!


Atenciosamente,
Rodrigo Carreiro Mourão
Borland Instructor Certified
Coordenador da Consultoria em Delphi
GOSTEI 0
Rogério Nascimento

Rogério Nascimento

18/05/2009

Sem problemas irmão, fique tranquilo.
GOSTEI 0
Rodrigo Mourão

Rodrigo Mourão

18/05/2009

Rapaz a parada não é tão simples não. Bem pra ter noção tentei "repintar" o componente. Não rolou. Entao fui para o RecrateWND. também não rolou. Apelei para destruir e criar de novo e nada. Ai apelei para meu mestre em delphi rsrsrsrsr  Adilson Junior, Master Trainning da Borland e meu Diretor. Ele sugeriu um Stream do Arquivo em Disco para fazer um Undo. Putz maior viagem.

Bem pra resumir, pensei numa coisa mais simples e mais obvia. Fazer com que o seu form principal hoje se torne o segundo form, ou seja, a aplicacao vai ter um form principal sem nada so para conter o segundo form com toda a infra. Assim quando quiser recarregar original destruimos e reciamos o form. Assim ele rele o DFM e coloca tudo como estava no inicio, 0 KM.

Eu ainda não implementei mas vou criar um exemplo aqui e te mando.

Abs.

Atenciosamente,
Rodrigo Carreiro Mourão
Borland Instructor Certified
Coordenador da Consultoria em Delphi
GOSTEI 0
Rogério Nascimento

Rogério Nascimento

18/05/2009

hahahah, sem problemas, fique tranquilo, não tenho pressa, este projeto estou desenvolvendo paralelo aos meus estudos, e ainda virão mais chamados relacionados a ele :D.

Abraços,

Rogério
GOSTEI 0
Rodrigo Mourão

Rodrigo Mourão

18/05/2009

Olá Rogério, demorou mais saiu !!

Seguinte abaixo tem um link com os fontes de um exemplo que criei para te mostrar a ideia. O lance é o seguinte. Eu tenho um form principal que nunca será mostrado, ele servirá so para manter a aplicação rodando. Quando eu crio o form principal eu chamo um metodo AlteraMenu passando o maior nivel ( ai vc troca pro nivel do usuario).

Este metodo ALteraMenu cria um form chamado FrmMenu que tem os PageControls, ListView, etc. Depois ele configura os ListView de acordo com o nivel e por ultimo exibe o formulario.

Quando vc for alterar o nivel eu destruo o formulario FrmMenu e recrio ele. Assim ele volta ao normal, exibindo todos os itens e aplica o novo nivel.

Quando vc cria em sair eu uso o aplication.terminated para encerrar a aplicação.

O caminho é este.

Abs!!!

Segue Link : www.rmfactory.com.br/rmfontes/ListView.rar

Atenciosamente,
Rodrigo Carreiro Mourão
Borland Instructor Certified
Coordenador da Consultoria em Delphi


GOSTEI 0
Rogério Nascimento

Rogério Nascimento

18/05/2009

Fala ae Rodrigo, tudo na paz !!!!

Bom, recebi aqui, executei, tudo beleza, entendi tudo o que vc me explicou certinho, mas na pratica não sei se conseguirei colocar isto em ação, vou dar uma estudada aqui e depois te falo.

Mas ja de antemão, agradeço sua gigantesca atenção.


Abraços.

GOSTEI 0
Rodrigo Mourão

Rodrigo Mourão

18/05/2009

Por nada Rogério !!

Vou colocar o chamado como encerrado, caso surja alguma outra dúvida é so reabrir.

Abs !
GOSTEI 0
Marcio Silva

Marcio Silva

18/05/2009

o arquivo deste link Segue Link : www.rmfactory.com.br/rmfontes/ListView.rar

voce teria ai para mandar.

Outro detalhe, o que deu o projeto, funcionou, tem como compartilhar.


Marcio



Olá Rogério, demorou mais saiu !!

Seguinte abaixo tem um link com os fontes de um exemplo que criei para te mostrar a ideia. O lance é o seguinte. Eu tenho um form principal que nunca será mostrado, ele servirá so para manter a aplicação rodando. Quando eu crio o form principal eu chamo um metodo AlteraMenu passando o maior nivel ( ai vc troca pro nivel do usuario).

Este metodo ALteraMenu cria um form chamado FrmMenu que tem os PageControls, ListView, etc. Depois ele configura os ListView de acordo com o nivel e por ultimo exibe o formulario.

Quando vc for alterar o nivel eu destruo o formulario FrmMenu e recrio ele. Assim ele volta ao normal, exibindo todos os itens e aplica o novo nivel.

Quando vc cria em sair eu uso o aplication.terminated para encerrar a aplicação.

O caminho é este.

Abs!!!

Segue Link : www.rmfactory.com.br/rmfontes/ListView.rar

Atenciosamente,
Rodrigo Carreiro Mourão
Borland Instructor Certified
Coordenador da Consultoria em Delphi






GOSTEI 0
Rogério Nascimento

Rogério Nascimento

18/05/2009

Olá Márcio, tudo bem?

Não tenho mais estes fontes, o processo que eu estava iniciando com o Rodrigo deu certo sim, porém esbarrei em outras limitações, pois queria utilizar o projeto como MDI e a estrutura de navegação seria um pouco diferente e queria também mostrar o menu desabilitado e não oculta-lo, da forma projetada inicial o menu seria ocultado ou quando o usuário clicasse no menu daria a mensagem que não tem permissão.

Relembrando aqui, não vejo dificuldades, você terá que ter uma tabela no banco com os nomes dos menus e os índices das imagens, na confirmação do login do usuário chamaria um método que carregaria todas as opções dos usuários em tempo de execução.

No onclick do listview você passaria o índice do listview para um método que chamaria a classe e criaria o objeto em tempo de execução.

Espero ter ajudado !
GOSTEI 0
Marcio Silva

Marcio Silva

18/05/2009

Tudo bem Rogerio!!, obrigado pela resposta, vou testar suas indicações.


Olá Márcio, tudo bem?

Não tenho mais estes fontes, o processo que eu estava iniciando com o Rodrigo deu certo sim, porém esbarrei em outras limitações, pois queria utilizar o projeto como MDI e a estrutura de navegação seria um pouco diferente e queria também mostrar o menu desabilitado e não oculta-lo, da forma projetada inicial o menu seria ocultado ou quando o usuário clicasse no menu daria a mensagem que não tem permissão.

Relembrando aqui, não vejo dificuldades, você terá que ter uma tabela no banco com os nomes dos menus e os índices das imagens, na confirmação do login do usuário chamaria um método que carregaria todas as opções dos usuários em tempo de execução.

No onclick do listview você passaria o índice do listview para um método que chamaria a classe e criaria o objeto em tempo de execução.

Espero ter ajudado !
GOSTEI 0
POSTAR