Como adicionar e remover items de um menu em runtime? - Delphi/Lazarus - Portugal-a-Programar
Como adicionar e remover items de um menu em runtime?
Por Knitter, 26 de Agosto de 2007 em Delphi/Lazarus
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);
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
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);
while (z < MenuWindow.Count) and (MenuWindow.Items[z].Caption <> Title) do
if z <> MenuWindow.Count then
MenuWindow.Delete(z);
if MenuWindow.Count = 0 then
MenuWindow.Enabled := False;
{* if z <> MenuWindow.Count then
MenuWindow.Remove(MenuWindow.Items[z]);
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.
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...
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?
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.
GC? Querias...
Por isso é que tens de ser tu a eliminar as referências.
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.
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.
Já estava a alterar o código para usar essa propriedade. Afinal sempre tem alguma utilidade
Sim queria, se não fosse pedir muito... ok, remover à mão, se assim tiver de ser.....
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);
//TODO: find the form that relates to this menu and show it. The form must exist.
MessageDlg('Menu carregado', mtConfirmation, [mbOK], 0);
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);
deletedItem: TMenuItem;
while (z < MenuWindow.Count) and (MenuWindow.Items[z].Tag <> ID) do
deletedItem := MenuWindow.Items[z];
deletedItem.Free;
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.
Knitter, estás quase lá, falta-te um bocadinho assim [ ]!
Se o objecto existe, então remove-o da memória. E não, o Delphi não tem GC, é uma linguagem à "macho"!
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
PS: o Parent é uma propriedade só de leitura??? Acho estranho...
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....
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.