Dynamiczne tworzenie komponentów na formie - prośba o sprawdzenie

0

Witam.

Pierwszy raz robię formę na której w zależności od sytuacji będę potrzebował w zależności od sytuacji różne komponenty (w różniej ilości), w związku z czym stawiam kolejne kroczki w dynamicznym tworzeniu komponentów.
Korzystając z rady @furious programming (z tematu: http://4programmers.net/Forum/Newbie/158490-dynamiczne_tworzenie_komponentow_-_niewidoczny_przycisk?p=1017984#id1017984) referencje do stworzonych komponentów przetrzymuje w tablicy (dzięki czemu zwalnianie i odwoływanie sie do nich jest bardzo wygodne).

Napisałem coś takiego:

     
 TMojaKlasa = class
  public 
    var
      komponenty                 : array of TObject;    
end;

/.../
     TPom := TMojaKlasa.Create;    

     SetLength(TPom.komponenty,1);
     TPom.komponenty[0]                := TPanel.Create(Self);
     TPanel(TPom.komponenty[0]).Parent := FRaport;
     TPanel(TPom.komponenty[0]).Align  := alBottom;
     TPanel(TPom.komponenty[0]).Height := 60;

     SetLength(TPom.komponenty, Length(TPom.komponenty)+1);
     TPom.komponenty[1]                  := TButton.Create(Self);
     TButton(TPom.komponenty[1]).Parent  := TPanel(TPom.komponenty[0]);
     TButton(TPom.komponenty[1]).Height  := 40;
     TButton(TPom.komponenty[1]).Width   := 120;
     TButton(TPom.komponenty[1]).Top     := (TPanel(TPom.komponenty[0]).Height-TButton(TPom.komponenty[1]).Height) div 2;
     TButton(TPom.komponenty[1]).Left    := FRaport.Width-TButton(TPom.komponenty[1]).Width-20;
     TButton(TPom.komponenty[1]).Caption := 'ZAMKNIJ';
     TButton(TPom.komponenty[1]).OnClick := @ButtonCloseClick;

     SetLength(TPom.komponenty, Length(TPom.komponenty)+1);
     TPom.komponenty[2]                     := TStringGrid.Create(Self);
     TStringGrid(TPom.komponenty[2]).Parent := FRaport;
     TStringGrid(TPom.komponenty[2]).Align  := alClient;

     FRaport.ShowModal;     

No i tutaj prośba do Was: powiedzcie mi czy taki kod jest poprawny? Czy powinienem podejść do tego inaczej ?

Oczywiście nie chodzi mi o to czy kod się skompiluje itp (bo to wiem: program działa zgodnie z zamierzeniem), tylko o to czy kod jest zgodny z "przyjętymi normami" :)

0

@hipekk - jeśli potrzebujesz komponenty różnych klas trzymać w jednej liście, to lepszym wyjściem było by skorzystanie z listy TObjectsList, tak jak zauważył @babubabu; W przypadku różnych klas i tak będziesz musiał sprawdzać typ klasy, np. za pomocą operatora Is; Jeśli chcesz trzymać listę komponentów tych samych klas, to możesz sobie zadeklarować dedykowaną macierz, ale jej obsługa będzie i tak mniej wygodna, niż wspomnianej listy; Tyle że unikniesz sprawdzania typu klas poszczególnych komponentów;

A jeśli o sam kod chodzi, to trochę w nim brakuje; Przede wszystkim pokazany urywek służy do dodania trzech różnych komponentów do macierzy, ale zamiast ustalić rozmiar tej macierzy na 3 od razu, Ty zwiększasz 3 razy po jedno miejsce; Druga sprawa to ciągłe rzutowanie, aby poustawiać właściwości komponentów; Albo skorzystaj z instrukcji wiążącej With, albo zadeklaruj sobie dodatkową zmienną i ją uzupełniaj, a na koniec przepisz referencję spod tej zmiennej do odpowiedniego pola macierzy;

Jeśli bawisz się z macierzami, to dużo lepszym rozwiązaniem jest alokacja pamięci z pewną rezerwą - np. od razu na 16 elementów; Do tego musisz mieć zmienne lub pola, w których przechowasz rozmiar całej tablicy, a także osobno ilość faktycznie dodanych elementów do macierzy; Dzięki temu przed dodaniem nowego elementu sprawdzasz ile jest elementów w tablicy i ile jest w niej wolnego miejsca; Jeśli zarezerwowałeś pamięć dla 16 elementów i tyle jest dodanych - zwiększasz rozmiar tablicy np. x2 i dopiero wtedy dodajesz element;

Jeśli jeszcze nie widzisz jak to działa - zobacz jak to jest zrealizowane w klasie TStrings, jeśli oczywiście masz dostęp do źródeł biblioteki standardowej.

0

Dziękuję serdecznie za odpowiedź :).

Przy testowaniu tworzenia komponentów trafiłem na jedną dziwną dla mnie rzecz:
Tworze jakiś przycisk i wszystko jest ok, pojawia się tam gdzie chce, jednak gdy przy ustawianiu właściwości dodam : Anchors := [akRight]; komponent się nie pojawia.
Sprawdzałem pozycję przycisku (top, left) jest taka sama w obydwu przypadkach.
Dlaczego tak się dzieje i gdzie jest "zaginiony" przycisk?


Uzupełniam brak:

     TPom.komponenty[1] := TButton.Create(Self);
     WITH TButton(TPom.komponenty[1]) DO
     begin
          Parent  := TPanel(TPom.komponenty[0]);

          Height  := 40;
          Width   := 120;
          Top     := TPanel(TPom.komponenty[0]).Height - Height - 13  ;
          Left    := FRaport.Width-Width-20;
          Caption := 'ZAMKNIJ';
          //Anchors := [akRight];
          OnClick := @ButtonCloseClick;
     end;    

Po od komentowaniu Anchors := [akRight]; przycisk "znika".

0

Sprawdziłem kod i u mnie działa (Lazarus 1.0.14, FPC 2.6.2):

type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    FCompList: array [0 .. 1] of TObject;
  end;

var
  Form1: TForm1;

implementation

{$R *.lfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
  { panel }
  FCompList[0] := TPanel.Create(Form1);

  with TPanel(FCompList[0]) do
  begin
    Parent  := Form1;
    Left    := 20;
    Top     := 20;
    Width   := 200;
    Height  := 100;
    Caption := '';
    Anchors := [akLeft, akTop, akRight, akBottom];
  end;

  { button }
  FCompList[1] := TButton.Create(Form1);

  with TButton(FCompList[1]) do
  begin
    Parent := TPanel(FCompList[0]);
    Left   := 20;
    Top    := 20;
    Width  := 100;
    Height := 25;
    Caption := 'My button';
    Anchors := [akTop, akRight];
  end;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  { panel }
  FCompList[0].Free();
  { button }
  FCompList[1].Free();
end;

Efektem wywołania kodu jest okienko z oboma komponentami (po lewej); Po prawej zaś rozciągnięte lekko okienko, z poprawnie działającymi kotwicami:

anchors-preview.png

Problem musi więc leżeć gdzie indziej niż w LCL, dlatego że wszystko działa poprawnie z różnymi konfiguracjami kotwic; Trzeba by zaprzągnąć debuger i wszystko na spokojnie posprawdzać.

0

Zauważyłem pewną zależność...

Przycisk Form1 tworzy komponenty na Form2 2 i ją pokazuje.

  
   
	// tworzenie pozostałych komponentów //

     TPom.komponenty[1] := TButton.Create(Self);
     WITH TButton(TPom.komponenty[1]) DO
     begin
          Parent  := TPanel(TPom.komponenty[0]);

          Height  := 40;  
          Width   := 120;    
          Top     := 50;  
          Left    := 120+20; 
          Caption := 'ZAMKNIJ'; 
          Anchors := [akRight];
     end;

    
     Form2.Show;   

Jeżeli Form2.Show; jest na końcu procedury przycisku a we własciwościach przycisku jest Anchors := [akRight]; pojawiają się problemy.
W chwili gdy przeniosę Form2.Show; na początek procedury, wszystko jest ok....

0

A ten drugi formularz (Form2) tworzysz dynamicznie, czy automatycznie przy starcie aplikacji?

0

Form2 jest stworzony na stałe (więc tworzony przy starcie aplikacji).

0

No to twórz go dynamicznie, tylko jeśli go potrzebujesz; A tak to za każdym razem gdy będziesz go potrzebował, Twój kod będzie mu na nowo dynamicznie tworzył komponenty, nadpisując stare instancje (w macierzy oczywiście).

0

Zmieniłem formę na tworzoną dynamicznie lecz nadal występuje problem.

Kod który działa:

procedure TFMain.MIRaportOgolnyClick(Sender: TObject);
begin    
     SetLength(TPom.komponenty,14);
     TPom.komponenty[13] := TForm.Create(Self);
     WITH TForm(TPom.komponenty[13]) DO
     begin
       Top:=0;
       Left:=0;
       Width:=300;
       Height:=300;
       Position:= poScreenCenter;
     end;
    TForm(TPom.komponenty[13]).Show;

     TPom.komponenty[0] := TPanel.Create(Self);
     WITH TPanel(TPom.komponenty[0]) DO
     begin
          Parent :=  TForm(TPom.komponenty[13]);
          Align  := alBottom;
          Height := 150;
     end;

     TPom.komponenty[1] := TButton.Create(FRaport);
     WITH TButton(TPom.komponenty[1]) DO
     begin
          Parent  := TPanel(TPom.komponenty[0]);

          Height  := 40;
          Width   := 120;
          Top     := TPanel(TPom.komponenty[0]).Height - Height - 13  ;
          Left    := TForm(TPom.komponenty[13]).Width-Width-20;      
          Caption := 'ZAMKNIJ';
          Anchors := [akBottom, akRight];
          OnClick := @ButtonCloseClick;
     end;   
end;  

ok.jpg

Kod który nie działa:

procedure TFMain.MIRaportOgolnyClick(Sender: TObject);
begin    
     SetLength(TPom.komponenty,14);
     TPom.komponenty[13] := TForm.Create(Self);
     WITH TForm(TPom.komponenty[13]) DO
     begin
       Top:=0;
       Left:=0;
       Width:=300;
       Height:=300;
       Position:= poScreenCenter;
     end;
   

     TPom.komponenty[0] := TPanel.Create(Self);
     WITH TPanel(TPom.komponenty[0]) DO
     begin
          Parent :=  TForm(TPom.komponenty[13]);
          Align  := alBottom;
          Height := 150;
     end;

     TPom.komponenty[1] := TButton.Create(FRaport);
     WITH TButton(TPom.komponenty[1]) DO
     begin
          Parent  := TPanel(TPom.komponenty[0]);

          Height  := 40;
          Width   := 120;
          Top     := TPanel(TPom.komponenty[0]).Height - Height - 13  ;
          Left    := TForm(TPom.komponenty[13]).Width-Width-20;      
          Caption := 'ZAMKNIJ';
          Anchors := [akBottom, akRight];
          OnClick := @ButtonCloseClick;
     end;   
    TForm(TPom.komponenty[13]).Show;
end;  

not ok.jpg

1

Kod działa, tylko miejsce przycisku staje się nieprawidłowe;

Poza tym nie o takie coś chodziło mi z tym formularzem; Zmienną przechowującą referencję do dynamicznie tworzonego formularza pozostaw tak jak było wcześniej - w module z klasą tego formularza; Nie pakuj jej do macierzy, bo to nic nie pomoże, a wzamian zmniejszysz czytelność kodu;

A tak poza tym - po co w ogóle tworzysz tutaj te komponenty dynamicznie, skoro zarówno panel, jak i przycisk i tak zawsze w tym formularzu będą się znajdować? Zrób ten formularz normalnie - w edytorze formularzy połóż na nim te komponenty, które zawsze będą istnieć, a dynamicznie twórz te, które się zmieniają; Nie wiem czy Twoje kombinacje są w tym przypadku potrzebne;

Tak nawiasem - używane do ustawienia położenia przycisku właściwości Width i Height mogą powodować problemy, bo przez użycie instrukcji wiążącej właściwości te nakładają się (takie same właściwości posiada klasa formularza); Sprawdź więc czy te obliczenia działają prawidłowo;


Edit: Albo jednak coś dopiszę; @hipekk - teraz widzę, że w ogóle nie ustawiasz szerokości tworzonego panela; Spróbuj nadać mu jakąś szerokość, np. pobierz ją z Form1.ClientWidth; Być może tutaj jest problem.

0
furious programming napisał(a):

Edit: Albo jednak coś dopiszę; @hipekk - teraz widzę, że w ogóle nie ustawiasz szerokości tworzonego panela; Spróbuj nadać mu jakąś szerokość, np. pobierz ją z Form1.ClientWidth; Być może tutaj jest problem.

BINGO !

Myślałem, że ustawiając Align := alBottom automatycznie ustawia się szerokość komponentu (taka jak szerokość komponentu nadrzędnego)...
Dziękuję :)

1 użytkowników online, w tym zalogowanych: 0, gości: 1