Dynamiczne tworzenie komponentów na własnym panelu

0

witam - jestem tu nowy, więc proszę o wybaczenie, jeśli coś zrobię źle!
Napisałem własny komponent dziedziczący po TPanelu (będę go nazywał dalej panelem) i w tym komponencie tworzę składnik będący ScrollBoxem (będę go nazywał dalej boxem). Jeśli w trakcie działania programu utworzę jakąś kontrolkę w boxie (podając boxa jako Parent), to jest wszystko ok.
Kłopot w tym, że w trakcie projektowania komponenty nie lądują w boxie.
Dodanie w konstruktorze panelu dla boxa

box.ControlStyle:= box.ControlStyle+ [csAcceptsControls];

zmieniło tyle, że kontrolki lądują pozornie w boxie: tzn. graficznie w nim są, przesunięcie za granicę włącza paski przewijania, ale w drzewie struktury dodana kontrolka jest w formie, a nie w moim komponencie; po uruchomieniu programu dodanej kontrolki po prostu nie ma:(
Dodam jeszcze, że w drzewie struktury nie widać mojego boxa a jedynie panel - być może to jest powodem; dodanie boxa jako własności nic nie daje (oprócz tego, że na liście własności panelu mam mam rozwijalną własność box).

0

A nie możesz po prostu dodawać kontrolek do normalnego scrollboxa? Co chcesz osiągnąć tworząc swój panel, a na nim scrollboxa? Prawdopodobnie nie jest to do niczego potrzebne, a tylko wprowadza zamęt.

0

@Juhas niestety jest mi to potrzebne, bo ma zastąpić formy mdi child w pewnej aplikacji i rozszerzyć kontrolę nad nimi i ich możliwości

0

Klasa TPanel (a właściwie TCustomPanel) zawiera już zdefiniowany styl csAcceptsControls w swoim konstruktorze, więc nie musisz tego sam robić; Poza tym sam Parent raczej nie wystarczy - jako Owner w konstruktorze komponentu też podaj referencję tego panelu i powinno być Ok:

var
  btnNew: TButton;
begin
  btnNew := TButton.Create(TutajPanel);
  btnNew.Parent := TutajPanel;

Spróbuj - być może tu leży problem.

0

@furious programming nie chodzi o złapanie kontrolek w panelu tylko w boxie;

  btnNew := TButton.Create(TutajPanel);
  btnNew.Parent := TutajPanel;

niestety działa w czasie wykonania, ale nie w czasie projektowania w "form designerze"(? nie wiem, czy to prawidłowa nazwa)

0

Pokaż kod komponentu, bo nie chce mi się dłużej zgadywać.

0

@furious programming po dużym uproszczeniu to mniej więcej tak:

unit TestPanel;
interface
uses
  System.SysUtils, System.Classes, Vcl.Controls, Vcl.ExtCtrls, Vcl.Forms;

type
  TTestPanel = class(TCustomPanel)
  private
    FWnetrze : TScrollBox;
  published
    property Wnetrze : TScrollBox   read FWnetrze   write FWnetrze;
  end;
procedure Register;

implementation

constructor TTestPanel.Create(AOwner: TComponent);
begin
    inherited;
    FWnetrze := TScrollBox.Create(Self);
    FWnetrze.Parent := Self;
end;
end.

próbowałem też

constructor TTestPanel.Create(AOwner: TComponent);
begin
    inherited;
    FWnetrze := TScrollBox.Create(AOwner);
    FWnetrze.Parent := Self;
end;

i wtedy w "designerze" mogę wrzucić do boxa kontrolki (choć w drzewie struktury jest to twór bezimienny), tylko że w czasie wykonania to nie jest "mój" box (należy do panelu, ale nie jest dostępny przez wskaźnik FWnetrze).

0

Nie wiem o czym Ty piszesz... Nazywaj rzeczy po imieniu, bo ciężko się połapać...

Zarówno klasa TPanel, jak i TScrollBox mają dodany styl csAcceptControls, więc wszystko powinno działać bez zarzutu; W konstruktorze panelu tworzysz komponent klasy TScrollBox, któremu do AOwner i Parent podajesz referencję panelu, a przy dynamicznym tworzeniu komponentów podajesz im w AOwner i Parent referencję ScrollBoxa;

Innego sposobu nie widzę, a opisany wyżej nie ma prawa niedziałać;

PS: Jeżeli udostępniasz przez właściwość jakiś obiekt, to raczej nie używaj w ten sposób settera; Dzięki temu co jest teraz, można spowodować wycieki pamięci, za pomocą takiego kodu:

var
  tpFoo: TTestPanel;
begin
  tpFoo := TTestPanel.Create(MainForm);
  tpFoo.Parent := MainForm;

  tpFoo.Wnetrze := nil;  // alleluja

I dziękuję - tracisz referencję do wcześniej utworzonego FWnetrze; Na upartego i tak da się ten obiekt zwolnić, choćbyś go głęboko ukrył i w ogóle nie udostępniał, jednak ten write nie jest potrzebny.

0
furious programming napisał(a):

Nie wiem o czym Ty piszesz... Nazywaj rzeczy po imieniu, bo ciężko się połapać...

:)wcześniej pisałem "ogólnie" (o panelu i boxie) - potem wkleiłem coś podobnego zrobionego na próbę przez kolegę - stąd nie dokładnie te same nazwy

furious programming napisał(a):

a przy dynamicznym tworzeniu komponentów

chodzi o to, że chcę to wykorzystywać nie dynamicznie (w czasie wykonania), tylko już w "designerze" (czyli przy projektowaniu wizualnym - po są to w końcu te komponenty;) - o czym zresztą na początku napisałem ;)

furious programming napisał(a):

PS: Jeżeli udostępniasz przez właściwość jakiś obiekt, to raczej nie używaj w ten sposób settera; Dzięki temu co jest teraz, można spowodować wycieki pamięci

za tę uwagę dziękuję, choć tak naprawdę tego ScrollBoxa w ogóle nie chciałbym dawać jako właściwość - to raczej gest rozpaczy przy wieeeeelu nieudanych próbach:(

0

Coś to nie hula jak należy - w designerze wszystko dobrze wygląda, jednak po uruchomieniu komponenty wewnątrz scrollboxa nie są widoczne;

Edit1: Nie tyle są niewidoczne, co już zwolnione z pamięci;
Edit2: A nie mozesz użyć ScrollBoxa zamiast Panelu?

Edit3: Z tym designerem będą problemy, dlatego że ScrollBox jest tworzony dynamicznie; Na pewno będzie wyglądać i działać dobrze, jeśli ScrollBoxa będziesz ręcznie kładł na swoim panelu;

Edit4: Albo zrobić sobie Frame'a, dodać do palety i używać.

0

@furious programmingrozumiem - ja po kilkunastu godzinach męczenia się z tym dziś (nie wspominając o dniach wcześniejszych) też; dzięki za dzisiaj, a jak na coś jeszcze wpadniesz to z góry dziękuję

1

@qqprim - w standardowy sposób tego nie zrobisz, dlatego że cała zawartość tak stworzonego scrollboxa we wnętrzu panelu nie wyląduje w pliku .dfm; Dlatego też po kompilacji i uruchomieniu projektu, scrollbox będzie pusty; Trzeba kombinować inaczej, coś w stylu komponentu TLabeledEdit, który składa się z dwóch komponentów, a ich dane (obu) lądują w pliku .dfm;

Jak coś wymyślę to dam znać.

0

A ja widzę pewną rzecz. Nie pamiętam dokładnie, jak działa wywoływanie metod rodzica, ale zamiast:
inherited;

dałbym:
inherited(AOwner);

(w konstruktorze oczywiście)

0
furious programming napisał(a):

@qqprim - w standardowy sposób tego nie zrobisz, dlatego że cała zawartość tak stworzonego scrollboxa we wnętrzu panelu nie wyląduje w pliku .dfm;
też to z przykrością zauważyłem :(

furious programming napisał(a):

...coś w stylu komponentu TLabeledEdit, który składa się z dwóch komponentów, a ich dane (obu) lądują w pliku .dfm
podejrzewam tu dziedziczenie wielokrotne, więc raczej to nie są komponenty składowe, tylko po prostu dziedziczone składowe w klasie (choć się zastanawiam jak, bo w delphim chyba nie ma dziedziczenia wielokrotnego w przeciwieństwie do C++)

furious programming napisał(a):

Jak coś wymyślę to dam znać.

dzięki

Ale wróciłbym do wcześniejszego Twojego pomysłu z ramkami (bo to mi nawet bardziej by odpowiadało). Napisanie takiego komponentu dziedziczącego po ramce, a nie panelu raczej nie powinno nastręczyć trudności, tylko nie mam zielonego pojęcia, jak tę nibyramkę dorzucić do File->new->other->by tu się pojawiła obok Unit, VCL Frame itd. i wyklikanie tego spowodowało otwarcie nowego pliku z klasą nibyramka i odpowiadającym jej .dfm. Chociaż problem scrollboxa wewnątrz też pozostanie nierozwiązany (mam pomysł taki "łopatologiczny", by do etapu kompozycji umieścić tam shape'a, a przy pierwszym uruchomieniu przenieść wszystkie komponenty do scrollboxa).

Najlepiej, gdyby się udało jakoś wymusić na designerze, by mój wewnętrzny scrollbox traktował jak swój i zapisywał zawarte w nim komponenty do dfm. :) :) :)

0

@Juhas
1.

Juhas napisał(a):

inherited(AOwner);(w konstruktorze oczywiście)
nie przechodzi kompilacji

  1. kłopot nie jest z konstruktorem panelu, tylko (jeśli już) scrollboxa z wewnątrz (który jest składnikiem klasy), a dla scrollboxa wołam konstruktor ja:
  • próba z self - nie można dokładać nic do scrollboxa z poziomu z designera
  • próba z AOwner - tworzony jest nienazwany komponent na który mogę kłaść komponenty, ale jego właścicielem nie jest nibyokno i na etapie wykonania nie ma do niego dostępu (choć zastanawiam się, czy jednak nie dałoby się go jakąś sztuczką przechwycić)
0
  1. Można zapisać albo samo inherited, albo po tym słówku podać nazwę metody i jej parametry, czyli inherited Create(AOwner); Ja stosuję tę drugą wersję, bo według mnie jest czytelniejsza; Szkoda tylko, że code insight nie zawsze podpowiada;
  2. To już wykluczyliśmy, problemem dotyczy dynamicznie tworzonego scrollboxa;

podejrzewam tu dziedziczenie wielokrotne, więc raczej to nie są komponenty składowe, tylko po prostu dziedziczone składowe w klasie (choć się zastanawiam jak, bo w delphim chyba nie ma dziedziczenia wielokrotnego w przeciwieństwie do C++)

To są dwa osobne komponenty; Napiszę Ci jak to wygląda w Lazarusie, bo w moim Delphi7 zepsuło się szukanie deklaracji;

TLabeledEdit dziedziczy z TCustomLabeledEdit i upublicznia (w sekcji published) jedynie właściwości - taka praktyka; Klasa TCustomLabeledEdit dziedziczy ze zwykłego TCustomEdit i posiada w sekcji private komponent etykiety - FEditLabel klasy TBoundLabel; Ten label tworzony jest w konstruktorze klasy edita, któremu do właściwości ControlStyle dodaje się styl csNoDesignSelectable (dzięki temu nie da się go zaznaczyć w oknie designera, bo zaznacza się cały edit); Na koniec etykieta ta dodana jest w sekcji public jako właściwość read-only (w docelowej klasie TLabeledEdit już w sekcji published); I to w sumie tyle - działa, label nie znika, jego dane lądują w .dfm;

Z tym TLabeledEdit jest nieco inaczej, bo są to dwa komponenty obok siebie, a nie jeden w drugim, więc po zmianie rodzica czy pozycji edita, trzeba też przesuwać label; W Twoim przypadku będzie prościej, bo scrollbox zawsze będzie wewnątrz panelu, więc nie trzeba go będzie przesuwać;

Może coś w ten sposób da się osiągnąć;

Ale wróciłbym do wcześniejszego Twojego pomysłu z ramkami (bo to mi nawet bardziej by odpowiadało). Napisanie takiego komponentu dziedziczącego po ramce, a nie panelu raczej nie powinno nastręczyć trudności, tylko nie mam zielonego pojęcia, jak tę nibyramkę dorzucić do File->new->other->by tu się pojawiła obok Unit, VCL Frame itd. i wyklikanie tego spowodowało otwarcie nowego pliku z klasą nibyramka i odpowiadającym jej .dfm.

Tego się tak nie robi :]

Tworzysz sobie ramkę, nadajesz jej nazwę i inne właściwości, kładziesz na nią scrollboxa (choć sama ramka także może posiadać swoje scrollbary) i ogólnie ustawiasz wszystkie komponenty, które każda ramka mieć powinna; Następnie zapisujesz sobie moduł ramki na dysku oraz klikasz PPM na tło ramki i wybierasz opcję Add To Palette; W oknie dialogowym, które się otworzy, możesz nadać nazwę takiemu komponentowi, wybrać zakładkę, nadać ikonkę itd.; Klikasz przycisk Ok i na palecie komponentów masz komponent ze swoją ramką; Teraz pozostaje tylko otworzyć swój projekt, wybrać komponent ramki i upuścić go na formularzu w jednej czy kilku kopiach;

Tylko pamiętaj, że ramka posiada swoje komponenty, których nie możesz usunąć z poziomu designera formularza, który taką ramkę posiada; Nie możesz też dodać komponentów do takiej ramki; Ogólnie to ramka jest niezmienna, może zawierać tylko te komponenty, które ułożyłeś na niej podczas jej projektowania; Właściwości komponentów należących do ramki zmienić możesz, jednak liczby komponentów już nie (przynajmniej z poziomu designera);

Nie wiem jak to rozwiązuje Twój problem, ale jak teraz "świeżym okiem" patrzę na ten wątek to raczej ramki w niczym Ci nie pomogą i raczej trzeba będzie pozostać przy zwykłych komponentach.

0

Coś mi się wydaje (aktualnie nie mam czasu na eksperymenty) że póki w komponencie ten TScrollBox nie będzie miał własnego edytora właściwości jak choćby TTabSheet w TPageControl to wielkie G będzie z tego. To tylko moja nie poparta żadnymi eksperymentami ani doświadczeniem (sam byłem trochę zdziwiony jak zobaczyłem że nie działa ale jak pisałem nie mam czasu wnikać) opinia.

0
furious programming napisał(a):

TLabeledEdit...etykieta dodana jest... jako właściwość ... label nie znika, jego dane lądują w .dfm;
jak tworząc scrollbox ustawię właściciela na właściciela panelu to jego zawartość zapisuje się w .dfm(!) Już wykombinowałem, by temu bezimiennemu scrollboxowi nadawać nazwę taką, jak ma panel+ coś jeszcze (by nie było kolizji nazw). A w momencie uruchomienia programu wskaźnik do scrollboxa przenieść na ten dziwnie nazwany scrollbox. Jeszcze tylko nie wiem do końca jak to zrobić (jest zdaje się funkcja wyszukująca komponenty po nazwie, ale w konstruktorze panelu jest chyba za wcześnie na taką operację, gdyż wartości własności (np. Name) są zdaje się nadawane później; przydało by się zdarzenie (komunikat) "po wykreowaniu" :) )

furious programming napisał(a):

Tego się tak nie robi :]
Tworzysz ramkę, ...kładziesz na nią scrollboxa... ustawiasz wszystkie komponenty... i na palecie komponentów masz komponent ze swoją ramką;... Nie możesz też dodać komponentów do takiej ramki...
takie rozwiązanie mnie nie interesuje. Ale wolałbym rozwiązanie z ramką wzorcową (jakby szablonem), bo ten komponent ma służyć do tworzenia (specyficznych) podokien, więc najlepiej gdyby każdy był tworzony z mojego wzorca (z moimi funkcjonalnościami) w osobnym pliku. Da się coś takiego zrobić, bo wraz z jakimś pakietem mam formę (wzorzec) w opisanym we wcześniejszym poście miejscu.

0

Da się coś takiego zrobić, bo wraz z jakimś pakietem mam formę (wzorzec) w opisanym we wcześniejszym poście miejscu.

A masz źródła tego czegoś?

0
furious programming napisał(a):

A masz źródła tego czegoś?
zadajesz mi niedyskretne pytanie :). Nie jestem biegły w Delphi (środowisku). Ktoś mi je wgrywał i wgrał jeszcze jakieś dodatki i raczej z nich mam coś takiego (w file->new->other) jak VCL Metropolis UI Form - stąd moje domniemanie, że taki wzorzec formy da się zrobić. Nie wiem, czy ten Metropolis jest darmowy i czy jego źródła są dostępne :( i czy nie strzelam strasznej gafy.

0

Pytam, bo jeżeli masz źródła to zawsze można odszukać to "coś", sprawdzić jak to ktoś zrobił i najzwyczajniej w świecie podpierdzielić pomysł - a co :]

Nie znam tej paczki, w sumie to już od dawna nie pisałem w Delphi, a jak pisałem to w staruśkim Delphi7, na dodatek w wersji Personal, więc bez źródeł; Od dawna piszę pod Lazarusem i choć źródła RTL/LCL są, to znowu problemy z portowaniem kodu stworzonego dla Delphi;

Póki co nie mam pomysłu jak to dobrze wykonać, jednak da się to na pewno zrobić - jakieś rozwiązanie istnieje.

0
furious programming napisał(a):

Póki co nie mam pomysłu jak to dobrze wykonać, jednak da się to na pewno zrobić - jakieś rozwiązanie istnieje.
bardzo budujące stwierdzenie :)

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