2013-02-23 9 views
6

Mam formularz zawierający TFrame. Numer TFrame zawiera ComboBox, który jest dynamicznie wypełniany. Każdy wpis ComboBox ma powiązany obiekt. Zanim zostanie wywołany nadpisany destruktor dla TFrame, Elementy w ComboBox zostały już wyczyszczone bez zwalniania powiązanych z nimi obiektów. Dzieje się tak, niezależnie od tego, czy upuszczam ComboBox w formularzu w widoku projektowym, czy też dynamicznie tworzę go w kodzie z zerową lub TFrame jako jego właścicielem. Obecnie używam zdarzenia OnDestroy zawierającego TForm, aby wywołać procedurę czyszczenia zawartego TFrame.Gdzie można bezpłatnie dynamicznie przydzielać obiekty komponentów TFrame?

Czy istnieje lepszy sposób, który nie wymaga jawnego wywoływania procedury przez kontener TFrame? Gdzie najlepiej powinien być wolny obiektów dodanych dynamicznie do ComboBox?

+6

Moja rada to zmienić projekt. Nie ma pola kombi będącego właścicielem tych obiektów. Niech ramka będzie je zawierała w dowolnym kontenerze, np. 'TObjectList '. Następnie możesz zniszczyć ten kontener w destruktorze ramki. –

Odpowiedz

3

Twoje pytanie nie jest zbyt użyteczne, ponieważ - ogólnie rzecz biorąc - jest zniechęcane do przechowywać danych (lub obiektów w Twoim przypadku) w kontroli GUI. Zobacz także komentarz Davida, jak zmienić swój projekt.

To, co sprawia, że ​​pytanie ma charakter interakcyjny, to jednak różnica między kombinacją, która jest dzieckiem formy bezpośrednio i jest dzieckiem innego dziecka w formularzu (w tym przypadku ramka). Wygląda na to, że elementy pola kombi są zniszczone przed wywołaniem destruktora tej klatki. Oczywistymi alternatywami do odkrycia są: przesłonięcie Frame.BeforeDestruction, przesłanie Frame.DestroyWindowHandle, przesłanie , lub przechwycenie WM_DESTROY w nadpisanym Frame.WndProc, ale żaden z nich nie jest wywoływany, zanim elementy już nie istnieją.

Następną rzeczą, którą należy wypróbować, jest powtórzenie tego dla pola kombi. Okazuje się, że kiedy WM_DESTROY przybywa do pola kombi, że przedmioty nadal tam są. Należy jednak uważać na przechwytywanie tej wiadomości, gdy kontrolka naprawdę jest niszczona, ponieważ VCL może często odtwarzać pole kombi. Wdrożyć go przy użyciu wstawienie klasę dla TComboBox, co następuje:

unit Unit2; 

interface 

uses 
    Windows, Messages, Classes, Controls, Forms, StdCtrls; 

type 
    TComboBox = class(StdCtrls.TComboBox) 
    protected 
    procedure WndProc(var Message: TMessage); override; 
    end; 

    TFrame1 = class(TFrame) 
    ComboBox1: TComboBox; 
    end; 

implementation 

{$R *.dfm} 

{ TComboBox } 

procedure TComboBox.WndProc(var Message: TMessage); 
var 
    I: Integer; 
begin 
    if (Message.Msg = WM_DESTROY) and (csDestroying in ComponentState) then 
    for I := 0 to Items.Count - 1 do 
     Items.Objects[I].Free; 
    inherited WndProc(Message); 
end; 

end. 

Teraz, aby odpowiedzieć na pytanie: „Czy to jest lepszy sposób?”

Tak, ponieważ zapewnia zapewnienie zniszczenia obiektu na poziomie klatki. Innymi słowy: nie musisz zapamiętaj, aby poradzić sobie z tym dla każdej instancji oddzielnie.

I nie, nie jest, ponieważ to rozwiązanie wymaga, aby obiekty w polu kombi mogły zostać zwolnione w dowolnych okolicznościach, które ograniczają użycie do niepotrzebnej dodatkowej granicy.

Czy ta odpowiedź jest przydatna? Cóż, jeśli przeszkadza ci to w używaniu obecnego podejścia, to tak właśnie jest.


Poza tym, ja również znaleźć inną alternatywę poprzez ustawienie ramy za Parent własności do zera w zawierającego postaci OnDestroy Handler:

procedure TForm2.FormDestroy(Sender: TObject); 
begin 
    Frame1.Parent := nil; 
end; 

W tym przypadku można bezpiecznie zniszczyć obiektów przechowywanych w kombi pudełko wewnątrz destruktora ramki. Ale to rozwiązanie jest nawet gorsze od obecnego, ponieważ nie jest opisowe. Wtedy Frame1.FreeComboObjects jest znacznie lepszy.

+1

Czy uważasz, że testowanie csDestroying w ComponentState może zastąpić FR? –

+0

@ Sertac Tak, i jest czystszy i bardziej opisowy. Zmieniono to. – NGLN

7

Mówisz, że po wywołaniu destruktora dla TFrame, Elementy ComboBox zostały już wyczyszczone. Tak nie jest, przedmioty ComboBox nigdy nie są usuwane. Gdy elementy zostaną zniszczone przez ComboBox, mają one tylko 0.

Po zamknięciu aplikacji i VCL niszczy formularz zawierający ramkę i ComboBox, natywna kontrola ComboBox jest również niszczona przez system operacyjny ponieważ znajduje się w niszczącym oknie. Gdy później uzyskasz dostęp do przedmiotów, aby zwolnić obiekty w destruktorze ramek, VCL musi odtworzyć natywny kontrolkę ComboBox, mając liczbę sztuk wynoszącą 0.

Proponowane przeze mnie rozwiązanie jest łatwe. Nie pozostawiaj wolnej ramki do ramy, zamiast tego zniszcz ramkę w zdarzeniu OnDestroy swojego formularza. To będzie, zanim podstawowe okno formularza zostanie zniszczone, dzięki czemu będziesz mógł uzyskać dostęp do swoich obiektów.

jednostka forma

procedure TMyForm.FormDestroy(Sender: TObject); 
begin 
    MyFrame.Free; 
end; 

rama

destructor TMyFrame.Destroy; 
var 
    i: Integer; 
begin 
    for i := 0 to ComboBox1.Items.Count - 1 do 
    ComboBox1.Items.Objects[i].Free; 
    inherited; 
end; 
+0

+1. To bardzo miłe rozwiązanie. – kobik

+0

@kobik - Dzięki. Lubię twoje też, ponieważ jest odsprzęgnięte od formy po zniszczeniu. –

6

Można by wykorzystać TFrame „s WM_DESTROY obsługi tak:

unit Unit2; 

interface 

uses 
    Windows, Messages, SysUtils, Classes, Controls, Forms, StdCtrls; 

type 
    TFrame1 = class(TFrame) 
    ComboBox1: TComboBox; 
    private 
    procedure WMDestroy(var Msg: TWMDestroy); message WM_DESTROY; 
    procedure FreeComboBoxItems; 
    public 
    constructor Create(AOwner: TComponent); override; 
    end; 

implementation 

{$R *.dfm} 

constructor TFrame1.Create(AOwner: TComponent); 
begin 
    inherited; 
    // Add some object items to the ComboBox 
    ComboBox1.AddItem('a', TButton.Create(nil)); 
    ComboBox1.AddItem('b', TMemoryStream.Create); 
    ComboBox1.AddItem('c', TList.Create); 
end; 

procedure TFrame1.WMDestroy(var Msg: TWMDestroy); 
begin 
    // Make sure the TFrame is actually destroying - not recreated 
    if (csDestroying in ComponentState) then 
    FreeComboBoxItems; 
    inherited; 
end; 

procedure TFrame1.FreeComboBoxItems; 
var 
    I: Integer; 
begin 
    OutputDebugString('TFrame1.FreeComboBoxItems'); 
    with Self.ComboBox1 do 
    for I := 0 to Items.Count - 1 do 
    begin 
     OutputDebugString(PChar(Items.Objects[I].ClassName + '.Free')); 
     Items.Objects[I].Free; 
    end; 
end; 

end. 

Inną opcją jest do Cr Zrób podstawową klasę przodków TAppBaseForm i TAppBaseFrame dla całej aplikacji i wyprowadź wszystkie swoje formularze jako TAppBaseForm i wszystkie ramki jako TAppBaseFrame. W ten sposób TAppBaseForm może powiadomić wszystkie swoje dziecko TAppBaseFrame, że formularz właściciela zostanie zniszczony na module obsługi zdarzenia TAppBaseForm.FormDestroy. W tym momencie elementy ComboBox są nadal ważne (jak opisano w Sertac Akyuz's answer).

Powiązane problemy