Dynamiczne menu odwołanie do tagów

0

Witam

Mam problem z odwołaniem do tagów dynamicznie tworzonego Menu. Elementy menu pobierane sa z ini. chciałbym np. z pozycji buttona wybrać dany item z menu bez względu na caption.

Przekazywanie i tworzenie menu wykonuje poniższa procedura.

 begin
    NazwaPrzekaznika[i]:= PlikINI.ReadString('NazwyPrzekaznikow', 'Przekaznik_'+IntToStr(i+1)+'_Nazwa', 'PrzekaŸnik '+IntToStr(i+1));
    MenuItem:= TMenuItem.Create(MGlowne);
    MenuItem.Caption:= NazwaPrzekaznika[i];
    MenuItem.Tag:= i;
    MenuItem.OnClick:= MenuUruchomPrzekaznik;
    MGlowne.Items[1].Add(MenuItem);
  end;
    if(MGlowne.Items[0].Count > 0)then
 begin
    for i:= MGlowne.Items[0].Count-1 downto 0 do
      MGlowne.Items[0].Delete(i);
0

Czytam to kilka razy, ale nie bardzo wiem o co Tobie dokładnie chodzi. Odwołać się zawsze do tworzonych dynamicznie komponentów możesz rzutując na ich typ i wykorzystując funkcję FindComponent. Tylko oczywiście należy nadać komponentom sensowną własność Name i przy odwoływaniu sprawdzać czy to co zwróci FindComponent nie jest równe nil. Poza tym nie wiem do czego nadal wykorzystywać chcesz własność własność Tag. Rozumiem, że po ustawianiu jej na Iterator pętli (tutaj u Ciebie chyba będzie numerowny od U, a nie od I + 1, ale może tak chcesz) będziesz gdzieś ten numer wykorzystywał później w kodzie. Może ktoś inny coś jeszcze doradzi, bo ja do końca nie mogę zrozumieć co chcesz osiągnąć i w czym problem. Bo jeżeli chcesz się odwołać do MenuItemów, nie po własności Name, a po Tag to musiał byś napisać własną funkcję wyszukującą w pętli wszystkie elementy Menu i po ustaleniu że Tag wynosi tyle co podany argument funkcji zwracała by jako Result ten MenuItem i oczywiście robiła Break.

0

Ok to możne inaczej. MMenu zawiera dwa buttony widoczne z poziomu kompilatora 'cos' i 'cos2'. Program w momencie uruchomienia pobiera do kazdego buttona itemy z pliku ini, itemy pobierane nie sa zapisane w kodzie. Problem polega na tym ze chciałbym odwoływać się do tych dynamicznych itemow przez np standardowy button z formy, odwołanie nie możne byc po name bo go będę modyfikował w ini. jedyne co mi pozostaje to tagi które narasta z każdym dynamicznym 'pod' itemem. Tak jak napisałeś procedura która odwołuje się do większego kodu.

Poniżej cala procedura.

var
  PlikINI: TIniFile;
  i: Integer;
  j: Integer;
  MenuItem: TMenuItem;
begin
  PlikINI:= TIniFile.Create(ExtractFilePath(Application.ExeName)+'programy.ini');

  IloscProgramow:= PlikINI.ReadInteger('Ogolne', 'IloscProgramow', 0);
  IloscPrzekaznikow:= PlikINI.ReadInteger('Ogolne','IloscPrzekaznikow',0);
for i := 0 to IloscPrzekaznikow-1 do
  begin
    NazwaPrzekaznika[i]:= PlikINI.ReadString('NazwyPrzekaznikow', 'Przekaznik_'+IntToStr(i+1)+'_Nazwa', 'PrzekaŸnik '+IntToStr(i+1));
    MenuItem:= TMenuItem.Create(MGlowne);
    MenuItem.Caption:= NazwaPrzekaznika[i];
    MenuItem.Tag:= i;
    MenuItem.OnClick:= MenuUruchomPrzekaznik;
    MGlowne.Items[1].Add(MenuItem);
  end;
    if(MGlowne.Items[0].Count > 0)then
 begin
    for i:= MGlowne.Items[0].Count-1 downto 0 do
      MGlowne.Items[0].Delete(i);
 end;
  for i:= 0 to IloscProgramow-1 do
    begin
    NazwaProgramu[i]:= PlikINI.ReadString('Program '+IntToStr(i+1), 'NazwaProgramu', 'Bez nazwy');
    MenuItem := TMenuItem.Create(MGlowne);
    MenuItem.Caption := NazwaProgramu[i];
    MenuItem.Tag:= i;
    MenuItem.OnClick := WybranieProgramu;
    MGlowne.Items[0]. Add(MenuItem);
    for j:= 0 to IloscPrzekaznikow-1 do
    begin
      Przekaznik[i, j]:= PlikINI.ReadInteger('Program '+IntToStr(i+1), 'Przekaznik_'+IntToStr(j+1), 0);
      Wlasciwosci.log('Wczytuje prog.'+ IntToStr(i+1)+' ('+NazwaProgramu[i]+')'+#13#10+'Przekaznik: '+IntToStr(j+1)+' ('+NazwaPrzekaznika[j]+') ze stanem: '+IntToStr(Przekaznik[i, j]));
    end;
  end;
  PlikINI.Free;

0

Jeżeli chcesz odwołać się do tych itemów z procedur MenuUruchomPrzekaznik WybranieProgramu to w nich wystarczy sprawdzić TMenuItem(Sender).Tag. Wtedy po kliknięciu na dany MenuItem zorientujesz się który to jest.
Przy odwoływaniu z innych procedur musisz zrobić tak jak poradził olesio.

0

Odwołanie ma być z innej procedury np. onclick buttona. Wiec pozostaje przykład Olesia, niestety to pierwsze moje podejście do tego typu rzeczy, i powiem szczerze ze przydał by się jakiś przykładowy kod :)

1
tayamoto napisał(a):

powiem szczerze ze przydał by się jakiś przykładowy kod :)

Powiem szczerze że przydał by się jakiś dokładny opis problemu i co chcesz osiągnąć. Dopraw to jeszcze TBrainem, debuggerem i googlem.
Przykładowy kod jest dla osób które są zbyt głupie żeby się nauczyć gramatyki języka (paru takich mamy), ale ciebie póki co nie widziałem w tej grupie. Jak szukasz łatwych efektów to dział praca. A jak szukasz przykładów a nie rozwiązań za ciebie to www.google.pl ma ich całkiem sporo.

0

Piszesz strasznie dziwnie/nieintuicyjnie... :P

Program w momencie uruchomienia pobiera do kazdego buttona itemy z pliku ini, itemy pobierane nie sa zapisane w kodzie.

Nic z tego nie idzie zrozumieć... Nie możesz normalnie napisać - fachowym językiem? Domyślam się, że chodzi o dynamiczne tworzenie komponentów i pobieranie informacji z pliku INI dla nowych pozycji w menu;

Problem polega na tym ze chciałbym odwoływać się do tych dynamicznych itemow przez np standardowy button z formy

To nie jest problem, trzeba to tylko dobrze przemyśleć;

odwołanie nie możne byc po name bo go będę modyfikował w ini.

Dlaczego? Po co? Kiedy? - opisz dokładniej te czynności;

jedyne co mi pozostaje to tagi które narasta z każdym dynamicznym 'pod' itemem.

Co to znaczy "poditemem"? Chodzi Ci o pozycję z danej sekcji menu? Czy SubItem danego Itema? Niekoniecznie musisz do tego używać tagów - listę można przechowywać choćby nawet w dodatkowej, specjalnie przygotowanej macierzy;


Nie wiem czy ja coś źle zrobiłem, ale podczas formatowania tego kodu (wybacz, ale dla mnie to nie jest sformatowane) zauważyłem, że masz o jeden end za dużo - większego znaczenia to nie ma, ale dziwi trochę;

Z racji tej, że trochę zmęczony jestem i myślę w zwolnionym tempie, proszę opisz krok po kroku co dokładnie chcesz zrobić, jak cały ten mechanizm wygląda i jaki efekt chcesz uzyskać, a postaram się pomóc, bo teraz to nie rozumiem wszystkiego;

0

Już chyba dokładniej niż dwa posty wyżej się nie da, do TBraina za cienki jestem :) wiec pozostaje Google.

1
tayamoto napisał(a):
    for i:= MGlowne.Items[0].Count-1 downto 0 do
      MGlowne.Items[0].Delete(i);

Potrafisz usuwać itemy z MGlowne.

Na podobnej zasadzie uzyskujesz dostęp do tagów takich itemów

MGlowne.Items[0][i].Tag

i sprawdzasz ich wartości.

ps
Ale skoro nadajesz kolejne wartości tagów MenuItem.Tag:= i; to od razu możesz odwołać się do konkretnego itema o indeksie i poprzez MGlowne.Items[0][i]

0

Faktycznie trochę namieszałem. Ok po kolei, na main menu mam utworzone dwa buttony "programy" oraz "przekaźniki". Podczas uruchamiania programu (formy) pobierane sa z ini dynamiczne itemy (nazwy) zarówno do "programy" i "przekaźniki". Nazwy Itemow (przekaźników lub programów) oraz ich ilość modyfikowana jest w pliku ini. A teraz to co chce osiągnąć, che odwoływać sie do tych dynamicznych itemow poprzez np button na formie. Z uwagi na mozliwosc zmiany nazwy 'programow' i 'przekaźników' w ini. nie mogę odnajdywać ich po name wiec pozostaje po tagu, chyba ze się mylę.

Ps. Przekaźniki to odwołania do 8 urządzeń zewnętrznych których nazwy modyfikuje w ini a programy to kombinacje on/off tych przekaźników, nawy tez muszę modyfikować w ini.

0
pelsta napisał(a):
tayamoto napisał(a):
    for i:= MGlowne.Items[0].Count-1 downto 0 do
      MGlowne.Items[0].Delete(i);

Potrafisz usuwać itemy z MGlowne.

Na podobnej zasadzie uzyskujesz dostęp do tagów takich itemów

MGlowne.Items[0][i].Tag

i sprawdzasz ich wartości.

ps
Ale skoro nadajesz kolejne wartości tagów MenuItem.Tag:= i; to od razu możesz odwołać się do konkretnego itema o indeksie i poprzez MGlowne.Items[0][i]

Już tego próbowałem z poziomu button.onclick i nie przyniosło rezultatów. Zmienna "i" to zmienna lokalna i nie chce jej deklarować globalnie. wiec zastąpiłem to MGlowne.Items[0][3] jako index itema i nic z tego nie wyszło.

0

Ok działa. Najprostsze rozwiązania są najlepsze dzięki za zainteresowanie i pomoc.

0
tayamoto napisał(a):

Już tego próbowałem z poziomu button.onclick i nie przyniosło rezultatów. Zmienna "i" to zmienna lokalna i nie chce jej deklarować globalnie. wiec zastąpiłem to MGlowne.Items[0][3] jako index itema i nic z tego nie wyszło.

tayamoto napisał(a):

Ok działa. Najprostsze rozwiązania są najlepsze dzięki za zainteresowanie i pomoc.

Teraz to już nic z tego nie rozumiem.

0

Przestudiowałem Twój kod i napisałem sobie prostą aplikację realizującą pewne rzeczy; Utworzyłem także przykładowy plik bazując na nazwach zaczerpniętych z Twojego kodu, jego zawartość:

[Ogolne]
IloscProgramow=4
IloscPrzekaznikow=2

[Program 0]
NazwaProgramu=Moj program 0
[Program 1]
NazwaProgramu=Moj program 1
[Program 2]
NazwaProgramu=Moj program 2
[Program 3]
NazwaProgramu=Moj program 3

[NazwyPrzekaznikow]
Przekaznik0=Moj przekaznik 0
Przekaznik1=Moj przekaznik 1

Sprawdziłem to, co napisałeś i u mnie działa (po niewielkich modyfikacjach); Analizując Twój kod zdziwiło mnie to, że piszesz:

Problem polega na tym ze chciałbym odwoływać się do tych dynamicznych itemow przez np standardowy button z formy, odwołanie nie możne byc po name bo go będę modyfikował w ini.

ale patrząc na zawartość pliku oraz Twój kod można śmiało napisać, że nie możesz się odwoływać do nazwy kontrolek bo w ogóle ich nie nadajesz - stąd wartość np. MainMenu1.Items[0][0].Name (jeśli taki istnieje) będzie równoważna z pustym łańcuchem (sprawdzałem); Jeżeli nie nadasz każdej z dynamicznie tworzonych pozycji jakiejkolwiek nazwy to nie ma siły żebyś w ogóle mógł używać takich funkcji jak chociażby FindComponent, bo ona potrzebuje jako jedyny argument właśnie nazwy szukanej kontrolki; Jak można wywnioskować z Twojego kodu - nowym kontrokom nadajesz jedynie wyświetlaną nazwę (MenuItem.Caption); Tak więc w swoim teście nadałem nazwy dynamicznie tworzonym kontrolkom i mogę śmiało wykorzystywać funkcje podaną przez @olesio;

IDE programu testującego:

IDE.png

Kod tworzący kontrolki bazując na zawartości pliku INI:

type
  TMainForm = class(TForm)
  {...}
  private
    iProgramsCount,
    iRepeatersCount: Word;
  {...}
  end;

{...}

procedure TMainForm.MenuRunRepeater(Sender: TObject);
begin
  Application.MessageBox(PChar('"' + TMenuItem(Sender).Name + '" was clicked!'), 'MenuRunRepeater Proc', MB_ICONINFORMATION);
end;

procedure TMainForm.MenuChooseProgram(Sender: TObject);
begin
  Application.MessageBox(PChar('"' + TMenuItem(Sender).Name + '" was clicked!'), 'MenuChooseProgram Proc', MB_ICONINFORMATION);
end;

procedure TMainForm.FormCreate(Sender: TObject);
var
  iniPrograms: TINIFile;
  miItem: TMenuItem;
  I: Word;
begin
  iniPrograms := TINIFile.Create(ExtractFilePath(Application.ExeName) + 'Programy.ini');

  try
    { PROGRAMS }
    iProgramsCount := iniPrograms.ReadInteger('Ogolne', 'IloscProgramow', 0);

    for I := 0 to iProgramsCount - 1 do
      begin
        miItem := TMenuItem.Create(mmMain);

        miItem.Name := 'Program' + IntToStr(I);
        miItem.Caption := iniPrograms.ReadString('Program ' + IntToStr(I), 'NazwaProgramu', 'Bez nazwy');
        miItem.OnClick := MenuChooseProgram;
        mmMain.Items[0].Add(miItem);
      end;

    { REPEATERS }
    iRepeatersCount := iniPrograms.ReadInteger('Ogolne', 'IloscPrzekaznikow', 0);

    for I := 0 to iRepeatersCount - 1 do
      begin
        miItem := TMenuItem.Create(mmMain);

        miItem.Name := 'Repeater' + IntToStr(I);
        miItem.Caption := iniPrograms.ReadString('NazwyPrzekaznikow', 'Przekaznik_' + IntToStr(I) + '_Nazwa', 'Przekaznik ' + IntToStr(I));
        miItem.OnClick := MenuRunRepeater;
        mmMain.Items[1].Add(miItem);
      end;
  finally
    iniPrograms.Free();
  end;
end;

procedure TMainForm.FormDestroy(Sender: TObject);
var
  I: Word;
begin
  { PROGRAMS LIST }
  for I := 0 to iProgramsCount - 1 do
    mmMain.Items[0].Clear();

  { REPEATERS LIST }
  for I := 0 to iRepeatersCount - 1 do
    mmMain.Items[1].Clear();
end;

Identyfikatory się różnią od Twoich - napisałem takie bo wolę angielskie, nie problem je oczywiście zmienić by były takie jak masz w swoim programie; Tutaj masz kod wykonywany podczas tworzenia formularza i zwalniania go z pamięci (nie wiem czy mmMain.Items[0].Clear() faktycznie usunie z pamięci itemy, ale myślę że tak, że nie spowoduje to memlaka); Procedura MenuChooseProgram wykonywana jest po kliknięciu w dowolny dynamicznie stworzony item w sekcji Programs List (patrz w projekcie), a procedura MenuRunRepeater jak można się domyślić wykonywana jest podczas kliknięcia w dowolny item w sekcji Repeaters List (także patrz w projekcie w załączniku);


Teraz zostaje wywołanie metody Click dynamicznie stworzonego itema; Nie bardzo rozumiem te słowa:

Elementy menu pobierane sa z ini. chciałbym np. z pozycji buttona wybrać dany item z menu bez względu na caption.

Problem polega na tym ze chciałbym odwoływać się do tych dynamicznych itemow przez np standardowy button z formy

Odwołanie ma być z innej procedury np. onclick buttona.

A teraz to co chce osiągnąć, che odwoływać sie do tych dynamicznych itemow poprzez np button na formie.

Już tego próbowałem z poziomu button.onclick i nie przyniosło rezultatów.

w kółko powtarzasz, że chcesz się z poziomu buttona odwołać do tych itemów, ale co to znaczy odwołać sie do buttona? Chcesz wywołać metodę Click dynamicznie stworzonego itema czy co? To jest proste, można na sztywno lub dzięki funkcji FindComponent (ale musisz tym kontrolkom nadać nazwy!); Ja oprogramowałem sobie dwa dodatkowe przyciski by sprawdzić to w jaki sposób da się wywołać je - z pola klasy TSpinEdit wybierasz indeks pozycji w menu i po kliknięciu w przycisk wywoływana jest przypisana podczas tworzenia metoda; Metody OnClick obu przycisków wyglądają tak:

procedure TMainForm.btnProgramExecuteClick(Sender: TObject);
begin
  { PROGRAM EXECUTING }
  if mmMain.Items[0].Count = 0 then
    Application.MessageBox('List of programs is empty!', 'Program executing', MB_ICONSTOP)
  else
    if seProgramIndex.Value > mmMain.Items[0].Count - 1 then
      Application.MessageBox('Current program index is invalid!', 'Program executing', MB_ICONSTOP)
    else
      mmMain.Items[0][seProgramIndex.Value].Click();
end;

procedure TMainForm.btnRepeaterExecuteClick(Sender: TObject);
begin
  { REPEATER EXECUTING }
  if mmMain.Items[1].Count = 0 then
    Application.MessageBox('List of repeaters is empty!', 'Repeater executing', MB_ICONSTOP)
  else
    if seRepeaterIndex.Value > mmMain.Items[1].Count - 1 then
      Application.MessageBox('Current repeater index is invalid!', 'Repeater executing', MB_ICONSTOP)
    else
      mmMain.Items[1][seRepeaterIndex.Value].Click();
end;

Nic nadzwyczajnego - wyświetlają krótki tekst zawierający m.in. nazwę kontrolki, która jest przekazana w argumencie procedury; Jeżeli dany item nie istnieje lub lista jest pusta wyświetlony zostaje odpowiedni komunikat;

Wszystko działa jak należy - odpal mój projekt i przestudiuj, może załapiesz o co chodzi; Kod przeglądaj raczej w kompilatorze IDE, bo tutaj pole jest dość wązkie i zawija linie kodu przez co trudniej się je analizuje; Program testujący napisany w Delphi 7 (Lazarus pewnie by go przełknął ;]);

0

Kod przeglądaj raczej w kompilatorze

Kolejny nie widzi różnicy między kompilatorem a IDE. I to jeszcze osoba o której się uważa że coś wie... eh.
Furious, pomagaj tylko jeżeli masz wiedzę, bo ostatnio okazuje się że masz dosyć dziwne zdanie o niektórych rzeczach (np. flushowanie outa bo się bufor przepełni [rofl])... Wiem, że masz dużą wiedzę, ale czasami jak palniesz to głowa mała...

0

Podstawowe pytanie - który item ma być przekazany do procedury kliknięcia buttona? Trzeba go jakoś wskazać, użytkownik musi jakoś go wybrać. Jak chcesz to zrealizować? Który item, to item "wybrany"? Myślę, że to jest kuriozum sprawy. Może zamiast ów buttona, do każdego itema dodaj subitema który po kliknięciu będzie robić to co miał robić button. Albo zamiast menu itemów użyj listbox'a. Wtedy wybranym itemem będzie item zaznaczony. Albo niech kliknięcie itema w menu tylko go zaznacza bez "włączania przekaźnika", "włączanie przekaźnika" podepniesz pod osobny przycisk.

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