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.

    1type
    2  TForm1 = class(TForm)
    3    pop: TPopupMenu;
    4    procedure FormCreate(Sender: TObject);
    5  private
    6    { Private declarations }
    7  public
    8    { Public declarations }
    9  end;
   10
   11  TItemInfo = class (TComponent)
   12  public
   13    iId: Integer;
   14    sHelp: string;
   15    constructor Create(Owner:TComponent; ID:Integer; const s:string); reintroduce;
   16  end;
   17
   18{$IFDEF CPUX64}
   19  IntPtr = NativeInt;
   20{$ELSE}
   21  IntPtr = Integer;
   22{$ENDIF}
   23
   24var
   25  Form1: TForm1;
   26
   27implementation
   28
   29{$R *.dfm}
   30
   31procedure TForm1.FormCreate(Sender: TObject);
   32  function moAddItem(const sCaption:string; iId: Integer; const sHelp:string):TMenuItem;
   33  begin
   34    Result := TMenuItem.Create(pop);
   35    Result.Caption := sCaption;
   36    Result.Tag := IntPtr(TItemInfo.Create(Result, iId, sHelp));
   37    pop.Items.Add(Result);
   38  end;
   39begin
   40  moAddItem('Pol1', 1, 'help1');
   41  moAddItem('Pol2', 2, 'help2');
   42  moAddItem('Pol3', 3, 'help3');
   43  moAddItem('Pol4', 4, 'help4');
   44  moAddItem('Pol5', 5, 'help5');
   45
   46end;
   47
   48{ TItemInfo }
   49
   50constructor TItemInfo.Create(Owner:TComponent; ID: Integer; const s: string);
   51begin
   52  inherited Create(Owner);
   53  iId := ID;
   54  sHelp := s;
   55end;

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.


Nabízíme Delphi školení a konzultace na různá témata, primárně ve Vaší firmě.

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

Partial English version.

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 nebo burzy práce).

Pokud chcete podpořit tento server libovolnou částkou, můžete použít PayPal. Moc děkuji.

Delphi Certified Developer

O Delphi.cz

Delphi je jediný moderní RAD nástroj podporující tvorbu nativních aplikací pro platformu Win32, Win64 , Mac OSX a na iPhone a Android (s výhledem na další platformy díky FireMonkey) na současném trhu (včetně Windows 8.1).

V současnosti je světová komunita přes dva miliónů vývojářů.

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.

Anketa

Poslední komentáře

Comment RSS