Paulo Nobre Postado Agosto 31, 2006 Denunciar Share Postado Agosto 31, 2006 Estou tentando criar um ListBox em tempo de execução.Estou fazendo,no OnClick de um image,varlstAtalhos:TListBox;memo:string//Esta variável vai pegar o conteúdo do listboxbegin...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? Citar Link para o comentário Compartilhar em outros sites More sharing options...
0 Thales Pontes Martins Postado Agosto 31, 2006 Denunciar Share Postado Agosto 31, 2006 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; Citar Link para o comentário Compartilhar em outros sites More sharing options...
0 Paulo Nobre Postado Setembro 1, 2006 Autor Denunciar Share Postado Setembro 1, 2006 Thales,Acabei descobrindo que deveria colocarLB.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. Citar Link para o comentário Compartilhar em outros sites More sharing options...
0 Thales Pontes Martins Postado Setembro 1, 2006 Denunciar Share Postado Setembro 1, 2006 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? Citar Link para o comentário Compartilhar em outros sites More sharing options...
0 Paulo Nobre Postado Setembro 1, 2006 Autor Denunciar Share Postado Setembro 1, 2006 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!! Citar Link para o comentário Compartilhar em outros sites More sharing options...
0 kmkg Postado Setembro 1, 2006 Denunciar Share Postado Setembro 1, 2006 Sim, o parent é responsável por destruir todos os controles em sua lista Controls.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. Citar Link para o comentário Compartilhar em outros sites More sharing options...
0 Thales Pontes Martins Postado Setembro 1, 2006 Denunciar Share Postado Setembro 1, 2006 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. Citar Link para o comentário Compartilhar em outros sites More sharing options...
0 kmkg Postado Setembro 1, 2006 Denunciar Share Postado Setembro 1, 2006 Realmente você tem razão. Vinha com essa idéia desde o D5 quando criava componentes em execução sem Owner. Na hora de liberar, se destruísse o pai antes, quando fosse destruir o filho dava erro de Control has no parent. Citar Link para o comentário Compartilhar em outros sites More sharing options...
0 Micheus Postado Setembro 1, 2006 Denunciar Share Postado Setembro 1, 2006 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.[]sdestructor 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; Citar Link para o comentário Compartilhar em outros sites More sharing options...
0 Thales Pontes Martins Postado Setembro 1, 2006 Denunciar Share Postado Setembro 1, 2006 É, 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? Citar Link para o comentário Compartilhar em outros sites More sharing options...
0 kmkg Postado Setembro 1, 2006 Denunciar Share Postado Setembro 1, 2006 Parece que é no TWinControl.Destroy que o Parent elimina seus filhos chamando Instance.Destroy; aí ele volta a executar TWinControl.Destroy rescursivamente. Citar Link para o comentário Compartilhar em outros sites More sharing options...
0 Micheus Postado Setembro 1, 2006 Denunciar Share Postado Setembro 1, 2006 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; Citar Link para o comentário Compartilhar em outros sites More sharing options...
0 Thales Pontes Martins Postado Setembro 1, 2006 Denunciar Share Postado Setembro 1, 2006 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. Citar Link para o comentário Compartilhar em outros sites More sharing options...
0 Paulo Nobre Postado Setembro 2, 2006 Autor Denunciar Share Postado Setembro 2, 2006 É, realmente esse é um forum com uma quantidade enorme de feras por metro quadrado.Na dúvida então uso o free!! Citar Link para o comentário Compartilhar em outros sites More sharing options...
0 s3c Postado Setembro 2, 2006 Denunciar Share Postado Setembro 2, 2006 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 Free2-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. Citar Link para o comentário Compartilhar em outros sites More sharing options...
0 Paulo Nobre Postado Setembro 2, 2006 Autor Denunciar Share Postado Setembro 2, 2006 Posso estar errado, mas comigo nunca me deu erro e nunca percebí memory-leaks. O que significa memory-leaks Citar Link para o comentário Compartilhar em outros sites More sharing options...
0 Thales Pontes Martins Postado Setembro 2, 2006 Denunciar Share Postado Setembro 2, 2006 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. Citar Link para o comentário Compartilhar em outros sites More sharing options...
0 Paulo Nobre Postado Setembro 2, 2006 Autor Denunciar Share Postado Setembro 2, 2006 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? Citar Link para o comentário Compartilhar em outros sites More sharing options...
0 Thales Pontes Martins Postado Setembro 2, 2006 Denunciar Share Postado Setembro 2, 2006 É 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.... Citar Link para o comentário Compartilhar em outros sites More sharing options...
0 Paulo Nobre Postado Setembro 2, 2006 Autor Denunciar Share Postado Setembro 2, 2006 Ok, Thales. Citar Link para o comentário Compartilhar em outros sites More sharing options...
0 s3c Postado Setembro 4, 2006 Denunciar Share Postado Setembro 4, 2006 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. Citar Link para o comentário Compartilhar em outros sites More sharing options...
0 Paulo Nobre Postado Setembro 4, 2006 Autor Denunciar Share Postado Setembro 4, 2006 Ok, s3c, entendi. Citar Link para o comentário Compartilhar em outros sites More sharing options...
Pergunta
Paulo Nobre
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
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.