Autor Beitrag
Gausi
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 8548
Erhaltene Danke: 477

Windows 7, Windows 10
D7 PE, Delphi XE3 Prof, Delphi 10.3 CE
BeitragVerfasst: Di 16.01.07 21:28 
Ich hab hier ein kleines Problem mit ner Komponente, die ich von TPanel abgeleitet habe. Diese hat ein OnPaint Event, sowie ein Canvas. Sinn ist der, dass ich selbst das Panel gestalten kann, ohne Paintboxen o.ä. über das Panel legen zu müssen. Das sieht bei mir dann so aus:
ausblenden 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:
unit NempPanel;
//...
type
  TNempPanel = class(TPanel)
  private
    FOnPaint: TNotifyEvent;
    FOwnerDraw: Boolean;
  protected
    procedure Paint; override;
  public
    property Canvas;
  published
    property OnPaint: TNotifyEvent read FOnPaint write FOnPaint;
    property OwnerDraw: Boolean read FOwnerDraw write FOwnerDraw;
  end;

implementation

procedure TNempPanel.Paint;
begin
  if FOwnerDraw AND Assigned(FOnPaint) then
    FOnPaint(Self)
  else
    inherited paint;
end;
//...
end.
Dasselbe habe ich auch mit ner Groupbox gemacht.

Ich habe nun in meiner Anwendung so ein Panel, und darauf mehrere solcher Groupboxen mit OnPaint-Event. Wenn nun auf einer dieser Groupbox etwas passiert (es wird ein Button verschoben, bzw. er ändert von alleine seine Position), wird da sinnigerweise das OnPaint-Event ausgelöst. Das ist gewollt. Leider verursacht das scheinbar eine OnPaint-Kaskade. D.h. OnPaint des Parent-Panels wird mehrfach aufgerufen (immer 13mal), und das OnPaint-Event der "Nachbar-Groupboxen" wird ebenfalls aufgerufen. In welcher Reihenfolge das genau passiert, habe ich noch nicht herausgefunden. Ich vermute, dass das OnPaint der Box ein Paint des Parents aufruft, was dann seinerseits die Childs neumalt, die dann wieder das Parent neu zeichnen (das würde dann mit den 13 in etwa hinkommen).

Das möchte ich gerne verhindern, da ich dadurch unter anderem ein Flackern bekomme. Gibts da nen Trick, oder muss ich anders an die Sache rangehen?

Ziel ist es, dass ich eine Komponente habe, die entweder von Windows gezeichnet wird (Standardaussehen), aber auch selbst gezeichnet werden kann (z.B. Hintergrundbild).

_________________
We are, we were and will not be.
alias5000
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 2145

WinXP Prof SP2, Ubuntu 9.04
C/C++(Code::Blocks, VS.NET),A51(Keil),Object Pascal(D2005PE, Turbo Delphi Explorer) C# (VS 2008 Express)
BeitragVerfasst: Di 16.01.07 23:00 
Schau mal ob dir dieser Code was bringt: www.delphi-forum.de/...68639&highlight=
powered by 100th topic

Gruß alias5000

_________________
Programmers never die, they just GOSUB without RETURN
Gausi Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 8548
Erhaltene Danke: 477

Windows 7, Windows 10
D7 PE, Delphi XE3 Prof, Delphi 10.3 CE
BeitragVerfasst: Mi 17.01.07 00:49 
So auf die Schnelle: Damit lässt sich was in der Richtung anfangen. Ein erster Versuch bewirkte ein endloses gegenseitiges Repaint, ein zweiter eine Verringerung von 13 auf 9 Repaints, dafür einige Darstellungsfehler. Ich werde aber da weiterprobieren.
Das ganze hängt auch vom Darstellungsmodus ab - bei Einstellung "klassisch" gibts deutlich weniger Repaints (nur noch 2) als beim "XP-Stil". Hängt vermutlich damit zusammen, dass im XP-Stil die Groupbox transparent ist...

Ich vermute allerdings, dass es da noch was anderes gibt. Oder ist es normal, dass ein Paint-Event an das Parent-Control durchgereicht wird? Und falls ja, wie kann man das unterbinden?
Nach unten ist das ja sinnvoll (also das ein Control beim Paint auch seine Childs neumalt). Aber nach oben? Ich hoffe mal, dass mir morgen früh jemand sagt, was ich bei der Komponente falsch gemacht habe 8).

_________________
We are, we were and will not be.
Martok
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 3661
Erhaltene Danke: 604

Win 8.1, Win 10 x64
Pascal: Lazarus Snapshot, Delphi 7,2007; PHP, JS: WebStorm
BeitragVerfasst: Mi 17.01.07 00:59 
Ich werf jetzt vor dem Schlafengehen nochmal was in den Raum:
WM_ERASEBACKGROUND. Könnte das was damit zu tun haben, dass die immer mit einem WM_PAINT auftritt, und dann 'irgendwen' neu zeichnen will?

Die könnte man dann mit msg.Result := 1 abwürgen, vielleicht hilft das?

_________________
"The phoenix's price isn't inevitable. It's not part of some deep balance built into the universe. It's just the parts of the game where you haven't figured out yet how to cheat."
Gausi Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 8548
Erhaltene Danke: 477

Windows 7, Windows 10
D7 PE, Delphi XE3 Prof, Delphi 10.3 CE
BeitragVerfasst: Mi 17.01.07 12:06 
Ohja, das hilft. Ein
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
private
    procedure WMEraseBkGnd(var Message: TWMEraseBkGnd); message WM_ERASEBKGND;
//...
procedure TNempGroupBox.WMEraseBkGnd(var Message: TWMEraseBkGnd);
begin
  if FOwnerDraw AND Assigned(FOnPaint) then
    Message.Result := 1
  else
    inherited;
end;
verringert das Neuzeichnen auf ein vernünftiges Maß (noch 5, aber das liegt wohl daran, dass ich beim Panel das noch nicht eingebaut habe, und auch noch vier kleine Panels auf dem Panel mit den ganzen Groupboxen liegen) und das flackern ist weg. Ich hatte gehofft, dass ein Message.Result irgendwo was bringen würde, aber wo ich das genau (und was genau) einbauen sollte, war mir unklar. Manchmal braucht man nur das richtige Stichwort. Danke! :zustimm:

_________________
We are, we were and will not be.
Martok
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 3661
Erhaltene Danke: 604

Win 8.1, Win 10 x64
Pascal: Lazarus Snapshot, Delphi 7,2007; PHP, JS: WebStorm
BeitragVerfasst: Mi 17.01.07 20:58 
*Juhuu, richtig geraten :dance: *

Schön, dass ich helfen konnte :)

_________________
"The phoenix's price isn't inevitable. It's not part of some deep balance built into the universe. It's just the parts of the game where you haven't figured out yet how to cheat."
delfiphan
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2684
Erhaltene Danke: 32



BeitragVerfasst: Sa 20.01.07 05:24 
Wie gesagt ist dein Flackern auf das Zeichnen des Hintergrundes zurückzuführen... Das ist weder mit Double-Buffered zu beseitigen, noch hat das mit mehrmaligem Aufrufen von OnPaint zu tun. Die WM_ERASEBKGND-Methode verhindert natürlich immer dieses Flackern, denn die Ursache für das Flackern wird beseitigt. Je nach dem führt das aber zu unerwünschten Nebeneffekten - grundlos wird der Hintergrund ja nicht gelöscht. Wenn bei deinem Fall nirgends der Hintergrund durchschimmert (weil du die ganze Fläche bemalst) funktioniert die Methode. Wenn du jedoch die Standard-Skins von Windows zulassen willst, würde ich das nicht unbedingt empfehlen, da das vorherie Zeichnen des Hintergrundes eventuell notwendig ist.

Übrigens: Bei den 13 Paints handelt es sich kaum um die selben Bereiche, die gezeichnet werden sollen, oder? Sondern es handelt sich bestimmt um verschiedene Bereiche um die Child-Components herum? So ineffizient ist Windows wahrscheinlich nicht ;) ganz im Gegenteil: Wenn sich die Bereiche tatsächlich nicht überschneiden, dann finde ich das recht clever von Windows.
Gausi Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 8548
Erhaltene Danke: 477

Windows 7, Windows 10
D7 PE, Delphi XE3 Prof, Delphi 10.3 CE
BeitragVerfasst: Sa 20.01.07 11:19 
user profile icondelfiphan hat folgendes geschrieben:
Je nach dem führt das aber zu unerwünschten Nebeneffekten - grundlos wird der Hintergrund ja nicht gelöscht. Wenn bei deinem Fall nirgends der Hintergrund durchschimmert (weil du die ganze Fläche bemalst) funktioniert die Methode. Wenn du jedoch die Standard-Skins von Windows zulassen willst, würde ich das nicht unbedingt empfehlen, da das vorherie Zeichnen des Hintergrundes eventuell notwendig ist.

Die Komponenten sind von TPanel bzw. TGroupbox abgeleitet. Unter XP sind diese transparent (wenn man ein Manifest auf der Form hat, siehe Anhang). Wenn die Groupbox/Panel neugezeichnet wird, ist somit ein Repaint des Hintergrundes nötig. In meinem Fall übermale ich die Box aber komplett, und es schimmert nichts durch. Daher auch das if... else inherited. Ohne dem ist die Konstruktion z.B. zur Entwurfszeit sehr unübersichtlich ;-)
user profile icondelfiphan hat folgendes geschrieben:
Übrigens: Bei den 13 Paints handelt es sich kaum um die selben Bereiche, die gezeichnet werden sollen, oder? Sondern es handelt sich bestimmt um verschiedene Bereiche um die Child-Components herum? So ineffizient ist Windows wahrscheinlich nicht ;) ganz im Gegenteil: Wenn sich die Bereiche tatsächlich nicht überschneiden, dann finde ich das recht clever von Windows.
Das kann sein. Ich habe nur die Paint-Ereignisse einer Groupbox gezählt, und die ging in 13er-Schritten hoch.
Einloggen, um Attachments anzusehen!
_________________
We are, we were and will not be.
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19315
Erhaltene Danke: 1747

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Sa 20.01.07 12:20 
Bei WM_ERASEBACKGROUND habe ich allerdings im Moment das Problem, dass, wenn ich es abfange, es Grafikfehler gibt. Bei mir habe ich eine VirtualTreeView und eigene Scrollbars rechts und unten, die natürlich mitgehen. Dabei "fühlt sich jetzt das Programm träge an", wenn man es resized, weil es Schlieren zieht, wenn man es vergrößert. Der Repaint kommt nicht häufig genug. Ich muss noch herausfinden, woran das liegt.

Aber was ich damit sagen will: Wenn du auch die Komponente mit dem Formular resizen willst, dann solltest du ausprobieren was passiert, wenn du es ein wenig schneller vergrößerst oder so (vielleicht liegts bei mir aber auch nur an der VirtualTreeView).
Bzw. wenn ich mir den Namen der GroupBox so ansehe, dann ist das mit dem Testen ja ohnehin kein Problem ;-).