TMenuItem a reference na objekty

vložil Radek Červinka 14. prosince 2011 21:32

Poněkud zavádějící nadpis, ale jedná se o případ, kdy potřebujeme mít asociaci mezi položkou menu (nebo něčím podobným) a nějakou datovou strukturou.

Poslední dobou jsem potřeboval vytvářet dynamicky položky menu a zároveň k nim mít vazbu na nějakou datovou strukturu. Za začátku jsem to řešil jako odkaz přes Tag někam, případně v Tag jsem měl přímo objekt, ale to byl problém s uvolňováním a tak mne napadlo jiné řešení (předpokládám, že mi v komentářích všichni napíší že je to stará známá věc - ale mne to prostě předtím nenapadlo).

Jádrem pudla je, že do Tag vložíme objekt, ale který je následníkem TComponent a jako vlastníka určíme patřičnou položku menu. Tj. pokud se uvolní položka menu, zároveň uvolní i svůj asociovaný objekt a my se nemusíme o nic starat. Já vím primitivní - ale mne to prostě nikdy dřív nenapadlo.

type
  TForm1 = class(TForm)
    pop: TPopupMenu;
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

  TItemInfo = class (TComponent)
  public
    iId: Integer;
    sHelp: string;
    constructor Create(Owner:TComponent; ID:Integer; const s:string); reintroduce;
  end;

{$IFDEF CPUX64}
  IntPtr = NativeInt;
{$ELSE}
  IntPtr = Integer;
{$ENDIF}

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
  function moAddItem(const sCaption:string; iId: Integer; const sHelp:string):TMenuItem;
  begin
    Result := TMenuItem.Create(pop);
    Result.Caption := sCaption;
    Result.Tag := IntPtr(TItemInfo.Create(Result, iId, sHelp));
    pop.Items.Add(Result);
  end;
begin
  moAddItem('Pol1', 1, 'help1');
  moAddItem('Pol2', 2, 'help2');
  moAddItem('Pol3', 3, 'help3');
  moAddItem('Pol4', 4, 'help4');
  moAddItem('Pol5', 5, 'help5');

end;

{ TItemInfo }

constructor TItemInfo.Create(Owner:TComponent; ID: Integer; const s: string);
begin
  inherited Create(Owner);
  iId := ID;
  sHelp := s;
end;

Klíčem je řádek 36, kdy nově vytvořené položce vytvoříme asociovaný objekt a jako vlastníka dáme tu vytvořenou položku. Obdobně by to mohlo fungovat pro položky treeview (ale tady bych šel asi do Virtual TreeView) nebo další komponenty.

Jinak přístup je pak samozřejmě TItemInfo(item.Tag).

Alternativou (možná i asi správnější, ale zase tím získáte závislost vašich datových struktur na VCL) může být podědění TMenuItem, kde přidáme naše položky a místo instance TMenuItem budeme přidávat instance našeho následníka.

Snad to aspoň někomu pomůže.

Tagy: , ,

Praxe

Komentáře

15.12.2011 13:57:51 #

Anonym

Tohle raději řeším potomkem TMenuItem, který má potřebnou property. Překladač mi pak navíc ohlídá typovou správnost a nemusím dělat přetypování zpátky na "cosi".

Anonym

15.12.2011 13:58:48 #

Anonym

Ha, a teď vidím, že je to v čláku napsané jako alternativa.

Anonym

17.12.2011 4:07:51 #

daemon_x

Teď si trochu zašťourám :)

"do Tag vložíme objekt" - do Tag vložíme ukazatel na instanci objektu ...
... což ale nemusíme, protože je kvůli 2 fieldům vytváření instancí objektů docela overkill; pro tento případ by se hodilo použít interposed classes; není třeba vytvářet objekty, ale jenom si rozšířit o dané fieldy stávající třídu TMenuItem

type
  TMenuItem = class(Menus.TMenuItem)
  public
    ID: Integer;
    Help: string;
  end;

procedure TForm1.FormCreate(Sender: TObject);

  procedure AddItem(const ACaption: string; const AID: Integer; const AHelp: string);
  var
    Item: TMenuItem;
  begin
    Item := TMenuItem.Create(PopupMenu1);
    Item.ID := AID;
    Item.Help := AHelp;
    Item.Caption := ACaption;
    PopupMenu1.Items.Add(Item);
  end;

begin
  AddItem('Pol1', 1, 'help1');
  AddItem('Pol2', 2, 'help2');
  AddItem('Pol3', 3, 'help3');
  AddItem('Pol4', 4, 'help4');
  AddItem('Pol5', 5, 'help5');
end;

Taky není vhodné používat jako výsledek funkcí objekty v ní vytvořené ...
Dávám 3 z 5 :-)

daemon_x

17.12.2011 6:59:57 #

oxo

ten alternativni zpusob se mi zda asi tak o 1000% prehlednejsi a jasnejsi. nehlede na to, ze tag je takova vecicka, kterou radsi moc nepouzivat...

oxo

17.12.2011 10:37:29 #

Radekc

>Taky není vhodné používat jako výsledek funkcí objekty v ní vytvořené ...

S tím nesouhlasím, zvláště ne v uvedeném případě (kdy je to interní funkce te metody). Čemu jsi tím vlastně pomohl? Docela by mne zajímal důvod.

Radekc

17.12.2011 10:42:09 #

Radekc

Navíc když je to funkce, tak si návratovou hodnotu mohu zapamatovat a použít ji třeba jako Parent menu pro další úroveň, což v tvém případě nelze. Popř. s ní pak můžu ještě provádět další speciální věci, které se netýkají ostatních položek.

No jsem zvědav na tvé důvody a plusy tvého řešení.

Radekc

17.12.2011 10:58:53 #

Radekc

>ten alternativni zpusob se mi zda asi tak o 1000% prehlednejsi a jasnejsi

Jo to je pravda, je to čistější z hlediska jazyka. Ale zase si zavlečeš do datových struktur závislost na TMenuItem. Což může být jedno, ale nemusí.

Radekc

17.12.2011 11:18:31 #

daemon_x

> Taky není vhodné používat jako výsledek funkcí objekty v ní vytvořené ...
> Docela by mne zajímal důvod

Kvůli přehlednosti ? Když si rozhážu konstruktory objektů do mnoha funkcí, pak je hledat tak s lucernou. Jasně u lokální funkce je to v pohodě, tam se neztratí.
Syntaktický důvod to nemá, tak jako tak se předává pointer na instanci objektu a jestli se předá jako deklarovaný parametr nebo jako výsledek funkce je jedno (tedy pravděpodobně, viděl jsem rady typu - nepoužívat předávání přes Result).

Ale hlavně bych se vyvaroval použití Tagu; jestli je to v projektu na kterém děláš sám, tak pohoda, ale vsadím se, že kdyby ne, bylo by jenom otázkou času, kdy ti někdo rozhodí ukazatele nějakou magickou konstantou :-)

daemon_x

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ů