Entwickler-Ecke

Grafische Benutzeroberflächen (VCL & FireMonkey) - Auf das Schliessen eines PopupMenus reagieren


galagher - Fr 08.03.19 20:23
Titel: Auf das Schliessen eines PopupMenus reagieren
Hallo!

Ich habe nun nach einiger Suche eine Möglichkeit gefunden, auf das Schliessen eine PopupMenus zu reagieren:
https://www.delphipraxis.net/26787-pruefen-ob-ein-popupmenue-geoeffnet-ist.html

Nur leider komme ich nicht drauf, wie man, wie dort angesprochen, herausfinden kann, um welches PopupMenu es sich handelt:
Zitat:
Wie kann man in CMMenuClosed then NAMEN des betreffenden Popup-Menüs erfahren? Geht das überhaupt?

Zitat:
Ja das geht, wenn man beim Senden der Nachricht auch noch eine zusätzliche Information hinzufügt (z.B. die Referenz auf die Popup-Instanz).

Zitat:
Und wie könnte man in der PRAXIS beim Senden der Nachricht in diesem Beispiel die betreffende Information einfügen?


Weiss jemand, wie das geht?


Delete - Fr 08.03.19 21:13

- Nachträglich durch die Entwickler-Ecke gelöscht -


galagher - Fr 08.03.19 21:36

Hallo Frühlingsrolle!

user profile iconFrühlingsrolle hat folgendes geschrieben Zum zitierten Posting springen:
Man könnte das noch erweitern und z.B. anstelle des wParam-Parameters, self.Name mitschicken, in etwa:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
procedure TExPopupList.WndProc(var Message: TMessage);
  Procedure Send( msg: Integer );
  Begin
    If Assigned( Screen.Activeform ) Then
      Screen.ActiveForm.Perform( msg, WParam(PChar(self.Name)),  //<- hier ist ein Fehler!
Message.lparam );
  End;
begin
  //...
end;



Leider klappt es mit WParam(PChar(self.Name)) nicht. Ich habe etwas herumexperiemtiert und bin auf Message.WParam(PChar(self.Name)) gekommen - .Name wird aber nicht erkannt bzw. gibt es das hier nicht. Auch Message.WParam(PChar(self)) funktioniert nicht, hier meckert der Compiler:
Zitat:
E2066 Operator oder Semikolon fehlt
- hier wird offenbar ein , erwartet, sonst nichts!

Ich verstehe, was du meinst, bekomme es aber nicht hin.


Delete - Fr 08.03.19 21:55

- Nachträglich durch die Entwickler-Ecke gelöscht -


galagher - Sa 09.03.19 06:02

user profile iconFrühlingsrolle hat folgendes geschrieben Zum zitierten Posting springen:

Nachtrag


Delphi-Quelltext
1:
function GetPopupName(Parent: TWinControl): string;                    

Naja, das liefert in jedem Fall das erste gefundenene PopupMenu, egal, welches gerade verwendet wird...


Delete - Sa 09.03.19 06:46

- Nachträglich durch die Entwickler-Ecke gelöscht -


galagher - Sa 09.03.19 07:53

user profile iconFrühlingsrolle hat folgendes geschrieben Zum zitierten Posting springen:
So funktioniert es explizit nur für TForm Objekte.

Das nützt mit für meine Zwecke nichts. Ich möchte herausfinden können, welches PopupMenu das Schliessen ausgelöst hat, also von welchem PopupMenu Send( CM_MENUCLOSED ); ausgelöst wurde!


Delete - Sa 09.03.19 09:27

- Nachträglich durch die Entwickler-Ecke gelöscht -


galagher - Sa 09.03.19 10:53

Funktioniert leider auch nicht:
user profile iconFrühlingsrolle hat folgendes geschrieben Zum zitierten Posting springen:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
procedure TExPopupList.WndProc(var Message: TMessage);
  Procedure Send( msg: Integer );
  Begin
    If Assigned( Screen.Activeform ) Then
      Screen.ActiveForm.Perform( msg, Integer(PChar(Screen.ActiveForm.PopupMenu.Name)), 0 );
  (*                                  |
                               Das produziert einen Zugriffsfehler!
  *)

  End;
begin
  // ... wie im verlinkten Post gegeben
end;

Ich krieg's einfach nicht hin!


Delete - Sa 09.03.19 11:07

- Nachträglich durch die Entwickler-Ecke gelöscht -


galagher - Sa 09.03.19 11:45

user profile iconFrühlingsrolle hat folgendes geschrieben Zum zitierten Posting springen:
Kann ich nicht bestätigen

Ich leider doch:
Zitat:
Im Projekt Help.exe ist eine Exception der Klasse EAccessViolation mit der Meldung 'Zugriffsverletzung bei Adresse 006D658E in Modul 'Help.exe'. Lesen von Adresse 00000008' aufgetreten.


Geht das nur mit PopupMenus der TForm? Ich habe PopupMenus, die zu verschiedenen Komponenten gehören. Bei keiner funktioniert es!

//Edit:
Auch bei PopupMenus von TForm kommt es zu dem Fehler!


Delete - Sa 09.03.19 17:35

- Nachträglich durch die Entwickler-Ecke gelöscht -


galagher - Sa 09.03.19 20:33

user profile iconFrühlingsrolle hat folgendes geschrieben Zum zitierten Posting springen:
Hm, führst du Delphi mit Adminrechten aus? Bei mir ist das der Fall.

Nein, eingeschränktes User-Konto. Muss doch aber egal sein, oder?

user profile iconFrühlingsrolle hat folgendes geschrieben Zum zitierten Posting springen:
Die Eigenschaft .Popupmenu ist keiner Klasse eindeutig zuordenbar, daher muss man den Code auf das jeweilige Parent umschreiben.
Mit RTTI könnte man es zurecht biegen, da bin ich aber nicht so sattelfest darin.

RTTI? Muss ich googeln!
Das Problem ist, dass Screen.ActiveForm.PopupMenu nicht zugewiesen ist, denn wenn man mit if Assigned(Screen.ActiveForm.PopupMenu) prüft, wird der Code nicht ausgeführt - ist also nicht zugewiesen.

Was ich nicht verstehe: Das PopupMenu gibt es ja, es gibt gleich mehrere, die jeweils verschiendene Komponenten haben. Sie existieren also, werden auch nirgendwo im Code "destroyed" oder so was in der Art.


Delete - Sa 09.03.19 20:50

- Nachträglich durch die Entwickler-Ecke gelöscht -


galagher - Sa 09.03.19 21:16

user profile iconFrühlingsrolle hat folgendes geschrieben Zum zitierten Posting springen:
Viele WinAPI Funktionen, die im Delphi allgegenwertig sind, benötigen Lese- und Schreibrechte, die nur mit Adminrechten ein "korrektes" Ergebnis liefern können.

Dann muss ich hier wohl aufgeben. Delphi ist unter meinem Adminkonto nicht eingerichtet. Ich kann mein Projekt nicht kompilieren, weil da diverse Komponenten nicht installiert sind.

//Edit: Klar könnte ich das jetzt alles auch im Adminkonto installieren.
Gibt es keinen einfacheren Weg, an das PopupMenu heranzukommen? Irgendwie, irgendwo muss mein Programm ja "wissen", welches PopupMenu gerade geschlossen wird!


Delete - Sa 09.03.19 21:25

- Nachträglich durch die Entwickler-Ecke gelöscht -


galagher - Sa 09.03.19 21:31

user profile iconFrühlingsrolle hat folgendes geschrieben Zum zitierten Posting springen:
Mit RTTI könnte man es zurecht biegen, da bin ich aber nicht so sattelfest darin.

Ich erst recht nicht!


galagher - So 10.03.19 11:14

Zunächst habe ich einige Methoden in gefälligere Namen umbenannt und zudem einige neue Methoden hinzugefügt:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
unit ExPopupMenu;

interface

uses Controls, Menus;

const
    CM_MENU_CLOSED = CM_BASE + 1001;
    CM_ENTER_MENU_LOOP = CM_BASE + 1002;
    CM_EXIT_MENU_LOOP = CM_BASE + 1003;

var
  FPopupMenu: TPopupMenu;

function GetCurrentPopupMenu: TPopupMenu;
procedure SetCurrentPopupMenu(const PopupMenu: TPopupMenu);

implementation

uses Messages, Forms;

type
    TPopupListEx = class(TPopupList)
    protected
      procedure WndProc(var Message: TMessage) ; override;
    private
      procedure PerformMessage(cm_msg : integer; msg : TMessage) ;
    end;

{ TPopupListEx }
procedure TPopupListEx.PerformMessage(cm_msg: integer; msg : TMessage) ;
begin
    if not (Screen.Activeform  = nilthen
      Screen.ActiveForm.Perform(cm_msg,
        (*Integer(PChar(Screen.ActiveForm.PopupMenu)),*)  //<- Ist nicht zugewiesen und daher nicht verwendbar!
        msg.WParam, msg.LParam) ;
end;

procedure TPopupListEx.WndProc(var Message: TMessage) ;
begin
    case message.Msg of
      WM_ENTERMENULOOP: PerformMessage(CM_ENTER_MENU_LOOP, Message) ;
      WM_EXITMENULOOP : PerformMessage(CM_EXIT_MENU_LOOP, Message) ;
      WM_MENUSELECT :
      with TWMMenuSelect(Messagedo
      begin
        if (Menu = 0and (Menuflag = $FFFFthen
        begin
          PerformMessage(CM_MENU_CLOSED, Message) ;
        end;
      end;
    end;
    inherited;
end;

function GetCurrentPopupMenu: TPopupMenu;
begin
  Result := FPopupMenu;
end;

procedure SetCurrentPopupMenu(const PopupMenu: TPopupMenu);
begin
  FPopupMenu := PopupMenu;
end;

initialization
    FPopupMenu := TPopupMenu.Create(nil);

    Popuplist.Free; //free the "default", "old" list
    PopupList:= TPopupListEx.Create; //create the new one
    // The new PopupList will be freed by
    // finalization section of Menus unit.

finalization
  if Assigned(FPopupMenu) then
    FPopupMenu := nil;  //<- Hier führt .Free zu einem Laufzeitfehler: "Runtime error 217 at 00409495"

end.


Ich habe das nun so gelöst, dass man im OnPopup des jeweiligen PopupMenus eben dieses zuweist, um später auswerten zu können, um welches PopupMenu es sich handelt:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
procedure TForm1.PopupMenu1Popup(Sender: TObject);
begin
  SetCurrentPopupMenu(TPopupMenu(Sender));
end;

//Hier kann man nun mehrere PopupMenus unterscheiden:
procedure TForm1.PopupMenuClosed(var msg: TMessage) ;
begin
  if GetCurrentPopupMenu = PopupMenu1 then
    Showmessage('PopMenu closed');
end;

procedure TForm1.PopupMenuEnter(var msg: TMessage) ;
begin
  if GetCurrentPopupMenu = PopupMenu1 then
    Showmessage('PopMenu entered');
end;

procedure TForm1.PopupMenuExit(var msg: TMessage) ;
begin
  if GetCurrentPopupMenu = PopupMenu1 then
    Showmessage('PopMenu exited');
end;


Ich habe damit mein Ziel erreicht, aber leider nicht "vollautomatisch"!

Vielleicht könnte mit noch jemand sagem, warum es im finalization-Abschnitt zu einem Laufzeitfehler kommt, wenn ich dort FPopupMenu.Free verwende, und ob es überhaupt notwenig ist, FPopupMenu freizugeben!


Delete - So 10.03.19 15:53

- Nachträglich durch die Entwickler-Ecke gelöscht -


galagher - So 10.03.19 20:04

user profile iconFrühlingsrolle hat folgendes geschrieben Zum zitierten Posting springen:
Zu dem Lsufzeitfehler wird es wahrscheinlich deswegen kommen, weil das übergebene Popupmenu1 dadurch an 2 Stellen freigegeben wird, was nicht geht.
Einmal von der Hauptklasse, und dann von deiner Klasse aus.
Es NIL zu setzen, find ich überflüssig.

Ja, das stimmt wohl, denn ich muss es nicht einmal erzeugen: Es funktioniert auch ohne .Create.

Natürlich wäre es noch einfacher, einen String zu verwenden und einfach auf .Name zu prüfen. Es klappt, ist aber dennoch nur eine halbe, oder sagen wir, eine dreiviertel-Lösung, denn ich benötige eben eine Methode, um das PopupMenu erst einmal "bekannt" zu machen. Schön wäre es, wenn das alles automatisch erledigt wird.

Und noch etwas gefällt mir nicht: Man kann auf FPopupMenu von ausserhalb zugreifen!


Sinspin - So 10.03.19 20:14

Hallo,
user profile icongalagher hat folgendes geschrieben Zum zitierten Posting springen:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
procedure TPopupListEx.PerformMessage(cm_msg: integer; msg : TMessage) ;
begin
    if not (Screen.Activeform  = nilthen
      Screen.ActiveForm.Perform(cm_msg,
        (*Integer(PChar(Screen.ActiveForm.PopupMenu)),*)  //<- Ist nicht zugewiesen und daher nicht verwendbar!
        msg.WParam, msg.LParam) ;
end;

Selbst wenn da ein Popup zugewiesen wäre würde es doch deutlich mehr Sinn machen den LParam zu verwenden und dann dort den Objektzeiger selber zu übergeben.
Generell ist es aber nicht sinnvoll eine bestehende Message zu verbiegen.
Deutlich sinnvoller ist es via PostMessage(Screen.ActiveForm.Handle, WM_MyNotification, WPraram, LParam) eine neue Message zu generieren auf die man via Messagehandler zugreifen kann ohne selber in WindowProc rumpfuschen zu müssen.

user profile icongalagher hat folgendes geschrieben Zum zitierten Posting springen:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
initialization
    FPopupMenu := TPopupMenu.Create(nil);

    Popuplist.Free; //free the "default", "old" list
    PopupList:= TPopupListEx.Create; //create the new one
    // The new PopupList will be freed by
    // finalization section of Menus unit.

finalization
  if Assigned(FPopupMenu) then
    FPopupMenu := nil;  //<- Hier führt .Free zu einem Laufzeitfehler: "Runtime error 217 at 00409495"

end.

Vielleicht könnte mit noch jemand sagem, warum es im finalization-Abschnitt zu einem Laufzeitfehler kommt, wenn ich dort FPopupMenu.Free verwende, und ob es überhaupt notwenig ist, FPopupMenu freizugeben!


initialization und finalization ist Unit level. Diese Abschnitte werden nur einmal beim Starten / Beenden des Programmes ausgeführt.
FPopupMenu ist eine globale Variable in der Du dir merkst welches Popup geclickt wurde. Für diese muss daher weder ein Object erzeugt werden noch muss sie freigegeben werden.
was du mit Popuplist.Free; bezweckst ist mir an der stelle auch schleierhaft. Das würde zumindest im finalization mehr Sinn machen.


Delete - So 10.03.19 20:48

- Nachträglich durch die Entwickler-Ecke gelöscht -


galagher - So 10.03.19 21:07

user profile iconSinspin hat folgendes geschrieben Zum zitierten Posting springen:
Deutlich sinnvoller ist es via PostMessage(Screen.ActiveForm.Handle, WM_MyNotification, WPraram, LParam) eine neue Message zu generieren auf die man via Messagehandler zugreifen kann ohne selber in WindowProc rumpfuschen zu müssen.

Das übersteigt eindeutig meine Fähigkeiten und auch mein Verständnis!

user profile iconSinspin hat folgendes geschrieben Zum zitierten Posting springen:
initialization und finalization ist Unit level. Diese Abschnitte werden nur einmal beim Starten / Beenden des Programmes ausgeführt.
FPopupMenu ist eine globale Variable in der Du dir merkst welches Popup geclickt wurde. Für diese muss daher weder ein Object erzeugt werden noch muss sie freigegeben werden.

Ok. Aber es ist doch nicht nur eine einfache Variable wie String oder Boolean, sondern ein TPopupMenu, also eine Komponente. Und da dachte ich zuerst, die muss man doch wieder freigeben. Man muss sie (als TPopupMenu) aber noch nicht einmal erzeugen! Ich verstehe das nicht ganz, aber ich akzeptiere es gerne!

Man kann nun nicht mehr von ausserhalb auf FPopupMenu zugreifen, ich habe das aber jetzt geändert. Es ist nun keine globale Variable mehr:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
//...
implementation

uses Messages, Forms;

var
  FPopupMenu: TPopupMenu;  //<- Hier!

type
    TPopupListEx = class(TPopupList)
//...


user profile iconSinspin hat folgendes geschrieben Zum zitierten Posting springen:
was du mit Popuplist.Free; bezweckst ist mir an der stelle auch schleierhaft. Das würde zumindest im finalization mehr Sinn machen.

Ich habe den Code von http://delphi.cjcsoft.net/viewthread.php?tid=45678 so übernommen, ich kannte Popuplist vorher nicht!


galagher - So 10.03.19 21:14

user profile iconFrühlingsrolle hat folgendes geschrieben Zum zitierten Posting springen:
user profile iconSinspin hat folgendes geschrieben Zum zitierten Posting springen:
was du mit Popuplist.Free; bezweckst ist mir an der stelle auch schleierhaft. Das würde zumindest im finalization mehr Sinn machen.

Menus.pas hat folgendes geschrieben:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
var
  PopupList: TPopupList;

implementation

initialization
  PopupList := TPopupList.Create;
  
finalization
  PopupList.Free;

Der Autor von http://delphi.cjcsoft.net/viewthread.php?tid=45678 hat das so geschrieben. Ok, viele Leute schreiben irgendwas...
Ich gebe aber zu, dass ich nicht weiss, ob das so korrekt ist. Jedenfalls funktionieren beide Varianten. Aber mir kam es natürlich schon seltsam vor, im initialization-Abschnitt etwas freizugeben...
Zitat:
The new PopupList will be freed by
finalization section of Menus unit.


Ist das wirklich so? "The new PopupList" wurde doch explizit mit .Create erzeugt und im Originalcode gibt es gar keinen finalization-Abschnitt!

Ich habe den Code jetzt wie von dir vorgeschlagen angepasst.


Delete - So 10.03.19 21:27

- Nachträglich durch die Entwickler-Ecke gelöscht -


galagher - So 10.03.19 21:36

user profile iconFrühlingsrolle hat folgendes geschrieben Zum zitierten Posting springen:
Wird die Anwendung beendet, dann wird die Variable Menus.PopupList in der Unit Menus.pas im Bereich FINALIZATION freigegeben.

Hätte ich ja nur nachschauen müssen dort!

So weit, so gut.
Danke euch allen für die Hilfe!

Bleibt nur noch, dass nicht ohne weiteres erkannt wird, welches PopupMenu gerade aktiv ist. Egal, wie ich es drehe, Screen.ActiveForm.PopupMenu ist niemals zugewiesen, daher kann es natürlich auch keine Eigenschaften (wie zB. Name) davon geben.
Wenn euch dazu etwas einfällt, ich bin ganz Ohr (oder Auge in diesem Fall)! :)


Sinspin - Mo 11.03.19 20:52

user profile icongalagher hat folgendes geschrieben Zum zitierten Posting springen:
Der Autor von http://delphi.cjcsoft.net/viewthread.php?tid=45678 hat das so geschrieben. Ok, viele Leute schreiben irgendwas...
Ich gebe aber zu, dass ich nicht weiss, ob das so korrekt ist. Jedenfalls funktionieren beide Varianten. Aber mir kam es natürlich schon seltsam vor, im initialization-Abschnitt etwas freizugeben...
Zitat:
The new PopupList will be freed by
finalization section of Menus unit.


Ist das wirklich so? "The new PopupList" wurde doch explizit mit .Create erzeugt und im Originalcode gibt es gar keinen finalization-Abschnitt!

Ich habe den Code jetzt wie von dir vorgeschlagen angepasst.

Ja. Wenn man den Zusammenhang kennt dann macht es so wie dort geschrieben Sinn. Und zwar auch ohne finalization in deiner unit.
Um das aktive Popup rauszubekommen wird dir nix anderes übrig bleiben als deinen Weg zu nutzen.

Was das PerformMessage angeht... das ist Geschmackssache. Wenn Du allerdings länger laufende Aktionen vorhast als wie im Beispiel, das ändern der Caption, dann solltest du mit PostMessage arbeiten.
Die Verarbeitung der Nachrichten bleibt so wie sie ist. Es wird nun allerding wirklich eine Nachricht (Windows-Botschaft) gesendet und diese erst verarbeitet wenn das Zielfenster seinen Nachrichtenstapel abarbeitet.


Delphi-Quelltext
1:
2:
3:
4:
5:
procedure TPopupListEx.PerformMessage(cm_msg: integer; msg : TMessage) ;
begin
  if Assigned(Screen.Activeform) then // schöner als test auf "<> nil"
    PostMessage(Screen.ActiveForm.Handle, cm_msg, msg.WParam, msg.LParam);
end;


galagher - Mo 11.03.19 21:07

user profile iconSinspin hat folgendes geschrieben Zum zitierten Posting springen:
Um das aktive Popup rauszubekommen wird dir nix anderes übrig bleiben als deinen Weg zu nutzen.

Kann man es denn nicht über PopupList rausbekommen?


Sinspin - Mo 11.03.19 23:49

Eigentlich bin ich der Meinung: finds doch einfach raus! Da lernste am meißten.
Andererseits, Du wirst noch ein bisschen Spass haben dich mit meinem Quelltext zu befassen. Der sieht saumäßig aus, da einfach nur zusammen geklatscht was nötig ist.
Daher, siehe Anhang ;-).


galagher - Di 12.03.19 22:11

user profile iconSinspin hat folgendes geschrieben Zum zitierten Posting springen:
Andererseits, Du wirst noch ein bisschen Spass haben dich mit meinem Quelltext zu befassen.
Macht prinzipiell das, was ich mir vorstelle. Man müsste nur an einer geeigneten Stelle FLatestPopup := nil anbringen, damit, wenn man mit der Maus über ein TMenuItem geht,das PopupMenu erneut erkannt wird. Erst dachte ich, FLatestPopup := nil gehört entweder ins CM_EXIT_MENU_LOOP oder ins CM_MENU_CLOSED, aber beides führt zu einer Zugriffsverletzung.

Und als zweiter Punkt: Warum kann das PopupMenu erst im WM_MENUSELECT erkannt werden und nicht schon im CM_ENTER_MENU_LOOP? TPopupListEx(Popuplist).LatestPopup wird derzeit dort nicht ermittelt.
Ich bekomm's aber nicht so hin, dass es dort funktioniert.

Scheint mir sinnvoller zu sein, das PopupMenu schon im CM_ENTER_MENU_LOOP zu erkennen und nicht erst, wenn man mit der Maus drübergeht!

Bitte nicht falsch verstehen, das ist keine Kritik! Ich dachte nur, ich kann es so hinbasteln, wie ich es möchte, aber dann funktioniert es nicht.


Sinspin - Mi 13.03.19 08:59

Ist mir schon klar dass du es anders besser finden würdest. Geht mir auch so. Aber ich habe nix gefunden.
Wobei ich auch nicht ernsthaft gesucht habe ob es auch zusätzliche Informationen für den anderen Events gibt.
Das kannst du ja machen. Tante Google wird dir dabei sicher helfen.


galagher - Mi 13.03.19 11:50

Ist nur die Frage, warum man das PopupMenu nicht im CM_ENTER_MENU_LOOP erkennen kann.
Und was das FLatestPopupbetrifft: Man könnte doch and (... <> FLatestPopup) (Ich habe den genauen Code jetzt nicht vor mir) weglassen, das muss ich aber noch testen, ob das auch wirklich immer zuverlässig funktioniert.

Und auch das Setzen von FLatestPopup := nil, das zu einer Zugriffsverletzung führt...

Irgendwie verhält sich der Code etwas seltsam.

user profile iconSinspin hat folgendes geschrieben Zum zitierten Posting springen:
Tante Google wird dir dabei sicher helfen.

Wenn ich etwas finde oder auf etwas draufkomme, stelle ich es hier rein!


galagher - Sa 16.03.19 22:08

Hallo zusammen!

Ich komme da nicht weiter. Ich dachte mir, warum das Ganze nicht in als eine eigene Komponente TPopupMenuEx realisieren? Gesagt, getan. Ich packe den Code also da rein, aber es tut nicht...

TPopupListEx.PerformMessage und PopupListEx.WndProc arbeiten zwar, PopupListEx.WndProc ruft aber TPopupMenuEx.PopupMenuClosed nicht auf, obwohl alles exakt so ist, wie es war, als ich den Teil, den nun TPopupMenuEx übernehmen soll, noch in Form1 hatte. Verständlich, was ich meine? TPopupMenuEx sollte nach meinem Plan den Part von Form1 übernehmen, TPopupListEx sollte seinen Teil erledigen und gut.

In einem zweiten Schritt hätte ich mir dann überlegt, wie ich ein property OnClose einbaue. So weit kam ich aber zunächst nicht. Es funktionierte einfach nichts richtig.

Dann kam ich auf die Idee, den ganzen PerformMessage-Kram ganz wegzulassen und nur ein property OnClose reinzusetzen. Das hatte ich immerhin schon so hingebracht, dass es funktionierte - allerdings schon gleich beim Popup der Komponente!

Ich krieg's nicht hin. :autsch:

Wie konnten die Programmierer von Borland und/oder Embarcadero denn nur ein OnClose bei einem PopupMenu weglassen? :motz:


Delete - So 17.03.19 00:10

- Nachträglich durch die Entwickler-Ecke gelöscht -


galagher - So 17.03.19 18:55

user profile iconFrühlingsrolle hat folgendes geschrieben Zum zitierten Posting springen:
Zeig mal, wie du es umgesetzt hast.
Ich schau es mir frühestens am SO Abend an.

Ich musste es erst aus dem __history-Ordner rausfischen, hatte es schon gelöscht! :mrgreen:

Das ist also die Version, in der immerhin schon mal PerformMessage ausgeführt wird, aber Prozedur PopupMenuClosed nicht.
Ich habe mich hier erstmal nur auf PopupMenuClosed beschränkt.


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
unit ExPopupMenu;

interface

uses Vcl.Controls, System.Classes, Menus, Messages, Forms;

const
  CM_MENU_CLOSED = CM_BASE + 1001;
  CM_ENTER_MENU_LOOP = CM_BASE + 1002;
  CM_EXIT_MENU_LOOP = CM_BASE + 1003;

type
  TPopupMenuEx = class (TPopupMenu)
  private
    FPopupMenu: TPopupMenu;
///    procedure PopupMenuEnter(var msg: TMessage) ; message CM_ENTER_MENU_LOOP;
      procedure PopupMenuClosed(var msg: TMessage) ; message CM_MENU_CLOSED;
///    procedure PopupMenuExit(var msg: TMessage) ; message CM_EXIT_MENU_LOOP;
  public
    constructor Create(AOwner: TComponent);// override;
  end;

type
  TPopupListEx = class(TPopupList)
  protected
    procedure WndProc(var Message: TMessage) ; override;
  private
    procedure PerformMessage(cm_msg : integer; msg : TMessage) ;
  end;

implementation

uses Unit1;// Zu Testzwecken!

constructor TPopupMenuEx.Create(AOwner: TComponent);
begin
  FPopupMenu := Self;
  inherited Create(AOwner);
end;

procedure TPopupListEx.PerformMessage(cm_msg: integer; msg : TMessage) ;
begin
  if Assigned(Screen.Activeform) then
    Screen.ActiveForm.Perform(cm_msg, msg.WParam, msg.LParam) ;
end;

procedure TPopupListEx.WndProc(var Message: TMessage) ;
begin
  case message.Msg of
    WM_ENTERMENULOOP: PerformMessage(CM_ENTER_MENU_LOOP, Message) ;
    WM_EXITMENULOOP : PerformMessage(CM_EXIT_MENU_LOOP, Message) ;
    WM_MENUSELECT :
    with TWMMenuSelect(Messagedo
    begin
      if (Menu = 0and (Menuflag = $FFFFthen
        PerformMessage(CM_MENU_CLOSED, Message) ;  //<- Dies wird ausgelöst, ...
    end;
  end;
  inherited;
end;

//... aber das hier nicht, obwohl es im private von TPopupMenuEx angegeben ist!
procedure TPopupMenuEx.PopupMenuClosed(var msg: TMessage) ;
begin
//Hier soll noch ein Prozedur-Aufruf rein,
//der per property OnClose gesetzt wird.
form1.Caption := 'Ja!';
end;

initialization
  PopupList:= TPopupListEx.Create; //create the new one
    // The new PopupList will be freed by
    // finalization section of Menus unit.

end.


Sinspin - Mo 18.03.19 18:35

user profile icongalagher hat folgendes geschrieben Zum zitierten Posting springen:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
type
  TPopupMenuEx = class (TPopupMenu)
  private
    FPopupMenu: TPopupMenu;
///    procedure PopupMenuEnter(var msg: TMessage) ; message CM_ENTER_MENU_LOOP;
       procedure PopupMenuClosed(var msg: TMessage) ; message CM_MENU_CLOSED;
///    procedure PopupMenuExit(var msg: TMessage) ; message CM_EXIT_MENU_LOOP;
  public
    constructor Create(AOwner: TComponent);// override;
  end;

Die procedure PopupMenuClosed solltest du dann aber auch in dem Form deklarieren dass du via PerformMessage aufrufst. Im Popup bringt das nix. Zumal ein Popup keine Form zu sein scheint.
Ich habe jedenfalls keine WindowProc gefunden die darauf hindeuten würde.
Windowsbotschaften können nur ausgeführt werden wenn dafür auch eine WindowProc da ist in die die selber deklarierten Botschaftsempfänger vom Compiler eingehangen werden.
Wenn ich Heute Abend noch Kopf für sowas habe schau ich nochmal rein.


galagher - Mo 18.03.19 19:23

user profile iconSinspin hat folgendes geschrieben Zum zitierten Posting springen:
Die procedure PopupMenuClosed solltest du dann aber auch in dem Form deklarieren dass du via PerformMessage aufrufst.

Nun, idealerweise gibt es dann in meiner Komponente ein property, das beim Doppelklick im Objektinspektor genau diese Prozedur in dem Form einfügt. Also zB. property OnClose (OnClose - ist das eigentlich ein reservierter Ausdruck? Wenn ja, dann eben zB. OnPopDown). Da doppelklickt man im OI drauf und es wird procedure TForm1.PopupMenuClosed erstellt.

Das ist der Plan, aber wie ich das hinbekomme, weiss ich nicht.


Delete - Di 19.03.19 18:59

- Nachträglich durch die Entwickler-Ecke gelöscht -


galagher - Do 21.03.19 09:20

Momentan komme ich nicht dazu, aber ich werde mir das noch anschauen und sehen, wie weit ich komme.