Ir para conteúdo
Fórum Script Brasil
  • 0

Lisbox Em Tempo De Execução


Paulo Nobre

Pergunta

Estou tentando criar um ListBox em tempo de execução.

Estou fazendo,no OnClick de um image,

var

lstAtalhos:TListBox;

memo:string//Esta variável vai pegar o conteúdo do listbox

begin

...

lstAtalhos:=TListBox.create(self);

...

Memo:=lstAtalhos.Items.text;

...

end;

Compila sem erro.

Quando clico no image a aprece a seguinte mensagem de erro:

EInvalidOperation with mensage "Control" has not parent Window.

O que está errado?

Link para o comentário
Compartilhar em outros sites

21 respostass a esta questão

Posts Recomendados

  • 0

Para criar qualquer componente em tempo de execução->

procedure TForm1.Image1Click(Sender : TObject);
var
  LB : TListBox;
begin
  LB:=TListBox.Create(Self);
  LB.Name:='ListBox1';
  LB.Parent:=Self;
  .
  .
  .
end;

Link para o comentário
Compartilhar em outros sites

  • 0

Thales,

Acabei descobrindo que deveria colocar

LB.Parent:=Self;

Você poderia me explixar o significado deste código(a linha acima apenas)?

A destruição dele é automatica ao fechar o form?

É realmente necessário colocar LB.Name:='ListBox1';

Pois estou usando sem e não estou tendo nenhum problema.

Estou fazendo uma gambiarrazinha, pois trago do registro para este listbox invisível e abro no word para o usuário formatar e imprimir.

Link para o comentário
Compartilhar em outros sites

  • 0
LB.Parent:=Self;

Você poderia me explixar o significado deste código(a linha acima apenas)?

É o seguinte, somente derivados de TWinControl, como Forms, Panels e GroupBoxes podem ser parent de outros controles. Um controle precisa de um parent para aparecer. Assim que é atribuido um parent a um controle ele pega o Device Context do parent para se desenhar na superfície dele. Device Context é um Handle que pode ser colocado num canvas para que se possa desenhar em uma janela ( Canvas.Handle:=GetDc(Form1.handle); ). Para isso os derivados de TWinControl tem uma propriedades Controls, que é um array de Handles dos seus filhos, e uma propriedade ControlCount que é um inteiro que informa quantos filhos o controle tem. É assim que o RadioButton desliga seus irmãos quando é clicado->

for i:=0 to parent.ControlCount-1 do
  if (parent.Controls[i] is TRadioButton) and (parent.Controls[i] <> self) then 
  parent.Controls[i].checked:=false;

E é por isso que os que estão dentro de um GroupBox são isolados do demais, porque o parent deles é o GroupBox e não o form.

A destruição dele é automatica ao fechar o form?

Sim, o parent é responsável por destruir todos os controles em sua lista Controls.

É realmente necessário colocar LB.Name:='ListBox1';

No seu caso a propriedade name não faz diferença já que você vai fazer referencia apenas pelo identificador.

Estou fazendo uma gambiarrazinha, pois trago do registro para este listbox invisível e abro no word para o usuário formatar e imprimir.

Se o ListBox é invisível porque usar ele então? Não é melhor usar um TStrings?

Link para o comentário
Compartilhar em outros sites

  • 0

Thales,

Muito Interessante a explicação, principalmente os detalhes do groupbox.

Se o ListBox é invisível porque usar ele então? Não é melhor usar um TStrings?

Realmente, Thales, estava bobeando inteiramente.

Valeu!!

Link para o comentário
Compartilhar em outros sites

  • 0
Colega, acho que você se enganou. O Parent não destroi nada, quem destroi é o Owner e se não tiver Owner, você tem que destruir na mão com Free.

Opa, então eu devo ter interpretado errado essa parte da documentação do Delphi->

The Parent property declared in TControl is similar to the Owner property declared in TComponent, in that the Parent of a control frees the control just as the Owner of a component frees that Component. However, the Parent of a control is always a windowed control that visually contains the control, and is responsible for writing the control to a stream when the form is saved. The Owner of a component is the component that was passed as a parameter in the constructor and, if assigned, initiates the process of saving all objects (including the control and its parent) when the form is saved.

Link para o comentário
Compartilhar em outros sites

  • 0

Thales, eu sempre tive o mesmo pensamento do colega kmkg. Até já havia ficado com a "pulga atrás da orelha" quando no outro post ao "Paulo" você fez a mesma citação.

Dei uma olhada por cima e aparentemente a coisa se mistura (coloquei as partes do código lá embaixo para tentar explicar melhor o que quero dizer).

Tendo em mente que a lista Components de um form é incrementada a cada create dos componentes onde passamos o Owner e considerando que eu chame o método Free de um Form, será acionado o seu método Destroy que seguindo a questão de herança vai desencadear nossa análise no método Destroy da classe TWinControl:

Observe que na classe TWinControl (destructor TWinControl.Destroy), um-a-um os componentes de sua lista serão destruídos. E para cada um deles, o método Destroy da classe TControl é acionado e, nele, o método SetParent(nil) fará com que o Parent remova o respectivo componente da tela.

O que vejo é que o Owner elimina os componentes (estrutura do objeto) enquanto o Parent elimina(libera) os recurso de janela alocados, já que as classes são apenas um meio de facilmente trabalharmos com os recursos oferecidos pela API do windows. É maios ou menos isto, não sei se me expressei bem.

[]s

destructor TCustomForm.Destroy;
begin
  if not (csDestroying in ComponentState) then GlobalNameSpace.BeginWrite;
  try
    if OldCreateOrder then DoDestroy;
    MergeMenu(False);
    if HandleAllocated then DestroyWindowHandle;
    Screen.RemoveForm(Self);
    FCanvas.Free;
    FIcon.Free;
    FreeAndNil(FActionLists);
    inherited Destroy;             // *** Desce mais um nível
  finally
    GlobalNameSpace.EndWrite;
  end;
end;

============================================================
destructor TScrollingWinControl.Destroy;
begin
  FHorzScrollBar.Free;
  FVertScrollBar.Free;
  inherited Destroy;             // *** Desce mais um nível
end;

============================================================
destructor TWinControl.Destroy;
var
  I: Integer;
  Instance: TControl;
begin
  Destroying;
  if FDockSite then
  begin
    FDockSite := False;
    RegisterDockSite(Self, False);
  end;
  FDockManager := nil;
  FDockClients.Free;
  if Parent <> nil then RemoveFocus(True);             // *** Nada de relevante
  if FHandle <> 0 then DestroyWindowHandle;           // *** Aqui a janela será destruida - via API Windows
  I := ControlCount;
  while I <> 0 do
  begin
    Instance := Controls[I - 1];
    Remove(Instance);                                  // *** Remove - nesta classe
    Instance.Destroy;                                   // *** uma a um, os componentes são destruidos
    I := ControlCount;
  end;
  FBrush.Free;
{$IFDEF LINUX}
  if FObjectInstance <> nil then WinUtils.FreeObjectInstance(FObjectInstance);
{$ENDIF}
{$IFDEF MSWINDOWS}
  if FObjectInstance <> nil then Classes.FreeObjectInstance(FObjectInstance);
{$ENDIF}
  inherited Destroy;
end;

procedure TWinControl.DestroyWindowHandle;
begin
  Include(FControlState, csDestroyingHandle);
  try
    if not Windows.DestroyWindow(FHandle) then    // *** Janela destruida
      RaiseLastOSError;
  finally
    Exclude(FControlState, csDestroyingHandle);
  end;
  FHandle := 0;
end;

procedure TWinControl.Remove(AControl: TControl);
begin
  if AControl is TWinControl then
  begin
    ListRemove(FTabList, AControl);
    ListRemove(FWinControls, AControl);
  end else
    ListRemove(FControls, AControl);
  AControl.FParent := nil;
end;

procedure TWinControl.RemoveControl(AControl: TControl);
begin
  Perform(CM_CONTROLCHANGE, Integer(AControl), Integer(False));
  if AControl is TWinControl then
    with TWinControl(AControl) do
    begin
      RemoveFocus(True);
      DestroyHandle;            // *** libera device contexts e destroi a janela
    end
  else
    if HandleAllocated then
      AControl.InvalidateControl(AControl.Visible, False);
  Remove(AControl);                 // *** Remove - nesta classe
  Perform(CM_CONTROLLISTCHANGE, Integer(AControl), Integer(False));
  Realign;
end;

procedure TWinControl.RemoveFocus(Removing: Boolean);
var
  Form: TCustomForm;
begin
  Form := GetParentForm(Self);
  if Form <> nil then Form.DefocusControl(Self, Removing);
end;

============================================================
destructor TControl.Destroy;
begin
  Application.ControlDestroyed(Self);
  if (FHostDockSite <> nil) and not (csDestroying in FHostDockSite.ComponentState) then
  begin
    FHostDockSite.Perform(CM_UNDOCKCLIENT, 0, Integer(Self));
    SetParent(nil);                                 // *** AQUI será desencadeado processo do parent (ver abaixo)
    Dock(NullDockSite, BoundsRect);
    FHostDockSite := nil;
  end else
    SetParent(nil);                                 // *** AQUI será desencadeado processo do parent
  ...
  inherited Destroy;                           // *** Continua destruindo!!!
end;

procedure TControl.SetParent(AParent: TWinControl);
begin
  if FParent <> AParent then
  begin
    if AParent = Self then
      raise EInvalidOperation.CreateRes(@SControlParentSetToSelf);
    if FParent <> nil then
      FParent.RemoveControl(Self);    // *** AQUI o Parent faz algo, chamará o remove do TWinControl
    if AParent <> nil then
    begin
      AParent.InsertControl(Self);
      UpdateAnchorRules;
    end;
  end;
end;

============================================================
destructor TComponent.Destroy;
begin
  Destroying;
  ...
  DestroyComponents;
  if FOwner <> nil then FOwner.RemoveComponent(Self);
  inherited Destroy;
end;

procedure TComponent.DestroyComponents;
var
  Instance: TComponent;
begin
  while FComponents <> nil do
  begin
    Instance := FComponents.Last;
    if (csFreeNotification in Instance.FComponentState)
      or (FComponentState * [csDesigning, csInline] = [csDesigning, csInline]) then
      RemoveComponent(Instance)
    else
      Remove(Instance);
    Instance.Destroy;
  end;
end;

procedure TComponent.RemoveComponent(AComponent: TComponent);
begin
  ValidateRename(AComponent, AComponent.FName, '');
  Notification(AComponent, opRemove);
  AComponent.SetReference(False);
  Remove(AComponent);
end;

procedure TComponent.Remove(AComponent: TComponent);
begin
  AComponent.FOwner := nil;
  FComponents.Remove(AComponent);     // *** remove da lista de componentes
  if FComponents.Count = 0 then
  begin
    FComponents.Free;
    FComponents := nil;
  end;
end;

Link para o comentário
Compartilhar em outros sites

  • 0

É, o negócio é complexo mesmo. Pelo que eu entendí você quis dizer que o owner libera a memória alocada pelo gerenciador de memória da Borland para conter os campos das classes, e o Parent trata de liberar Handles e outros recursos requisitados ao Windows, né?

Mas afinal de contas, é ou não preciso usar ListBox.Free;?

Ou só é preciso se não for passado o Owner como parametro no Create?

Link para o comentário
Compartilhar em outros sites

  • 0

Tá ficando boa essa discursão... :)

Pelo que eu entendí você quis dizer que o owner libera a memória alocada pelo gerenciador de memória da Borland para conter os campos das classes, e o Parent trata de liberar Handles e outros recursos requisitados ao Windows, né?
Thales, penso que é isso. Mas como eu disse, olhei meio por cima. O correto seria depurar passo a passo para tirar a dúvida, só que estou meio sem tempo.
Mas afinal de contas, é ou não preciso usar ListBox.Free;?
Depende do caso. Veja abaixo.
Ou só é preciso se não for passado o Owner como parametro no Create?
Ai é que está. Tentando achar um exemplo para isto, constatei o seguinte (código lá embaixo): - Se você definir o Owner, mas não definir o Parent, o Destroy é executado; - Se você não definir o Owner, mas definir o Parent, o Destroy é executado; Assim, se for um componente não visual, você terá que chamar explicitamente o Free caso não passe o Owner. Logo a minha primeira afirmativa não é 100% verdadeira, tem mesmo que depurar para entender o que acontece. E chamar Free, é problema? - Se tiver owner: não implicará em problema porque o próprio componente ao ser destruido "solicitará" sua exclusão da lista Components do Owner; (é só observar o ComponentCount) - Se tiver apenas Parent: também não é problema. Logo, acho que um bom hábito é: se você cria o componente em run-time, destrua ele em run-time. O Teste: É só criar uma classe derivada de TListBox em seu form, com o método Destroy, e colocar alí um break-point. você observará que ao sair (fechar) o form a execução parará exatamente lá. Não tem nenhuma chamada a FREE mesmo.
type
  TForm1 = classe(TForm)
  ...
  end;

  TMyListBox = class(TListBox)
    destructor Destroy; override;
  end;
...

destructor TMyListBox.Destroy;
begin
  ShowMessage('Destruindo MyListBox');
  inherited;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
// opção 1: sem owner, com parent
//  with TMyListBox.Create(nil) do
//  begin
//    Parent := Self;

// opção 2: com owner, sem parent
  with TMyListBox.Create(Self) do
  begin
//    Parent := Self;
    Left := 120;
    Top := 8;
    Width := 250;
    Height := 300;
  end;
end;

Link para o comentário
Compartilhar em outros sites

  • 0

Bom, outro teste é criar e destruir varias vezes um form com um componente deste e mostrar a situação da memória com a função GetHeapStatus. Se a cada destruição a memória voltar ao estado anterior é porque não tem erro.

Mas é sempre bom colocar um Free por via das duvidas, porque se o objeto já tiver sido destruído a função não ocasiona um erro, só não faz nada.

Link para o comentário
Compartilhar em outros sites

  • 0

Pessoal, vim acompanhando este tópico e queria postar minha opinião:

Vou me referir a Objetos para uma abrangência geral.

1-Objetos criados com Owner --> nunca executo Free

2-Objetos criados sem Owner --> sempre executo Free com uma ressalva (se possuir Parent, executo Objeto.Parent := nil; antes do Free).

Posso estar errado, mas comigo nunca me deu erro e nunca percebí memory-leaks.

Link para o comentário
Compartilhar em outros sites

  • 0

Memory leaks é quando você destrói um objeto que não vai ser mais usado e sobra alguma memória ainda alocada mais que perdeu a referencia e não é mais vista pelo seu programa. E aquela parte não poderá ser usada por outro programa porque ainda é considerada como alocada pelo sistema operacional. Tipo se você tem um objeto->

Objeto = Class(TObject);
private
  bmp, foto : TBitmap;
public
  procedure Create; override;
  procedure Destroy; override;
end;

implementation

procedure Objeto.Create;
begin
  inherited Create;
  bmp:=TBitmap.Create;
  Foto:=TBitmap.Create;
end;

procedure Objeto.Destroy;
begin
  bmp.Destroy;
  inherited Destroy;
end;

nesse caso ocorrerá um leak porque o objeto foto ainda existirá na memória já que não foi destruido, e perderá completamente a referencia pelo programa que não poderá acessa-lo. E o objeto ficará alocado não deixando que outros programas usem aquela memória.

Link para o comentário
Compartilhar em outros sites

  • 0

Hum, entendi, mas, quando o s3c, disse "não percebi Memory leaks", foi investigando com mais profundidade?

Porque, pelo que entendi seria apenas uma prática de má programação não é?

Erro mesmo não acontece, não é isso?

Link para o comentário
Compartilhar em outros sites

  • 0

É isso na maioria das vezes é prática de má programação. É claro que esse foi um exemplo idiota, mas na programação pesada muitas vezes ocorre de se deixar passar algum ponteiro sem liberar.

Eu mesmo estava escrevendo um programa que estava funcionando tudo legal, aí eu pensei que num estava acontecendo leak nenhum, mas quando eu resolví colocar um GetHeapStatus lá, eu percebi que a cada vez que eu fazia um DragDrop a Heap.TotalAllocated aumentava em 1KB, aí eu dei uma revisada no código e ví que tinha esquecido de liberar algums ponteiros. Aí ficou tudo certo.

Agora, ele disse que não percebeu nenhum leak, mas eu não sei o que ele fez pra verificar que realmente não tinha nenhum leak. Vai ver ele só deu uma olhada assim... é num tem nada não....

Link para o comentário
Compartilhar em outros sites

  • 0

Bom, nunca cheguei a verificar leaks de memória tão detalhadamente como o Thales que se encasqueta com leaks de KBs. Verifico a grosso modo pelo task manager do Windows e em leaks acima de 2MB, aí sim vou dar uma escovada de bits para ver o que está ocorrendo. Como boa prática de programação, todo objeto criado em run-time deve se ter a preocupação de liberá-lo; mas por outro lado, teoricamente você pode criar o que quiser sem liberar nada e quando você fechar sua aplicação o Windows se encarrega de liberar toda a memória utilizada. No meu caso só tenho 2 .exe; um programa servidor de Banco de Dados ± 20MB e o outro é a aplicação cliente gerenciadora de menus ± 10MB; todo o resto do sistema é baseado em dlls; ou seja; quando o usuário chamar uma opção de menú, a aplicação cliente carrega a dll responsável por aquilo, executa e quando terminar a dll é descarregada da memória. Com esta prática vim obtendo excelentes resultados de otimização de memória.

Link para o comentário
Compartilhar em outros sites

Participe da discussão

Você pode postar agora e se registrar depois. Se você já tem uma conta, acesse agora para postar com sua conta.

Visitante
Responder esta pergunta...

×   Você colou conteúdo com formatação.   Remover formatação

  Apenas 75 emoticons são permitidos.

×   Seu link foi incorporado automaticamente.   Exibir como um link em vez disso

×   Seu conteúdo anterior foi restaurado.   Limpar Editor

×   Você não pode colar imagens diretamente. Carregar ou inserir imagens do URL.



  • Estatísticas dos Fóruns

    • Tópicos
      152,3k
    • Posts
      652,3k
×
×
  • Criar Novo...