Dynamiczne tworzenie komponentów bez zadeklarowanej tablicy

0

Witam.
Szukałem, próbowałem i pozostało forum .. o ile się tak da.
Mam taką główną pętlę:

   for j:= 0 to Count -1 do
   begin

      With TPanel.Create(ScrollBox) do
      begin
         Parent:= aScrBox;
         Font := Font;
         Name:='Panel'+IntToStr(j); // nadanie nazwy przyciskowi
         Caption:= Name;
         Width  := 30;
         Height := 21;
         Top  := j *Height +1;
         Left := 0;
         Align:= alTop; // alTop;
         Visible:=True;
      end;

   end;

Jak widać tworze sobie na ScrollBox-ie Count paneli. A teraz gwóźdź programu, na tych TPanel chciałbym umieścić np. dla przykładu i odróżnienia TLabel i TButton.
Standardowo można oczywiście zrobić listę dynamiczną z rekordem zawierającym TPanel, TLabel, TButton ale zastanawiam się czy nie można np utworzyć Labela i Buttona na podstawie nazwy tekstowej Panela Panel+IntToStr(j). Próbowałem wykorzystać Find przy tworzeniu ale był problem z Parent i tu się moje pomysły skończyły.

Tak więc proszę o podpowiedź jak tworzyć i jeśli się da jak potem się do takich komponentów odwoływać. Może jakiś tymczasowy rekord z komponentami w procedurze (nie globalny), który jako jeden byłby w pętli wywoływany ..
Dla uściślenia przy odwoływaniu miało by to działać w stylu:

TLabel( komponent_rodzic_TPanel('Panel'+IntToStr(j)) ).Caption:= 'coś tam'
2

Być może był problem z FindComponent, dlatego że AOwner <> Parent, a do tego AOwner nie jest formularzem; Nie mam tutaj pewności (późno już), ale sprawdziłem taki kod:

procedure TForm1.FormCreate(Sender: TObject);
var
  ctrlParent: TWinControl;
  pnlNew: TPanel;
  btnNew: TButton;
  I: Integer;
begin
  { initialize parent object }
  ctrlParent := Form1;
 
  { create panels }
  for I := 0 to 4 do
  begin
    pnlNew := TPanel.Create(Form1);
 
    with pnlNew do
    begin
      Parent  := ctrlParent;
      Name    := 'Panel' + IntToStr(I);
      Caption := '';
      Width   := 300 - I * 20;
      Height  := 200 - I * 20;
      Left    := 10;
      Top     := 10;
    end;
 
    { rewrite parent object }
    ctrlParent := pnlNew;
  end;
 
  { create button }
  btnNew := TButton.Create(Form1);
 
  with btnNew do
  begin
    Parent  := Form1.FindComponent('Panel4') as TWinControl;
    Name    := 'Button';
    Caption := 'Click me!';
    Width   := 100;
    Height  := 25;
    Left    := 10;
    Top     := 10;
  end;
end;

i ładnie tworzy zarówno panele, jak i na końcu sam przycisk; Efekt poniżej:

form.png

Jak widać w powyższym przykładzie - właścicielem komponentów zawsze jest obiekt formularza, natomiast zmieniają się jedynie rodzice; Jeżeli zmienię właścicieli - metoda FindComponent nie znajdzie panela, bo formularz nie będzie jego właścicielem; Coś w tym jest;

Czyli na to by wychodziło, że właścicielem (czyli AOwner) musi być formularz, a rodzicem (czyli Parent) już dany komponent grupujący.

Ownerem komponentu nie musi być formularz - można przekazać do konstruktora Nil (albo jakiś inny komponent), ale trzeba wywołać w takim przypadku metodę SetSubComponent; A rodzicem może być dowolny komponent, który zaprojektowany jest do "przechowywania" innych, czyli np. formularz, panel czy groupbox.

0

Super. Przystosowałem na swój sposób i działa.Jeszcze tylko takie mniej istotne pytanie.

  1. Bo gdy tworzę dynamicznie listę 100 wierszy z komponentów na ScrollBar to trwa około 4 sec. Przy dłuższej - oczywiście dłużej. Jest może jakiś sposób żeby to przyspieszyć, bo mogą być listy gdzie będą zawierać i 50-100tys wierszy.
    Najprostsze co mi przychodzi do głowy to wyświetlać tylko tyle pozycji ile się zmieści a potem suwakiem przesuwać i podmieniać wartości .. sposób szybszy ale wymaga dodatkowego szperania w OnClick i innych .. może da się inaczej?

  2. Może da się wyłączyć odświeżanie na czas tworzenia, bo nawet przy liście 25-30 wierszy widać jak się tworzą komponenty - jakby nie było trwa to około sekundę.

1

@Integers - nie wiem czy to dobry pomysł;

  1. Zauważ, że np. tworzenie 50-ciu tysięcy komponentów musi trwać - tego raczej nie przeskoczysz; Wyświetlanie tylu pozycji, ile mieści się na ekranie znacznie przyspieszy pracę programu, więc w tym kierunku trzeba by kombinować, dlatego że aktualizowanie interfejsu to dość kosztowna operacja;

  2. Z tego co widzę, nie ma możliwości wyłączenia odświeżania komponentu; Być może szybciej uda się wykonać tworzenie tych komponentów, jak ukryjesz ScrollBoxa na czas tworzenia, a potem już gotowego pokażesz na ekranie; Trzeba by pokombinować.

2

Zastanów się nad TFrame

0

Pociągnę jeszcze temat tworzenia "listy" komponentów przy użyciu pojedynczych deklaracji jak zaproponował @furious programming co prawda panel jeden pod drugim jak w przykładzie: Dynamiczne tworzenie komponentów bez zadeklarowanej tablicy ale to bez znaczenia.

Pytanie:

  • Skoro jest lista paneli - wierszy to wypadało by mieć możliwość klikania na jeden z nich - zaznaczania

  • Na panelu jest np label wiec zarówno TPanel.OnClick jak i TLabel.OnClick będą wywoływały tę samą procedurę odpowiadająca za zaznaczenie - zmianę kolory np na niebieski

  • Nazwy w danym wierszu wyglądają np. tak:

panel12
label12
  • Czy są inne możliwości rozpoznania "numeru" komponentu niż rozgryzanie
TPanel(Sender).Name
TLabel(Sender).Name

żeby pobrać coś co by było równoznaczne z wierszem? Myślałem o zmianie Tag przy tworzeniu .. ale tag się może przydać do innych rzeczy.

W skrócie Chodzi o to że jak kliknę na **Panel **i **Label **to ponieważ mają wspólny **OnClick **to przydało by się coś "krótkiego" dającego nr wiersza. Jak nie to zawsze pozostaje sprawdzenie czy to nazwy panela czy to nazwy Labela osobno.
Ale może coś jest szybszego.

Cały zabieg po to ze wtedy wrzuciłbym to w OnClick w klasie TRodzic. która nie będzie wiedzieć z jakimi komponentami ma do czynienia TPotomek.

2
  1. Używaj TFrame.
  2. Tworząc natychmiast pakuj do tablicy, zaś w Frame.Tag wstaw index tego Frame w tablice.

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