TPopUpMenu com menuitens repetidos

Delphi

20/10/2008

pessoal, estou criando um descendente de TPopUpMenu que já venha com um item dentro, que eu uso muito.

Está funcionando beleza, porem se eu recorto e colo o menu, o item dele duplica. Se eu recorto e colo varias vezes, ele vai duplicando, duplicando .... e a aplicação não roda por causa dos componentes com nome repetido.


o que eu faço?


valew!


Vitor Rubio

Vitor Rubio

Curtidas 0

Respostas

Nerdex

Nerdex

20/10/2008

[quote:b605bc86b9=´Cabulosamente vitor^_^´]o que eu faço?[/quote:b605bc86b9]
[list:b605bc86b9][list:b605bc86b9]... faz um if[/list:u:b605bc86b9][/list:u:b605bc86b9]
[list:b605bc86b9][list:b605bc86b9] :roll: [/list:u:b605bc86b9][/list:u:b605bc86b9]


GOSTEI 0
Vitor Rubio

Vitor Rubio

20/10/2008

um if onde? em que metodo?

não importa onde eu faça, ele sempre copia, isso porque:

no momento que eu colo o componente ele instancia o mesmo, executando seu constructor, mas logo depois ele copia os itens que estão como texto na area de transferencia e eu não sei como resolver esse problema.


GOSTEI 0
Discorpio

Discorpio

20/10/2008

Boa tarde Vitor^_^.

Quando voce cria um componente personalizado, mesmo que este herde de outro componente nativo do Delphi, como é o caso do TPopUpMenu, jamais voce deve copiá-lo com itens inseridos.

Se voce já o criou com itens inseridos, isto apenas lhe convém quando voce vai utilizar este componente repetidas vezes em diversos formulários em que o TPopUpMenu utiliza os mesmos itens para todos, ou seja, você o criou um componente TPopUpMenu com itens padrão para todos os formulários, tais como: Inserir, Editar, Excluir, Salvar e etc.

Muito embora, voce definiu a classe do componente com esses itens padrões, cada instância do seu componente TPopUpMenu vai agir independetemente uma das outras, ou seja, desde que ele seja instanciado em formulários diferentes. Entretanto, para que ele tenha o mesmo efeito no mesmo formulário, os nomes dos Itens jamais poderão estar engessados, ou seja, com o mesmo nome, isto porque o Delphi precisa diferenciar qual evento OnClick invocar e qual MenuItem referenciar, pois o MenuItem também é um objeto, ou seja, uma classe.

Se voce pretende utilizar menus popups com itens padrões, utilize-o em formulários diferentes. Já para o mesmo formulário então utilize componentes PopUps que possuam itens com nomes diferentes.


GOSTEI 0
Vitor Rubio

Vitor Rubio

20/10/2008

Boa tarde Vitor^_^. Quando voce cria um componente personalizado, mesmo que este herde de outro componente nativo do Delphi, como é o caso do TPopUpMenu, jamais voce deve copiá-lo com itens inseridos. Se voce já o criou com itens inseridos, isto apenas lhe convém quando voce vai utilizar este componente repetidas vezes em diversos formulários em que o TPopUpMenu utiliza os mesmos itens para todos, ou seja, você o criou um componente TPopUpMenu com itens padrão para todos os formulários, tais como: Inserir, Editar, Excluir, Salvar e etc. Muito embora, voce definiu a classe do componente com esses itens padrões, cada instância do seu componente TPopUpMenu vai agir independetemente uma das outras, ou seja, desde que ele seja instanciado em formulários diferentes. Entretanto, para que ele tenha o mesmo efeito no mesmo formulário, os nomes dos Itens jamais poderão estar engessados, ou seja, com o mesmo nome, isto porque o Delphi precisa diferenciar qual evento OnClick invocar e qual MenuItem referenciar, pois o MenuItem também é um objeto, ou seja, uma classe. Se voce pretende utilizar menus popups com itens padrões, utilize-o em formulários diferentes. Já para o mesmo formulário então utilize componentes PopUps que possuam itens com nomes diferentes.



Bom dia, Discorpio

Sim, eu já sabia de tudo isso, e criei um componente filho de tpopupmenu que já venha com 3 itens que eu uso muito frequentemente. Se eu colocar 2 na mesma form, ligados a componentes diferentes não tem problema, pois os itens são gerados com nomes diferentes. Eu concateno um nome ´engessado´ com o nome do proprio componente. Se o componente é criado como popup1, os menus serão popup1_x, popup1_y, popup1_z.

estou montando esse menu para criar funções de copiar/colar em dbgrids.

o problema está ocorrendo na hora da escrita/leitura dos elementos do stream de DFM. Por exemplo, quando eu recorto o componente ele é deletado da form, certo? porem quando eu colo, ele recria o componente, assim como acontece com qualquer outro, popups, edits, seja o que for.
no momento da colagem o componente não existe e é criado, e executando-se oconstructor do meu menu ele já adiciona os itens padrão que eu criei. o problema é que logo depois disso ele verifica que já tem itens no stream DFM que está na área de transferencia, e recria esses itens, tal qual eu recortei, como ultimo passo da colagem.

esses itens entram como novas referencias, porem apontando para os mesmos objetos já instanciados. se eu deletar um, o objeto será destruido com free e qualquer alteração nesses itens resultará em um access violation.


GOSTEI 0
Vitor Rubio

Vitor Rubio

20/10/2008

se eu programar para criar os itens em tempo de execução e nunca em tempo de design dá certo, porem o delphi poe os meus itens em primeiro lugar, e os que eu adicionei em design por ultimo, mas eu queria por os itens padrão em ultimo lugar e os customizados primeiro.

Algum metodo que o delphi executa que eu não sei qual é, na hora que cria o componente ele executa, num determinado momento, a leitura do dfm.

precisava saber que método é esse.


GOSTEI 0
Discorpio

Discorpio

20/10/2008

Boa tarde vitor^_^.

Vamos por etapa:

1º) Voce pode mudar a ordem do itens do seu componente PopupMenu dinamicamente, e colocar os componentes previamente definidos por ele por último sim, sabe como ? Basta voce indicar o ItemIndex na hora da criação dinâmica começando com o Zero (0). Mas como se já existe um MenuItem lá dentro previamente configurado com esse índice :?: Simples, quando voce acrescenta um MenuItem ao seu PopupMenu, e indica o índice como Zero(0), ele automaticamente reorganiza os Itens do seu PopupMenu. Vamos ao teste:

procedure TForm1.FormCreate(Sender: TObject);
var MenuItem: TMenuItem;
begin

  MenuItem := TMenuItem.Create(PopupMenu1);
  
  MenuItem.Caption := ´Outro Item´;
  MenuItem.Name := ´PopupMenu1_A´;
  PopupMenu1.Items.Insert(0,MenuItem);
   
  MenuItem.Caption := ´Mais um Item´;
  MenuItem.Name := ´PopupMenu1_B´;
  PopupMenu1.Items.Insert(1,MenuItem);

  MenuItem.Free;
  
end;


Agora existe outro detalhe que eu me esqueci de te passar, é que voce pode desviar a execução do evento OnClick de todos os ítens do seu PopupMenu para uma única procedure, fazendo com que voce não precise se preocupar com o nome dos ítens, isto porque quando voce não define o nome dos ítens lá dentro do se componente, o Delphi automaticamente o define como MenuItem1, MenuItem2, ..... Então voce pode fazer o desvio da procedure assim:

procedure TForm1.FormCreate(Sender: TObject);
var MenuItem: TMenuItem;
begin

  MenuItem := TMenuItem.Create(PopupMenu1);
  
  MenuItem.Caption := ´Outro Item´;
  MenuItem.Tag := 0;
  MenuItem.OnClick := MenuCapturaClick;
  PopupMenu1.Items.Insert(0,MenuItem);
   
  MenuItem.Caption := ´Mais um Item´;
  MenuItem.Tag := 1;
  MenuItem.OnClick := MenuCapturaClick;
  PopupMenu1.Items.Insert(1,MenuItem);

  MenuItem.Free;
  
end;

procedure TForm1.MenuCapturaClick(Sender: TObject);
begin
   case TMenuItem(Sender).Tag of
       0: begin
               ....
               .... 
           end;
       1: begin
               ....
               ....
           end;
   end;
end;


Repare que eu além de definir o ItemIndex dentro da posição do ítem de menu na criação dinâmica, também redefini as propriedades Tags, e é para isso que elas servem. Incialmente por default essa propriedades são configurados com o valor zero (0), entretanto eles servem para identificar um objeto, por acasião de um casting, ou na passagem de parâmetros. Só não se esqueça de declarar a procedure MenuCapturaClick, podendo ser em Private ou Public, tanto faz, desde que voce a declare com o parâmetro Sender: TObject, pois ela será um ponteiro de Evento.

2º) Quanto ao método de reescrever o Stream DFM, vou passá-lo a você agora, entretanto devo alertá-lo que tome cuidade quando reescrever componentes através do arquivo DFM, para que voce não faça modificações indesejadas. Ai vai:


function TForm1.ComponentToString(Component: TComponent): string;
var
  BinStream:TMemoryStream;
  StrStream: TStringStream;
  s: string;
begin
  BinStream := TMemoryStream.Create;
  try
    StrStream := TStringStream.Create(s);
    try
      BinStream.WriteComponent(Component);
      BinStream.Seek(0, soFromBeginning);
      ObjectBinaryToText(BinStream, StrStream);
      StrStream.Seek(0, soFromBeginning);
      Result:= StrStream.DataString;
    finally
      StrStream.Free;
    end;
  finally
    BinStream.Free
  end;
end;

function TForm1.StringToComponent(Value: string): TComponent;
var
  StrStream:TStringStream;
  BinStream: TMemoryStream;
begin
  StrStream := TStringStream.Create(Value);
  try
    BinStream := TMemoryStream.Create;
    try
      ObjectTextToBinary(StrStream, BinStream);
      BinStream.Seek(0, soFromBeginning);
      Result := BinStream.ReadComponent(nil);
    finally
      BinStream.Free;
    end;
  finally
    StrStream.Free;
  end;
end;



GOSTEI 0
Vitor Rubio

Vitor Rubio

20/10/2008

Amigo, muito obrigado, aprendi algumas coisas novas legais com o código que você mandou, mas acho que você entendeu errado, vou ver se consigo ser mais claro.

o meu componente cria 3 itens de menu logo no constructor dele, até aí sem problemas

Agora existe outro detalhe que eu me esqueci de te passar, é que voce pode desviar a execução do evento OnClick de todos os ítens do seu PopupMenu para uma única procedure, fazendo com que voce não precise se preocupar com o nome dos ítens, isto porque quando voce não define o nome dos ítens lá dentro do se componente, o Delphi automaticamente o define como MenuItem1, MenuItem2, ..... Então voce pode fazer o desvio da procedure assim:


isso aí eu tambem fiz. Criei 3procedures com o parametro sender as Tobject, cada uma com sua utilidade e atribui a cada item que eu criei do menu.


2º) Quanto ao método de reescrever o Stream DFM, vou passá-lo a você agora, entretanto devo alertá-lo que tome cuidade quando reescrever componentes através do arquivo DFM, para que voce não faça modificações indesejadas. Ai vai:


Achei muito interessante esse código, mas ele não resolve, eu acho, o meu problema, veja: meu componente funciona perfeitamente em runtime, o problema é em design - time, na hora de interagir com a IDE do delphi. Veja; se eu coloco meu popup menu na form, beleza, ele cria os itens certinho, mas se eu recortar e colar ele na form ele duplica os itens, porque: quando cola ele na form ele executa o constructor em design-time, o que é normal, e cria os itens programados para criar, mas depois ele recria os outros itens iguais aos primeiros que estavam na area de transferencia e são criados depois da criação do componente.


eu solucionei a questão não mostrando os itens em design-time, apenas em runtime, mas isso é ruim porque o programador não pode ver nem manipular esses primeiros itens padrão.

mesmo assim obrigado, você me deu várias ´luzes´

vou postar o codigo do componente aqui quado estiver pronto.


GOSTEI 0
POSTAR