Jak pobrać kliknięty item z TMainMenu przy pomocy TPopupMenu?

0

Do MainMenu dodaję itemy dynamicznie. Gdy klikam w jakiś item w MainMenu, wykonuje się zdarzenie wyświetlenia PopupMenu1, a w tym PopupMenu1 wybieram z listy item np. "Informacje" i chcę wyświetlić nazwę klikniętego wcześniej w MainMenu itemu.

Form1.Caption := TMenuItem(Sender).Caption;

ten kod rzecz jasna zadziała tak, że poda mi nazwę itemu wywołanego TMenuItem, w tym przypadku będzie to nazwa itemu z PopupMenu1 (bo z Popup1, z itemu wywołałem kod), a przecież chcę uzyskać nazwę klikniętego 3 sekundy wcześniej itemu w MainMenu1.
Jak to uzyskać?

(Na wszelki wypadek podam wam co będzie mi potrzebne [wolę o tym napisać, bo czasami ktoś nie wie co doradzić komuś nie znając założeń programu]. Do MainMenu1 dodaję dynamicznie itemy, o tym już pisałem. Po kliknięciu w dowolny item w MainMenu1 wyskakuje PopupMenu1, który będzie miał kilka opcji (itemów) - wyświetlenie nazwy klikniętego w MainMenu1 itemu, zmianę jego nazwy, usunięcie tego itemu w MainMenu1 i to wszystko na razie).

Będę niezwykle wdzięczny za jakieś rady.

2
if Sender is TMenuItem then
   Form1.Caption := TMenuItem(Sender).Parent.Caption;
0

kod nie wyświetla niczego w Form1.Caption.
Pamiętaj, że kod zamieszczam w itemie (o nazwie "Info") w PopupMenu1, który wyświetlam po kliknięciu na item w MainMenu1:

procedure TForm1.Info1Click(Sender: TObject);
begin
  //kod
end;

//dorzucam obrazek by ci to zobrazować
user image
Czyli w skrócie:
Wybieram coś (item) z MainMenu1 klikając na tym -> wyskakuje PopupMenu1 z opcjami jak na obrazku wyżej -> klikam na "Info" -> wykonuje się kod, który ma wyświetlać nazwę klikniętego wcześniej itemu (tego z MainMenu1), który wywołał Popupa

3

Zapisz na co kliknąłeś przed odpaleniem PopupMenu.
Zapisz w jakieś specjalnej składowej bądź w PopupMenu1.Tag

1

dziękuję wam za pomoc, a może być zrobione tak?

dodałem ogólną tablicę (do trzymania info, w co kliknąłem w menu, czyli kategoria która i który item):

var
  Item: array[0..1] of Byte;

a niżej takie rzeczy (to event dodany, bo dynamicznie robię itemy więc muszę im przypisać zdarzenie od razu by po kliknięciu coś robiły):

procedure TForm1.PopupMenuItemsClick(Sender: TObject);
var
  Poz: TPoint;
begin
  Item[0] := TMenuItem(Sender).Parent.MenuIndex; //kliknieta zakladka
  Item[1] := TMenuItem(Sender).MenuIndex; //klikniety item w zakladce

  GetCursorPos(Poz);
  PopupMenu1.Popup(Poz.X, Poz.Y); //wyswietlenie PopupMenu1 w miejscu klikniecia
end;

czyli jak klikam w dynamiczny item w MainMenu1 to do tablicy globalnej dodaję sobie pozycję zakładki (np. czy to trzecia zakładka, czy pierwsza itd) i pozycję klikniętego itema z tej zakładki w manu np. item szósty. A później wyświetlam PopupMenu1

A w popupmenu mam przycisk Info, który ma taki kod:

procedure TForm1.Info1Click(Sender: TObject);
begin
  Form1.Caption := MainMenu1.Items[Item[0]].Items[Item[1]].Caption;
end;

czyli po prostu belka formy dostaje nazwę klikniętego itemu (pamiętać należy by Właściwość MainMenu1 - AutoHotkeys ustawić na maManual by nie dodawało przed nazwą znaczka &).
Taka tablica chyba w porządku jest? Ma tylko dwa elementy i mogę sobie operować na konkretnym itemie teraz bez problemu, bo znam jego idealne położenie.
Dodam, że zarówno MainMenu jak i PopupMenu nie tworzę dynamicznie (bo przykładowo chcę na sztywno ustawić np. 2, 3 kategorie w menu, a itemy dodać tylko dynamicznie lub później dodać nowe kategorie dynamicznie i itemy dynamicznie). Jak na razie jest git, ale później sprawdzę wszystko dokładniej, tymczasem problem rozwiązany. Dziękuję raz jeszcze.

3
procedure TForm1.PopupMenuItemsClick(Sender:TObject);
begin
  PopupMenu1.Tag:=Integer(Pointer(Sender)); //klikniety item w zakladce 
  PopupMenu1.Popup(Mouse.CursorPos.X,Mouse.CursorPos.Y); //wyswietlenie PopupMenu1 w miejscu klikniecia
end;

procedure TForm1.Info1Click(Sender: TObject);
begin
  Form1.Caption:=TMenuItem(Pointer(PopupMenu1.Tag)).Caption;
end;

A nie prościej tak jak cię poradziłem?

0

aha, o to chodziło (źle rozumiałem wcześniej no i z Pointerem nie domyśliłbym się, początkujący jestem).
Fajny sposób, dziękuję :) i przynajmniej czegoś mnie nauczyłeś

0

Jeszcze jedno pytanko.
Jak dodać zdarzenie OnClick do MainMenu1? Bo aktualnie PopupMenu1 wyświetla mi się tylko wtedy, gdy kliknę na jakimś itemie w MainMenu1, bo zdarzenie OnClick istnieje w itemie, a nie w ogólnym MainMenu.

Krótko mówiąc jeśli zamierzałbym dodawać nowe itemy do MainMenu (tylko za pomocą PopupMenu1), który ma tylko wpisane kategorie same i zero itemów na liście, to PopupMenu1 nie wyświetliłby się nigdy, bo nie istnieje Event w MainMenu1 o nazwie OnClick. Jedyny istniejący Event w MainMenu1 to OnChange, który dla mnie jest raczej bezużyteczny.

Jak dodać do MainMenu1 zdarzenie OnClick? Mam nadzieję, że nie będzie trzeba robić nowego komponentu na podstawie TMainMenu.

Obrazek obrazujący o co mi chodzi:
user image

Wybaczcie, że tłumaczę wam pisząc tyle tekstu, po prostu chcę mieć pewność, że każdy mnie zrozumie.

1

Dodaj (zawsze) jeden pusty Item.

0

a jak wykryć kliknięcie lewym lub prawym przyciskiem myszy w menu lub ogólnie? Jak zrobiłem ogólny komunikat (Application.OnMessage) to kliknięcia wykrywa tylko na formie i na komponentach na formie, ale w MainMenu1 gdy klikam to niczego nie wykrywa, tak jakby całościowo olewało (ignorowało) komunikaty dla kliknięć w MainMenu :O :(
Nawet GetKeyState nie działa na kliknięcia w MainMenu! ;(

0

Chcę móc kliknąć prawym przyciskiem na wybranej zakładce/kategorii [...]

Czekaj, czekaj - chcesz otworzyć menu kontekstowe po kliknięciu PPM na pozycję menu? Na TMenuItem? Przecież nie ma dostępnego odpowiedniego zdarzenia... Czemu w ogóle chcesz to w ten sposób zrobić?

Według mnie za bardzo kombinujesz - dziwną funkcję sobie wymyśliłeś; Zastanów się czy nie lepiej zrobić to w inny sposób, bo nigdzie w programach nie widziałem żeby można było kliknąć PPM na menu aplikacji i wywołać menu kontekstowe; Nikt z użytkowników nie wpadnie na to, że taka funkcja istnieje (bo jest to dziwne) więc imho trzeba by się zastanowić nad inną formą modyfikacji tego menu; Nie bez powodu klasa TMainMenu nie oferuje takiego zdarzenia, podobnie jak klasa TMenuItem; Tego się po prostu nigdy nie robiło i pewnie nigdy się robić nie będzie;

Oczywiście możesz podczas kliknięcia (jeśli w ogóle kontrolujesz ten proces) pobrać referencję do komponentu znajdującego się pod kursorem w momencie kliknięcia; Spróbuj, może ten sposób będzie odpowiedni: Get the Control Under the Mouse in a Delphi application;

Ewentualnie rozbuduj klasę TMainMenu o nowe zdarzenie (jakiś OnClick czy OnMouseDown/OnMouseUp) żebyś mógł w ogóle obsłużyć jakikolwiek komunikat informujący o kliknięciu na menu.

0

bo do programiku dla siebie chcę dodać Menu, w którym będę trzymał np. strony i odpalał je przez ShellExecute po kliknięciu lewym przyciskiem myszy, a prawym przyciskiem wyświetlałbym PopupMenu by np. zmienić adres, wywalić, albo dodać nowy. I właśnie dlatego chcę móc wykryć, który klawisz wciśnięto w MainMenu1 :). A dlaczego MainMenu wybrałem do tego celu? Bo akurat świetnie to wygląda dla mnie i spełnia moje wymagania wizualne w 100 procentach, do tego operowanie na tym Menu nie wymaga wielu linijek kodu i mam odwalonych z góry większość rzeczy przez ten komponent.
Ja wiem, że w firefoxie Menu robi za zwykłe menu, a do zakładek dodali pod paskiem adresu belkę i na niej można foldery dodawać i zakładki, klikać prawym, ale gdybym ja miał to robić to pewnie robiłbym to masę czasu i może nic by mi z tego nie wyszło, a MainMenu jest dość proste i skoro w pełni mnie zadowala jego wygląd to chcę je wykorzystać, tylko ten durny problem wykrywania lewego/prawego przycisku pozostaje :(
A wg ciebie fatalny mam pomysł, by MainMenu wykorzystywać tak? W końcu "programista" (nie uważam się za programistę, używam tylko tego słowa raz tutaj) powinien wymyślać to co mu się podoba, powinien nie czuć żadnych ograniczeń i móc robić to czego inni ludzie nie zrobili, racja? I wszystko powinno być możliwe w teorii.

0

Ewentualnie rozbuduj klasę TMainMenu o nowe zdarzenie (jakiś OnClick czy OnMouseDown/OnMouseUp)

a trudno to się robi? Nigdy nie bawiłem się w to. Chodzi ci o stworzenie komponentu nowego na podstawie TMainMenu, czy coś innego masz na myśli? Jakieś wskazówki mógłbyś udzielić? I może na wstępie powiedz, czy to trudna rzecz dla mnie jeśli nigdy w tym się nie bawiłem, bo może bez sensu będzie tłumaczenie mi czegoś, co mnie przerośnie. :)

0

Menu, w którym będę trzymał np. strony i odpalał je przez ShellExecute po kliknięciu lewym przyciskiem myszy, a prawym przyciskiem wyświetlałbym PopupMenu by np. zmienić adres, wywalić, albo dodać nowy.

Do tego robi się osobny moduł, w którzym zarządza się zgromadzonymi adresami (nowy formularz z wygodnym ich organizowaniem); Pierwszą pozycją (lub ostatnią) w takim submenu jest opcja dostępu do takiego modułu i tam się wszystko organizuje;

Ja wiem, że w firefoxie Menu robi za zwykłe menu, a do zakładek dodali pod paskiem adresu belkę i na niej można foldery dodawać i zakładki, klikać prawym

Tak, ale dodawanie nowych pozycji czy przeciąganie to rzecz trywialna (znana przez niemal wszystkich użytkowników), a klikanie PPM na menu już takie oczywiste nie jest, więc mało kto zorientuje się, że tak można (rządzić tym może przypadek); Dlatego odradzam takiego rozwiązania, bo nawet jeśli świetnie wygląda (wizualnie), to nikomu raczej nie przyjdzie do głowy klikać na nie prawym przyciskiem myszy, a już na pewno mało kto będzie przypuszczał, że tak można edytować menu; We wszystkich programach jakie mają możliwość manipulowania menu jest przewidziany moduł do takich modyfikacji - linki czy inne informacje są przedstawione na liście lub w drzewie, która jest czytelna i łatwo jest taką listą (czy drzewem) zarządzać;

W końcu "programista" (nie uważam się za programistę, używam tylko tego słowa raz tutaj) powinien wymyślać to co mu się podoba, powinien nie czuć żadnych ograniczeń i móc robić to czego inni ludzie nie zrobili, racja?

Po części masz rację, jednak powinieneś się kierować wiedzą i zdolnościami przeciętnych "użyszkodników", a nie swoimi aspiracjami, bo Ty owszem, będziesz wiedział co i gdzie jest (w końcu to Ty robidz program), ale użytkownicy nie będą znali tego programu i jak zobaczą, że nie ma modułu do zarządzania linkami to się wkurzą :]

a trudno to się robi? Nigdy nie bawiłem się w to. Chodzi ci o stworzenie komponentu nowego na podstawie TMainMenu, czy coś innego masz na myśli? Jakieś wskazówki mógłbyś udzielić?

Tak, o to mnie chodzi, jednak nie jest to tak proste, jak dodawanie - musisz poczytać jak się rozbudowuje komponenty o nowe zdarzenia (obsługa nowych komunikatów) i sprzężyć je z menu kontekstowym, co może okazać się trudne; Jeśli nigdy tego nie robiłeś (nie rozbudowywałeś istniejących klas komponentów) to będziesz musiał trochę poczytać i poćwiczyć.

0

to w skrócie co mi doradzasz? Dodam, że tylko ja będę używał tego programiku. Nie myślę o publikowaniu go w necie, robię go tylko dla siebie.
Chcę po prostu jak najszybciej zrobić to durne menu w dowolny, prosty sposób (czyli np. użyć jakiegoś komponentu, jakiego? i wolałbym nie pobierać z netu gotowych komponentów, bo większa część mnie uważa, że tylko w niektórych przypadkach warto pobrać komponent z netu [choć najlepiej nie pobierałbym nigdy żadnego, ale że newbie jestem to dopuszczam wyjątki czasami, ale do menu wolę nie korzystać z komponentów z netu, wolę to co oferuje XE5]).

0

Nigdy nie próbowałem aż tak manipulować menu programu więc praktyki w tym nie mam, ale sądzę, że trzeba będzie wspomóc się funkcjami WinAPI; Nie udało mi się wyposażyć komponentu TMainMenu w obsługę komunikatu do rozpoznania klikania PPM, ale nie wiem czy czegoś nie pokręciłem, bo mało czasu miałem na test; Może i jest opcja z takim pokombinowaniem, ale niestety nie mam czasu się w temat zagłębiać;

Być może ma to coś wspólnego z funkcją MenuItemFromPoint; Może inni znający WinAPI lepiej ode mnie wpadną na jakiś lepszy pomysł.

0

Można odpalić edytor menu dostępny podczas designtime.

0

@furious programming jak dobrze zrozumiałem to wystarczy w klasie TForm:

  private
    { Private declarations }
    procedure WMMenuRButtonUp(var Msg: TMessage); message WM_MENURBUTTONUP;
procedure TForm1.WMMenuRButtonUp(var Msg: TMessage);
var
  MenuItem: TMenuItem;
begin
  MenuItem:= MainMenu1.FindItem(GetMenuItemID(Msg.LParam, Msg.WParam), fkCommand); //item menu ktory kliknieto ppm
  if Assigned(MenuItem) and (MenuItem.Parent.GetParentMenu = MainMenu1) then
    TrackPopupMenuEx(PopupMenu1.Handle, TPM_RECURSE or TPM_BOTTOMALIGN or TPM_RETURNCMD,
                            Mouse.CursorPos.X, Mouse.CursorPos.Y, Handle, nil); //wyświetlamy popup w miejscu kursora
end;
0

@kAzek - niestety, sprawdzam pod WinXP + Delphi7 i menu kontekstowe nie wyskakuje;

Komunikat WM_MENURBUTTONUP nie jest przechwytywany, więc kod spod proedury WMMenuRButtonUp nie jest wykonywany; U Ciebie ten kod działa?

2

Teraz jak wiem o co chodzi ten kod powinien działać:

unit uMain;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, Menus;

type
  TForm1 = class(TForm)
    mmMain: TMainMenu;
    miFile: TMenuItem;
    mEdit: TMenuItem;
    miView: TMenuItem;
    pmMain: TPopupMenu;
    miAdd: TMenuItem;
    miDelete: TMenuItem;
    miClear: TMenuItem;
    procedure miAddClick(Sender: TObject);
  private
    procedure WMNCButtonUp(var Msg: TWMNCRButtonUp); message WM_NCRBUTTONUP;

  end;

var
  Form1: TForm1;

  //funkcja jest zle zadeklarowana w Delphi 7 dlatego trzeba ja sobie zadeklarowac poprawnie
  function MenuItemFromPoint(hWnd: HWND; hMenu: HMENU; ptScreen: TPoint): Integer;
    stdcall external 'user32.dll';

implementation

  procedure TForm1.WMNCButtonUp(var Msg: TWMNCRButtonUp);
  var
    miItem: TMenuItem;
    iMenuPos, iMenuID: Integer;
    ptScreen: TPoint;
  begin
    ptScreen:= Point(Msg.XCursor, Msg.YCursor);
    iMenuPos:= MenuItemFromPoint(Handle, mmMain.Handle, ptScreen);
    if iMenuPos >= 0 then
    begin
      iMenuID:= GetMenuItemID(mmMain.Handle, iMenuPos);
      miItem:= mmMain.FindItem(iMenuID, fkCommand);
      if Assigned(miItem) and (miItem.Parent.GetParentMenu = mmMain) then
      begin
        pmMain.Tag:= Integer(Pointer(miItem));
        pmMain.Popup(ptScreen.X, ptScreen.Y);
      end;
    end;
  end;

{$R *.dfm}

procedure TForm1.miAddClick(Sender: TObject);
var
  miItem: TMenuItem;
begin
  miItem:= TMenuItem(Pointer(pmMain.Tag));
  ShowMessage(Format('Kliknięto %s wywolane z %s', [TMenuItem(Sender).Caption,
     miItem.Caption]));
end;

end.
0

Pozwoliłem sobie zaznaczyć kod, który podał @kAzek bo sprawdziłem i pod Windows 7 64 bit oraz Delphi 7 Personal działa ok. A pytający bardzo dziwnie kombinuje. No ale jeżeli taka jego wola to coż poradzić ;) Natomiast nie wiem czy sam bym wpadł na takie rozwiązanie. Ponieważ pod WinAPi używałem głownie PopUp menu i to dosyć rzadko.

W jednej swojej aplikacji kombinowałem tylko aby zawsze na kontrolkach będącymi standardowymi polami edycyjnymi z klasą zawierającą słowo Edit pokazało się moje PopUpMenu albo dodać do systemowego jakieś pozycje. Ponieważ - za pewne ze względów bezpieczeńśtwa - nie da się dodać pozycji do tego systemowego i ją obsłużyć, posługiwałem się globalnym Hookiem LowLevelowym na myszkę i przechwytywanie momenu kliknięcia środkowego przycisku jak również dorobiłem globalną dla systemu obsługę własnych skrótów klawiszowych dla opcji w tym PopUpMenu. Wtedy pokazuję swoje menu jeżeli kliknięto na taką kontrolkę.

Konkretnie chodziło o to żeby sobie łatwo zmieniać case tekstu w danym Editcie na wszystkie litery małe/wielkie. Dla moich potrzeb działa to ok i tak, jak chciałem. Jednak docelowy user musi to uruchomić z prawami Admina, jeżeli ma włączone UAC. Ponieważ wiadomo - taki Hook inaczej się nie założy. Dla mnie jednak problemu nie ma. Anyway, problem pytającego rozwiązany. I jak widać funkcja, na którą wcześniej zwrócił uwagę @furious programming jednak bardzo się przydała :)

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