Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   GUI-Design mit VCL / FireMonkey / Common Controls (https://www.delphipraxis.net/18-gui-design-mit-vcl-firemonkey-common-controls/)
-   -   Delphi MenuItem neu zeichnen ohne ganzes Menü neu zu zeichnen (https://www.delphipraxis.net/196205-menuitem-neu-zeichnen-ohne-ganzes-menue-neu-zu-zeichnen.html)

StephanR 1. Mai 2018 10:51

MenuItem neu zeichnen ohne ganzes Menü neu zu zeichnen
 
Hallo DPler,

in meinem aktuellen Projekt muss ich während ein PopUpMenü bereits geöffnet ist und dargestellt wird den Caption mehrerer MenuItems ändern.
Die Änderung selbst ist weiters unproblematisch, allerdings löst das Ändern der Caption das Kommando MenuChanged(true) aus, welches dann das
ganze Menü neuzeichnet. Dabei wird aber bei jeder Änderung das PopUp geschlossen und neu geöffnet, was ganz nervig "flackert".
Ich habe daraufhin eine Childklasse erstellt (ThackedMenuItem) und darin die SetCaption-Funktion so überschrieben, dass ich MenuChanged blockieren kann.

Leider habe ich bislang keinen Weg gefunden, trotzdem das Neuzeichnen des jeweiligen MenuItem auszulösen (und nur dieses). Erst wenn der Nutzer mit der Maus darüberfährt wird über das Hover-Ereignis der Menüeintrag neu gezeichnet.

Gibt es irgend einen anderen Weg, das Neuzeichnen auch ohne Mausereignis auszulösen? Invalidate etc. gibt es ja leider nicht, da TMenuItem kein zeichenbares Control ist... Kann man Hover oder Mouseleave an ein MenuItem senden, so dass dann neu gezeichnet wird?

Alternativ habe ich versucht, in der SetCaption-Funktion die Routine DrawItem aufzurufen (ist OwnerDraw). Dabei habe ich es aber bislang nicht geschafft den dazugehörigen Canvas des Menüfensters zu ermitteln. Somit klappt das neu selber darüber zeichnen bislang nicht.

Es wäre toll, wenn Ihr für eine der beiden Varianten eine Lösung kennen solltet und diese hier posten könntet.

Vielen Dank
Stephan

EWeiss 2. Mai 2018 05:53

AW: MenuItem neu zeichnen ohne ganzes Menü neu zu zeichnen
 
Delphi-Quellcode:
if (PDis.itemAction and ODA_DRAWENTIRE) <> 0 then


und vielleicht noch etwas mit dem DC rumspielen.
Delphi-Quellcode:
GetClipBox(PDis.HDC, Rect);


Aber ohne WM_DRAWITEM geht wohl gar nichts.

gruss

hoika 2. Mai 2018 08:25

AW: MenuItem neu zeichnen ohne ganzes Menü neu zu zeichnen
 
Hallo,
hilft vielleicht EnableMenuItem (oder MnuItem.Enabled)?
Das einmal auf False und dann wieder auf True zurück.
Das müsste ja eigentlich das Neuzeichnen aufrufen.

Das Popupmenu wird zumindestens schon mal nicht neu aufgebaut.

Mavarik 2. Mai 2018 08:44

AW: MenuItem neu zeichnen ohne ganzes Menü neu zu zeichnen
 
Nur mal aus Neugier:
Warum Items neu zeichnen?

Was hat sich den geändert, was nicht in der MS vor dem öffnen nicht schon bekannt war?

StephanR 2. Mai 2018 09:04

AW: MenuItem neu zeichnen ohne ganzes Menü neu zu zeichnen
 
Zitat:

Zitat von EWeiss (Beitrag 1401018)
Delphi-Quellcode:
if (PDis.itemAction and ODA_DRAWENTIRE) <> 0 then


und vielleicht noch etwas mit dem DC rumspielen.
Delphi-Quellcode:
GetClipBox(PDis.HDC, Rect);


Aber ohne WM_DRAWITEM geht wohl gar nichts.

gruss

Hallo EWeiss,

um ein WM_DRAWITEM manuell auszulösen benötige ich aber wenigstens das Handle des Canvas, auf dem das Menü(item) gezeichnet werden soll. Und genau das habe ich bislang nicht herausgefunden. Nächstes Problem wäre dann die genaue Position des Rect, indem das Menuitem gezeichnet werden soll. Aber das könnte ich zur not noch ausrechnen...

Viele Grüße
Stephan

StephanR 2. Mai 2018 09:06

AW: MenuItem neu zeichnen ohne ganzes Menü neu zu zeichnen
 
Zitat:

Zitat von hoika (Beitrag 1401027)
Hallo,
hilft vielleicht EnableMenuItem (oder MnuItem.Enabled)?
Das einmal auf False und dann wieder auf True zurück.
Das müsste ja eigentlich das Neuzeichnen aufrufen.

Das Popupmenu wird zumindestens schon mal nicht neu aufgebaut.

Hallo hoika,

leider löst das Ändern von Enabled wieder MenuChanged aus und das schließt und öffnet das PopUp zur Aktualisierung.
Genau das möchte ich umgehen...

VG
Stephan

StephanR 2. Mai 2018 09:14

AW: MenuItem neu zeichnen ohne ganzes Menü neu zu zeichnen
 
Zur Erläuterung, warum ich denn einzelne Menuitems aktualisieren möchte/muss.

Bei meinem Projekt handelt es sich um eine Software für Sportwettbewerbe, die auf mehreren Clients im Netzwerk parallel betrieben wird. Die Daten liegen dabei zentral auf einem Fileserver.
Das Menü, welches zum Öffnen der einzelnen Wettbewerbe verwendet wird, soll neben dem Namen des einzelnen Wettbewerbs zu jedem Wettbewerb den aktuellen Status anzeigen (x Starter gemeldet, Wettbewerb in der x. Runde mit y Startern,...). Das Sammeln dieser Daten ist jedoch im Netzwerkbetrieb so langsam, dass der Nutzer ziemlich lange auf das Öffnen des Menüs warten müsste (sollte ich diese Daten erst Sammeln und dann das Menü anzeigen).
Deshalb habe ich das Zusammenstellen dieser Daten in einen parallelen Thread ausgelagert, der noch läuft während das Menü bereits geöffnet ist. Das Menü ist somit zunächst nur mit den Namen der Wettbewerbe bestückt und wenn dann die Statusdaten bereitstehen sollen diese mit angezeigt werden.
Aktuell geschieht diese Aktualisierung/das Neuzeichnen des MenuItems durch die Bewegung der Maus über die einzelnen Menüpunkte (Hover-Ereignis).
Dies ist aber nicht sonderlich intuitiv... Deshalb möchte ich gerne das Neuzeichnen der MenuItems anstoßen, sobald die jeweilige Info verfügbar wurde.

VG
Stephan

EWeiss 2. Mai 2018 09:14

AW: MenuItem neu zeichnen ohne ganzes Menü neu zu zeichnen
 
Zitat:

Zitat von StephanR (Beitrag 1401033)
Zitat:

Zitat von EWeiss (Beitrag 1401018)
Delphi-Quellcode:
if (PDis.itemAction and ODA_DRAWENTIRE) <> 0 then


und vielleicht noch etwas mit dem DC rumspielen.
Delphi-Quellcode:
GetClipBox(PDis.HDC, Rect);


Aber ohne WM_DRAWITEM geht wohl gar nichts.


gruss

Hallo EWeiss,

um ein WM_DRAWITEM manuell auszulösen benötige ich aber wenigstens das Handle des Canvas, auf dem das Menü(item) gezeichnet werden soll. Und genau das habe ich bislang nicht herausgefunden. Nächstes Problem wäre dann die genaue Position des Rect, indem das Menuitem gezeichnet werden soll. Aber das könnte ich zur not noch ausrechnen...

Viele Grüße
Stephan

Delphi-Quellcode:
hMenuWnd := WindowFromDC(PDis.HDC);

und

Delphi-Quellcode:
PDis.hwndItem

Bei dem Rect musst du gar nichts berechnen diesen gibt dir
GetClipBox zurück.. und zwar den vom selektierten HDC


gruss

StephanR 2. Mai 2018 09:48

AW: MenuItem neu zeichnen ohne ganzes Menü neu zu zeichnen
 
Delphi-Quellcode:
hMenuWnd := WindowFromDC(PDis.HDC);

und

Delphi-Quellcode:
PDis.hwndItem

Bei dem Rect musst du gar nichts berechnen diesen gibt dir
GetClipBox zurück.. und zwar den vom selektierten HDC


gruss[/QUOTE]

Hallo EWeiss,

Danke für Deine schnelle Antwort. Kannst Du mir bitte noch erklären, worauf genau sich "PDis" bezieht?
Ich steh da nämlich gerade etwas auf dem Schlauch...
Aktuell hatte ich vor, in meiner Child-Routine des SetCaption eine WM_DRAW-Message zusammenzubauen beziehungsweise DrawItem direkt aufzurufen.
Dort habe ich zwar Zugriff auf das Handle des TMenuItem aber PDis und sein HDC sind mir dort nicht bekannt.
Vielleicht kannst Du mir etwas mehr Info rund um Deinen Tipp geben :-)

Vielen Dank
Stephan

EWeiss 2. Mai 2018 09:55

AW: MenuItem neu zeichnen ohne ganzes Menü neu zu zeichnen
 
Sollte selbst erklärend sein.

Delphi-Quellcode:
  PDis: PDrawItemStruct absolute lP;
Initialisierung..

Delphi-Quellcode:
    WM_DRAWITEM:
      begin
        PDis := Pointer(lP);
        case PDis^.CtlType of
          ODT_MENU: // 1 Ownerdrawn menu item
            begin
              hMenuWnd := WindowFromDC(PDis.HDC);

gruss

StephanR 2. Mai 2018 10:07

AW: MenuItem neu zeichnen ohne ganzes Menü neu zu zeichnen
 
Zitat:

Zitat von EWeiss (Beitrag 1401040)
Sollte selbst erklärend sein.

Delphi-Quellcode:
  PDis: PDrawItemStruct absolute lP;
Initialisierung..

Delphi-Quellcode:
    WM_DRAWITEM:
      begin
        PDis := Pointer(lP);
        case PDis^.CtlType of
          ODT_MENU: // 1 Ownerdrawn menu item
            begin
              hMenuWnd := WindowFromDC(PDis.HDC);

gruss

Hallo MWeiss,

ich habs immer noch nicht kapiert...
Für mich sieht dies so aus, als setzt Du voraus, es gäbe bereits eine Message WM_DRAWITEM, die ich gerade empfangen und analysieren kann.

So wie ich das sehe, muss ich aber diese Message selber zusammenstellen wenn ich eine MenuItem-Caption geändert habe (es wird ja beim Ändern der Caption eines MenuItem keine solche Message ausgelöst, sonst hätte ich ja das ganze Problem nicht).
Woher bekomme ich denn dann die ganzen Infos über den DeviceContext etc., die mir die empfangene Message einfach so schenken würde, damit ich die Message auslösen kann?

Viele Grüße
Stephan

EWeiss 2. Mai 2018 10:12

AW: MenuItem neu zeichnen ohne ganzes Menü neu zu zeichnen
 
Wenn du ein Menu erstellst wird WM_DRAWITEM immer aufgerufen unabhängig davon ob es ein OwnerDraw Menu ist oder nicht.
Wenn du mit VCL arbeitest musst du diese Message natürlich überschreiben wenn du damit arbeiten möchtest.

such mal im Forum da wird sich bestimmt was finden..
Ein Beispiel hier.

https://www.delphipraxis.net/186217-...usfuehren.html

gruss

freejay 2. Mai 2018 16:05

AW: MenuItem neu zeichnen ohne ganzes Menü neu zu zeichnen
 
Mir scheint ja, dass Du zwar mit einem TPopUpMenu arbeitest, aber eigentlich ja tabellarische Informationen anzeigst. Dann könnte man auch ein Grid oder eine Listbox verwenden. Dann müsstest Du dem - für Deinen speziellen Anwendungsfall - zickigen PopUpMenü keine Manieren beibringen, sondern könntest mit einem sehr genügsamen Grid oder einer ListBox arbeiten. :wink:


Alle Zeitangaben in WEZ +1. Es ist jetzt 07:44 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz