Nahrazení frames za vložené formuláře

vložil Radek Červinka 5. června 2014 00:08

Tento trik určitě většina programátorů zná, ale třeba to někomu pomůže a mimochodem to ukazuje genialitu návrhu Delphi.

Jistě je Vám známo co jsou to frames - jedná se o pseudo okna, která můžete v IDE designovat a pak je používat v jiných formulářích. Je to celkem slušné řešení, ale není to úplně ono. Má to jisté omezení, sem tam IDE nefunguje 100% podle toho co si představuji atd. Postupně jsem přešel na méně RAD přístup, a to "injektování" formuláře do jiného controlu na jiném formuláři (já tomu říkám osobně dokování, ale dokování je v Delphi něco jiného).

Pro mne je to velmi flexibilní věc: např. můžete jiné formuláře injektovat do dynamicky vytvářených záložek PageControlu, nebo stejný formulář můžete přímo zobrazit atd. Za mne je hlavním důvodem modularita, případně OnCreate atd.

Mějme dva formuláře: na jednom bude panel, do kterého budeme injektovat za běhu jiný formulář (tj. formulář 2) , např. s TMemo (nebo samozřejmě s čímkoliv jiným). Navíc si k panelu přidáme dvě tlačítka, ať se nám to pro začátek lépe ladí. Jedno tlačítko formulář injektuje, druhého uvolní.

type
  TForm21 = class(TForm)
    Panel1: TPanel;
    btnDock: TButton;
    btnFree: TButton;
    procedure btnDockClick(Sender: TObject);
    procedure btnFreeClick(Sender: TObject);
  private
    { Private declarations }
    FAnotherForm:  TForm22;
  public
    { Public declarations }
  end;

var
  Form21: TForm21;

implementation

{$R *.dfm}



Abych parafrázoval klasika: pokud se v prvním jednání objeví na stěně puška, musí ve třetím jednání vystřelit. Takže pokud máme v deklaraci metody, musíme je implementovat:

procedure TForm21.btnDockClick(Sender: TObject);
begin
  // dock
  FAnotherForm := TForm22.Create(Self);
  FAnotherForm.Parent := Panel1;
  FAnotherForm.Align := alClient;
  FAnotherForm.BorderIcons := [];
  FAnotherForm.BorderStyle := bsNone;
  FAnotherForm.Show;
  FAnotherForm.Memo1.Text :='delphi.cz';
end;

procedure TForm21.btnFreeClick(Sender: TObject);
begin
  FAnotherForm.Parent := nil;
//  Self.RemoveComponent(FAnotherForm);
  FreeAndNil(FAnotherForm);

end;

První obsluha je celkem jasná, vytvoříme instanci formuláře (vlastníkem, a tedy ten kdo ho uvolní je formulář via Self), přes Parent ho přiřadíme do panelu.

Tohle je častý omyl u začátečníků - nepleťte si vlastníka (toho kdo bude objekt uvolňovat v okamžiku kdy vlastník zanikne) a ten prvek, který bude vizuálním otcem. Mohl bych napsat jako vlastníka Panel1, a bylo by to korektní - ale takto se můj formulář zruší, až se zruší ten druhý formulář. Mezitím mohu cvičit s Panel1 a nebude to mít vliv na život injektovaného formuláře. Možná to trošku komplikuji, ale chtěl jsem ukázat, že vlastník a parent nemusí být ten sám.

Pokud neprovedete přiřazení Parent tak neuvidíte nic, ale objekt bude existovat.

Několik málo komponent nemá dostupný Parent (myslím, že např. TWebBrowser), pokud mermomocí potřebujete Parent i v těchto případech, můžete použít háček s přetypováním na předchůdce.

Další řádky v uvedené metodě jen schovávají některé prvky formuláře a formulář zobrazí.

Druhá metoda je odstranění dynamicky injektovaného prvku. Pro jistotu - Delphi automaticky při uvolnění vlastníka uvolní i injektovaný formulář, ale někdy to chcete udělat sami - třeba protože tam chcete injektovat něco jiného. Mimochodem v těchto případech není třeba vždy uvolňovat, stačí jen pracovat s Parent (vhodně nastavit na nil atd., a zase zpět). Prakticky stačí jen to volání Free, to provede ty ostatní věci uvedené před ním.

Tento princip Vám např. umožní dynamicky dokovat Váš toolbar z okna někam jinam, třeba do Ribbonu.

Tagy:

Začátečníci

Komentáře

5.6.2014 11:19:05 #

JaroB

Nahrazení frames za vložené formuláře – kapku jiný způsob práce s podřízeným oknem

Dělám to asi podobně.

Frames nepoužívám, protože jsem si před lety jimi zaplevelil aplikaci a pak nevěděl jak z toho (resp. bylo to ohromně pracné).

Následující mechanismus lze použít v podstatě na každý form, který chceme změnit na subform.

Deklarace v subformu, v jednoduché class funkci, která vytvoří a umístí subform na parent panel nadřízeného formuláře.

  public
    { Public declarations }
    class function CreateAsSubform(AOwner: TCustomForm; AParent: TPanel): TfFlow;

implementace je následující

class function TfFlow.CreateAsSubform(AOwner: TCustomForm; AParent: TPanel): TfFlow;
begin
  Result := TfFlow.Create(AOwner);
  Result.Parent := AParent;
  Result.ParentWindow := AParent.Handle;
  Result.Color := AParent.Color;
  Result.Top := 0;
  Result.Left := 0;
  Result.Align := alClient;
  Result.BorderStyle := Forms.bsNone;
  Result.ControlStyle := Result.ControlStyle + [csParentBackground];
  Result.Invalidate;
end;

Pár poznámek ke kódu:
Nastavení ControlStyle je pouze proto, že drží background, i když je položen na tabsheet pagecontrolu (nemrší to styly, ani barvy a na různých Windows to vypadá (skoro) stejně). Také je lepší subform položit na panel (kvůli themes je to dokonce podmínka), proto jsem zvolil právě panel jako parent. Protože je to okno, tak jsem nastavil i jeho handle do ParentWindow.
Invalidate je tam jaxi navíc, jenom upraví prvečky, pokud by subform měl vlastní událost OnResize, nebo tak.

A jak to použít?

Deklarace proměnné pro subform (je lepší public, ale záleží na situaci) na nadřízeném formuláři nebo může zůstat jako globální proměnná u formuláře (opět dle situace):

public
  fFlow: TfFlow;


Např. v OnCreate nadřízeného formuláře (pro statickou vazbu) nebo pod nějakou vhodnou událost např.

  fFlow := TfFlow.CreateAsSubform(Self, pnProduct);
  Screen.Cursor := crHourGlass;
  try
    //zde volání metod subformu např. pro nastavení výchozích hodnot nebo adaptace na nadřízený formulář
    fFlow.Reset;
    fFlow.CopyDataFrom(Self);
    //přesýpací hodiny jsou pro časově náročnější operace např. když subform otevírá rovnou DB nebo tak. Nemusí to být, jak kdo potřebuje.
  finally
    Screen.Cursor := crDefault;
  end;
  fFlow.Show;

A zrušení obdobně pod nějakou událost (nebo se o vymetení postará parent resp. jeho owner tj. nadřízený form)

  if Assigned(fFlow) then
    //před tím může být volání úklidových metod subformu atp. a potom úklid na nadřízeném formuláři (např. zneviditelnění toho panelu)
    FreeAndNil(fFlow);


Závěrem můžu říct, že se to vyplatí a zjednoduší se i správa podřízených oken. Mám s tím velmi dobré zkušenosti a využívám to ve většině aplikací (a uživatelů jsou tisíce).  Používám to už od Delphi 2006, ale v jednodušší formě jsem to měl v aplikacích už v dobách Delphi5.

JaroB

5.6.2014 23:01:28 #

Daniel Andrascik

napodobne to tak pouzivam tiez dlhe roky a v mnohych projektoch. Uz teraz neviem v ktorom ale tusim v D2007 mi nefungovalo priradenie Top a Left pozicie takehoto subformu. V pripade ze nepouzivate Align := alClient ale napriklad chcete umiestnit tri taketo formy vedla seba tak sa mi aj tak vsetky tri umiestnili na poziciu 0,0. Musel som to vyriesit tak ze som hodnoty Top a Left musel este raz priradovat vo vlastnosti OnShow. Mozno existuje lepsie riesenie, ale mne to problem vyriesilo a dalej som nebadal po tom kde problem vznika

Daniel Andrascik

6.6.2014 9:11:41 #

JaroB

Tady je právě ta finta, že každý subform se umísťujete na vlastní panel a jeho align si řídí nadřízený formulář jako bezprostřední vlastník.

JaroB

6.6.2014 15:11:55 #

Daniel Andrascik

vacsinou pouzivam tiez panel alebo tabsheet ako podklad pre subform a vlastnost align nastavujem na alClient. Vtedy ziaden problem nehrozi na ziadnej verzii delphi. Ale obcas som dynamicky umiestnoval mnoho takychto subformov vedla seba, alebo pod seba na jedneho a toho isteho kontainera (panel, alebo tabsheet) a tam som musel suradnice umiestnenia takto osetrovat, pretoze niketore delphi s tym pracovali troska inac.

Daniel Andrascik

Komentování ukončeno

Naše nabídka

MVP
Ing. Radek Červinka - Embarcadero MVP
profil na linkedin, Twitter:@delphicz

Nabízím placené poradenství a konzultace v oblasti programování a vývoje SW.
Dále nabízíme i vývoj speciálního software na zakázku.

Neváhejte nás kontaktovat (i ohledně reklamy).

love Delphi

O Delphi.cz

Delphi je moderní RAD nástroj podporující tvorbu nativních aplikací pro platformu Win32, Win64, Mac OSX, Linux a na iPhone a Android.

Delphi.cz je nezávislý portál pro uživatele Delphi. Portál není koncipován pro úplné začátečníky, i když i ti se zde nebudou nudit, ale spíše na programátory, kteří již něco znají a chtějí své znalosti dále rozvíjet a sledovat novinky.

Poslední komentáře

Comment RSS

Dle měsíců