• Revista PROGRAMAR: Já está disponível a edição #53 da revista programar. Faz já o download aqui!

Knitter

Como adicionar e remover items de um menu em runtime?

9 mensagens neste tópico

Boas,

Numa aplicação que estou a fazer tenho um menu, Janela, que permite listar todas as janelas MDI Child que estão activas, para que esse menu seja dinâmico, sempre que um form é criado é adicionado o seu nome ao menu, eu garanto que apenas um menu de cada tipo é criado, sempre que um form é fechado, teoricamente, o item do menu correspondente é apagado, e aqui está o problema, consigo adicionar items ao menu mas não consigo remover items do menu, ou melhor, ao remover, sei que o item foi removido do menu mas o nome não desaparece.

Código que adiciona o item:

procedure THotelGest.UpdateWindowMenuAdd(Title: String);
var
  item: TMenuItem;
begin
  if not MenuWindow.Enabled then
    MenuWindow.Enabled := True;
  
  item := TMenuItem.Create(MenuWindow);
  item.Caption := Title;
  MenuWindow.Add(item);
//TODO: code to add a new menu item
end;

Código para remover um item, é invocado pelo evento OnClose de cada MDI Child no momento em que este fecha:

procedure THotelGest.UpdateWindowMenuRemove(Title: String);
var
  z: Integer;
begin
  z := 0;
  while (z < MenuWindow.Count) and (MenuWindow.Items[z].Caption <> Title) do
    z := z + 1;

  if z <> MenuWindow.Count then
  begin
    MenuWindow.Delete(z);
    if MenuWindow.Count = 0 then
      MenuWindow.Enabled := False;
  end;
  {* if z <> MenuWindow.Count then
  begin
    MenuWindow.Remove(MenuWindow.Items[z]);
    if MenuWindow.Count = 0 then
      MenuWindow.Enabled := False;
  end;*}
end;

Como podem ver, experimentei remover pela referência e pelo índice, nada feito. Pelos debugs que fiz sei que os métodos são invocados correctamente, que o item a remover é encontrado e que é removido dos Items do MenuWindow, no entanto o texto mantém-se. Digo que o item é removido porque, caso só exista um item, isto é, se apenas existir um MDI Child, e se esse form for fechado, a condição MenuWindow.Count = 0 é verdadeira e o MenuWindow é tornado inactivo. No entanto se não for o último, a propriedade MenuWindow.Count é subtraida um valor, e o item é removido, mas o menu não é actualizado mostrando o nome de um form que não existe e um item que já não faz parte do array Items.

Existe alguma função/procedimento que actualize o aspecto do menu? Pelo que me parece é um problema de actualização de IU mas não sei como verificar se é mesmo disso ou se tenho outro erro.

Obrigado.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Delphi, que saudades... Já lá vão uns anos valentes mas a ver se te consigo ajudar, por isso aqui vão os meus "dois cêntimos": creio que o problema reside no facto de eliminares o item da lista mas o objecto continuar a persistir em memória.

Antes de fazeres MenuWindow.Delete(z); recupera o objecto para uma variável e vê lá se ele não continua em memória depois de fazeres o delete.

É bem provável que tenhas de eliminar o objecto da memória.

Como disse, já lá vão uns anos, mas espero que tenha ajudado...

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Olha o M6... andavas um pouquito desaparecido :(

Bem experimentei obter a referência para o objecto e só depois remover do menu, o resultado é o mesmo. Mas de momento tenho outro problema no código, que já estava à espera que aparecesse. Estou a usar a Caption do menu como identificador e isso não serve visto que estou a colocar esse texto no código e a comparar com o que o delphi me envia, isto é, de um lado tenho a string hardcoded, de outro tenho a caption enviada pelo delphi, e são diferentes, a do delphi tem um & para fazer o atalho do menu. Terei de adicionar um identificador único a cada form, embora ainda não saiba como.

Mas ao executar a linha MenuWindow.Delete(z); eu sei que estou a remover um elemento de um vector, ou pelo menos é assim que penso, não estando a usar a referência para o objecto em mais lado nenhum.... hum, espera lá, o delphi tem garbage collector?....

Mas voltando ao ponto, porque raio, removendo eu uma referencia da lista de objecto, continua a ser mostrado o nome. Eu sei que o MenuWindow.Count mostra o número correcto de elementos, quer adicione, quer remova, o que acontace é que, graficamente, o raio do menu continua a ter objectos a mais. Não há método nenhum para forçar a interface a ser redesenhada?

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Olha o M6... andavas um pouquito desaparecido :D

Muito trabalho e depois... férias! :)

Bem experimentei obter a referência para o objecto e só depois remover do menu, o resultado é o mesmo.

Estranho... Verifica se o item não tem um .parent e se tem coloca-o a null/nil.

Mas de momento tenho outro problema no código, que já estava à espera que aparecesse. Estou a usar a Caption do menu como identificador e isso não serve visto que estou a colocar esse texto no código e a comparar com o que o delphi me envia, isto é, de um lado tenho a string hardcoded, de outro tenho a caption enviada pelo delphi, e são diferentes, a do delphi tem um & para fazer o atalho do menu. Terei de adicionar um identificador único a cada form, embora ainda não saiba como.

Fácil. Usa o .tag para lhe dares um id único. :(

Mas ao executar a linha MenuWindow.Delete(z); eu sei que estou a remover um elemento de um vector, ou pelo menos é assim que penso, não estando a usar a referência para o objecto em mais lado nenhum.... hum, espera lá, o delphi tem garbage collector?....

GC? :D Querias...

Por isso é que tens de ser tu a eliminar as referências. :P

Mas voltando ao ponto, porque raio, removendo eu uma referencia da lista de objecto, continua a ser mostrado o nome. Eu sei que o MenuWindow.Count mostra o número correcto de elementos, quer adicione, quer remova, o que acontace é que, graficamente, o raio do menu continua a ter objectos a mais. Não há método nenhum para forçar a interface a ser redesenhada?

Não me parece que o problema esteja no desejo do menu...

O que acontece se seleccionares a opção que acabaste de eliminar?

Assim de repente, e sem saber mais nada, continuo a apostar que o objecto continua em memória e de alguma forma ligado ao menu. Depois de obteres a referência e de fazeres o .delete, faz .free à referência que obtiveste.

Edit: deixo-te aqui um link a um artigo que escrevi há uns anos atrás, pode ser que ajude: http://www.delphi3000.com/articles/article_2896.asp?SK=

Já agora, não sei se conheces, mas o Delphi 3000 era bastante bom, usava-o bastante, dá uma vista de olhos, pode ser que ajude.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Estranho... Verifica se o item não tem um .parent e se tem coloca-o a null/nil.

Não sei se era à propriedade Parent que te referias, mas se for, a propriedade é apenas de leitura, não sei como a colocar a null.

Fácil. Usa o .tag para lhe dares um id único. :(

Já estava a alterar o código para usar essa propriedade. Afinal sempre tem alguma utilidade :D

GC? :D Querias...

Por isso é que tens de ser tu a eliminar as referências. :P

Sim queria, se não fosse pedir muito... ok, remover à mão, se assim tiver de ser.....

O que acontece se seleccionares a opção que acabaste de eliminar?

Assim de repente, e sem saber mais nada, continuo a apostar que o objecto continua em memória e de alguma forma ligado ao menu. Depois de obteres a referência e de fazeres o .delete, faz .free à referência que obtiveste.

Então, depois de algumas alterações passo a ter:

Um novo método que irá ser responsável pelo evento OnClik dos menus, a acção é sempre a mesma, ver qual o MDI Child que corresponde ao menu e mostrá-lo.

{** 
* Generic event to show the caller form.
*} 
procedure THotelGest.WindowsMenuItemDoAction(Sender: TObject);
begin
//TODO: find the form that relates to this menu and show it. The form must exist.
  MessageDlg('Menu carregado', mtConfirmation, [mbOK], 0);
end;

Ao procedimento que adiciona items ao menu foi colocada a linha:

item.OnClick := WindowsMenuItemDoAction;

desta forma o item passa a ter uma acção.

A função que remove os items do menu passou a:

{**
* Updates the window menu removing a new open window.
*}
procedure THotelGest.UpdateWindowMenuRemove(ID: Integer);
var
  z: Integer;
  deletedItem: TMenuItem;
begin
  z := 0;
  while (z < MenuWindow.Count) and (MenuWindow.Items[z].Tag <> ID) do
    z := z + 1;

  if z <> MenuWindow.Count then
  begin
    deletedItem := MenuWindow.Items[z];
    MenuWindow.Delete(z);
    deletedItem.Free;
    if MenuWindow.Count = 0 then
      MenuWindow.Enabled := False;
  end;
end;

Resultado, confirma-se que o objecto ainda existe.

Passos usados para debug:

  • Adicionar um break point no ciclo que permite encontrar o form baseado na tag;
  • Correr a aplicação adicioando dois forms MDI Child;
  • Neste momento o menu mostra dois items, cada um representa um form aberto anteriormente;
  • Fechar um dos forms activando o break point;
  • Verifico que o ciclo é percorrido até o form a fechar ser encontrado, a propriedade Count mostra dois items. Apos ser removido a propriedade Count mostra 1 item, como devia de mostrar.
  • A função termina, a quantidade de items que o vector tem, dada pela propriedade Count, é um, está correcto, mas o menu mantém-se inalterado.
  • Ao clicar no item, aparece a mensagem, o dito item ainda existe em memória e continua ligado ao menu.
  • Ir à máquina dos chocolates insultando o delphi e a borland, pelo meio pensar no que raio pode estar a acontecer e porque não estou também de férias e tenho este trabalho para entregar?!, Devia era ir jogar WOW e enfiar o Turbo Delphi Explorer num qualquer orificio do prof de DA............... hum.... kit-kat.. yap uma pausa para kit-kat.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Knitter, estás quase lá, falta-te um bocadinho assim [ ]! :D

Se o objecto existe, então remove-o da memória. :( E não, o Delphi não tem GC, é uma linguagem à "macho"! :D

Vê lá se não é isto que tu queres: http://www.ramosdainformatica.com.br/art_recentes01.php?CDA=139 http://www.ramosdainformatica.com.br/art_recentes01.php?CDA=325 :P

PS: o Parent é uma propriedade só de leitura??? Acho estranho...

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Vou ver esse textos.

A propriedade Parente do objecto TMenuItem é de leitura, pelo menos é o que me diz o compilador quando tento atribuir a item.Parent o valor nill

Mas remover o objecto da memória não é só invocar o método Free? Estou a invocar o método mas o raio do item está lá na mesma....

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Et Voilá!

E onde estava o erro? No programador claro! E que mania que têm estes progamadores de dizer mal das linguagens de das tecnologias e dos professores...

Bem com tantas mudanças alterei uma linha de código que era responsável por colocar o identificador único nos menus, resultado como era eu que estava a testar e abria sempre os mesmos forms, um deles com a tag = 0 o ciclo funcionava, mal mas funcionava, mas a referência era errada e não estava a remover o item.

Já está a funcionar correctamente, pelo menos assim parece.

Obrigado.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

De nada. Ainda bem que pude ser útil. :(

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Crie uma conta ou ligue-se para comentar

Só membros podem comentar

Criar nova conta

Registe para ter uma conta na nossa comunidade. É fácil!


Registar nova conta

Entra

Já tem conta? Inicie sessão aqui.


Entrar Agora