Delphi-PRAXiS
Seite 1 von 2  1 2      

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 Welche Windows-Events zum Zeichnen eines Buttons (https://www.delphipraxis.net/58586-welche-windows-events-zum-zeichnen-eines-buttons.html)

sh17 9. Dez 2005 10:20


Welche Windows-Events zum Zeichnen eines Buttons
 
Folgendes Problem:

Ich habe eine abgeleitete TButton-Klasse, bei der ich die Caption und noch anderes selbst zeichne. Der Button selbst wird weiterhin von Windows erstellt.

Gezeichnet wird das ganze über

Delphi-Quellcode:
procedure TMyButton.WndProc(var Message: TMessage);
begin
    inherited WndProc(Message);
    if Message.Msg in [WM_PAINT,WM_ACTIVATE,BM_SETSTATE,WM_ENABLE,WM_NCPAINT] then
    begin
      AdditionalPaint;
    end;
end;
Bei XP kommt es manchmal vor, das AdditionalPaint nicht durchlaufen wird und der Button dann leer ist. Das ganze tritt zufällig auf und meistens, wenn ich aus einer anderen Maske wiederkomme. Fährt man mit der Maus über den leeren Button, wird das Zeichnen wieder erzwungen.

Meine Frage: Welche Windows-Ereignisse gibt es noch, auf die ich das Zeichnen anwerfen muss?

Luckie 9. Dez 2005 11:05

Re: Welche Windows-Events zum Zeichnen eines Buttons
 
MSDN-Library durchsuchenInvalidateRect erzwingt ein Neuzeichnen des betreffenden Fensters.

Vjay 9. Dez 2005 11:26

Re: Welche Windows-Events zum Zeichnen eines Buttons
 
InvalidateRect erzeugt doch auch nur eine WM_PAINT ( + evtl. WM_ERASEBKGND) ...

Ich vermute, dass manche (Windowsbehandlungs-)Funktionen den Button direkt über DrawFrameControl neu Zeichnen und das Windows-Messageprinzip umgehen.

Ich habe nämlich genau das gleiche Problem.
Ein Beispiel: Wenn man in einer nicht (Manifest-) XP-Anwendung die Maustaste auf dem Button gedrückt hält, so wird dieser neu gezeichnet, aber ich erhalte nie eine Message darüber.

Evtl. hilft dir ein Funktionshook weiter, ich habe an diesem Punkt keine Lust mehr gehabt weiter zu suchen.

Weiterhelfen konnte mir damals auch niemand, da dies doch wirklich tief ins Eingemachte geht.

Ich hänge hier mal meine alte WNDProc an, die größtenteils funktioniert.
Zur Erklärung: Während die Maustasten gedrückt werden, erzeuge ich ein Overlay-Fenster, welches das Neuzeichnen des Buttons durch die Original-Wndproc verdeckt.
Es gibt zwar Funktionen um das Neuzeichenen von Fenster vorübergehend zu unterdrücken, aber von diesen kann ich nur abraten, da sie negative Nebeneffekte besitzen. Besonders wenn man in Fremdanwendungen "rumwurschtelt" sollte man sich so transparent und unauffällig benehmen wie es nur möglich ist.

Delphi-Quellcode:
// result= true = originalwndproc wird direkt aufgerufen
function TPulseButton.buttonWndProc( const handle: dWord; const Msg: cardinal; const wParam, lParam: dWord; var rueckgabe: dWord): Boolean;
begin
 result:= false;
 if MovingControlWndProc( handle, Msg, wParam, lParam) then exit;

 
   case Msg of
    WM_PAINT{, WM_NCPAINT}: begin
               if wParam= 0 then Paint
               else result:= true;
              end;
    WM_TIMER: begin
               if WParam = 999 then NextPulse
               else if WParam = 996 then
               begin
                if not isAltDown then
                begin
                 killTimer(handle, 996);
                 InvalidateRect(handle, NIL, false);
                end;
               end
               else if WParam = 998 then checkMouseOver
               else if WParam = 995 then
               begin
                killTimer(handle, 995);
                InvalidateRect(handle, NIL, false);
               end
               else result:= true;

               result:= true;
              end;
   WM_SIZE, WM_WINDOWPOSCHANGED, WM_MOVE: begin
              Rueckgabe := CallWindowProc( origWndProc, handle, Msg, wParam, lParam);
              InvalidateRect(handle, NIL, false);
             end;
   WM_LBUTTONDOWN: begin
                     if not LButtonDown then
                     begin
                      LButtonDown:= true;
                      activate;
                      createOverlayWindow;
                      Rueckgabe := CallWindowProc( origWndProc, handle, Msg, wParam, lParam);
                      destroyOverlayWindow;
                      InvalidateRect(handle, NIL, false);
                     end;
                    end;
   WM_LBUTTONUP, WM_LBUTTONDBLCLK: begin
                   if LButtonDown then
                   begin
                    LButtonDown:= false;
                    createOverlayWindow;
                    Rueckgabe := CallWindowProc( origWndProc, handle, Msg, wParam, lParam);
                    destroyOverlayWindow;
                    InvalidateRect(handle, NIL, false);
                   end;
                  end;
   WM_ENABLE, WM_RBUTTONDBLCLK, WM_SHOWWINDOW, WM_NCACTIVATE, WM_CAPTURECHANGED,
   WM_KILLFOCUS, WM_SETFOCUS, BM_SETSTATE, BM_SETSTYLE, WM_SETTEXT: begin
                  createOverlayWindow;
                  Rueckgabe := CallWindowProc( origWndProc, handle, Msg, wParam, lParam);
                  destroyOverlayWindow;
                  InvalidateRect(handle, NIL, true);
                  Paint;
                  InvalidateRect(handle, NIL, false);
                 end;
    WM_NCHITTEST: begin
                   Rueckgabe := HTCLIENT;
                   InvalidateRect(handle, NIL, false);
                  end;
    WM_MOUSEMOVE: begin
                   Rueckgabe := CallWindowProc( origWndProc, handle, Msg, wParam, lParam);
                   InvalidateRect(handle, NIL, false);
                   killTimer(handle, 998);
                   setTimer(handle, 998, 100, NIL);
                  end;
    WM_ACTIVATEPULSING: Pulsing:= boolean( wParam);
    WM_NCDESTROY: begin
                 Rueckgabe := CallWindowProc( origWndProc, handle, Msg, wParam, lParam);
                 deInitControl( handle);
                end;
   else
    result:= true;
   end;
 
end;
Dazu noch der besagte Funktionshook hat mir recht passable Ergebnisse gebracht. Ist allerdings größtenteils ungetestet. Es gibt einfach zuviele Anwendungen und Fälle.

sh17 9. Dez 2005 12:06

Re: Welche Windows-Events zum Zeichnen eines Buttons
 
ich probier es mal aus. Danke erst mal.

Flocke 10. Dez 2005 01:15

Re: Welche Windows-Events zum Zeichnen eines Buttons
 
Liste der Anhänge anzeigen (Anzahl: 2)
Zitat:

Zitat von Vjay
Ich vermute, dass manche (Windowsbehandlungs-)Funktionen den Button direkt über DrawFrameControl neu Zeichnen und das Windows-Messageprinzip umgehen.

Ich habe nämlich genau das gleiche Problem.
Ein Beispiel: Wenn man in einer nicht (Manifest-) XP-Anwendung die Maustaste auf dem Button gedrückt hält, so wird dieser neu gezeichnet, aber ich erhalte nie eine Message darüber.

Genau da liegt das Problem - ein TButton ist eben kein natives Delphi-Control sondern eine von Windows vorgegebene Fensterklasse. Da kann man das Zeichnen nicht erweitern, sondern müsste es komplett neu schreiben.

Ich fand das Thema ganz interessant :stupid: und habe mal eine von TButton abgeleitete Komponente geschrieben, die BS_OWNERDRAW nutzt. Im Gegensatz zu WM_PAINT kriegt man WM_DRAWITEM/CN_DRAWITEM nämlich jedesmal, wenn der Button bzw. Teile davon neu gezeichnet werden müssen.

Code siehe Anlage (OwnerDrawButton.pas), unterstützt Themes und Hot-Tracking. Ihr könnt den Button entweder komplett selbst zeichnen oder nur den Inhalt (den Rahmen übernimmt dann der Komponente).

Als Beispiel ist im 2. Quelltext (ButtonWithImage.pas) eine abgeleitete Klasse drin, die einen Standard-Button mit Bild zeichnet (über Images/ImageIndex, so dass Actions funktionieren) - soll aber wie gesagt nur als Beispiel dienen, wie man daraus eigene Button-Klassen machen kann.

// EDIT

Hab' beide Dateien noch mal überarbeitet (hatte ja eh' noch niemand heruntergeladen).

Übrigens habe ich gesehen, dass TBitBtn dasselbe Verfahren benutzt (die Quellen sind sogar ziemlich ähnlich), also könnte man auch den Subclassen bzw. als Vorlage benutzen.

sh17 12. Dez 2005 05:16

Re: Welche Windows-Events zum Zeichnen eines Buttons
 
Zitat:

Hab' beide Dateien noch mal überarbeitet (hatte ja eh' noch niemand heruntergeladen).
habs runtergeladen :-D
schau es mir heut mal an.

sh17 16. Dez 2005 12:51

Re: Welche Windows-Events zum Zeichnen eines Buttons
 
Liste der Anhänge anzeigen (Anzahl: 1)
@Flocke

so, hab meine Button-Klasse mal umgebaut mit Deiner Variante. Soweit funktionierts

Nur wird um die Buttons so eine Art "Dreckrand" gezeichnet. Tritt das bei Dir auch auf? (siehe Anhang)

Flocke 16. Dez 2005 15:51

Re: Welche Windows-Events zum Zeichnen eines Buttons
 
Hmmm ... bei mir sehen die Buttons ganz normal aus. Zeichnet du den Rahmen selbst? Ein bisschen Quelltext wäre nicht schlecht.

sh17 19. Dez 2005 08:41

Re: Welche Windows-Events zum Zeichnen eines Buttons
 
ich hab es mal mit Deinem Original-Button probiert, der selbe Effekt. Das einzige, was ich geändert habe, ist: Die Units ThemeMgr,ThemeSrv hinzugefügt, weil Delphi6. Vielleicht liegt das der Hund begraben.

Flocke 19. Dez 2005 15:42

Re: Welche Windows-Events zum Zeichnen eines Buttons
 
Wie sieht das Ganze denn aus, wenn du zum Testen mal das Standardlayout verwendest und kein spezielles Theme?

Häng' doch mal deinen aktuellen Quelltext an (oder per PN), dann kann ich das hier testen.


Alle Zeitangaben in WEZ +1. Es ist jetzt 15:37 Uhr.
Seite 1 von 2  1 2      

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