Jump to content

Como adicionar e remover items de um menu em runtime?


Knitter
 Share

Recommended Posts

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.

Link to comment
Share on other 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...

10 REM Generation 48K!
20 INPUT "URL:", A$
30 IF A$(1 TO 4) = "HTTP" THEN PRINT "400 Bad Request": GOTO 50
40 PRINT "404 Not Found"
50 PRINT "./M6 @ Portugal a Programar."

 

Link to comment
Share on other 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?

Link to comment
Share on other sites

Olha o M6... andavas um pouquito desaparecido 😄

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? 😄 Querias...

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

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.

10 REM Generation 48K!
20 INPUT "URL:", A$
30 IF A$(1 TO 4) = "HTTP" THEN PRINT "400 Bad Request": GOTO 50
40 PRINT "404 Not Found"
50 PRINT "./M6 @ Portugal a Programar."

 

Link to comment
Share on other 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 😄

GC? 😄 Querias...

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

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.
Link to comment
Share on other sites

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

10 REM Generation 48K!
20 INPUT "URL:", A$
30 IF A$(1 TO 4) = "HTTP" THEN PRINT "400 Bad Request": GOTO 50
40 PRINT "404 Not Found"
50 PRINT "./M6 @ Portugal a Programar."

 

Link to comment
Share on other 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....

Link to comment
Share on other 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.

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

×
×
  • Create New...

Important Information

By using this site you accept our Terms of Use and Privacy Policy. We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.