![]() |
Event bei Z-Order Änderung o.ä.
Aloah!
Ich stelle gerade eine Komponente um (Abkömmling von TGraphicControl), bei der diverse Beschriftungen von TextOut() auf TLables in der Komponente geändert werden. (Hintergrund ist, dass die Beschriftungen z.T. ausserhalb des Rects der Komponente liegen müssen, und wenn man das mittels TextOut() auf den Parent-Canvas malt, verhält sich das in der IDE beim Verschieben usw. sehr hässlich.) Das klappt auch wunderbar (TMS macht es z.B. genau so), jedoch gibt es ein Problem mit Labels, die Teilweise im Rect der Komponente liegen: Wenn man die Komponente mittels BringToFront oder im Editor nach vorne setzt, verschwinden diese Labels hinter ihr. Mein naiver Lösungsversuch: Im Paint() meiner Komponente einfach "Labels[i].BringToFront;". Das löst aber leider auch wiederum das Paint-Ereignis aus, und ist ein prima Weg sich die IDE abzuschießen :mrgreen:. Und eigentlich ist mir das auch zu brutal. Ich würde daher ganz gerne eine Art OnZOrderChange() Event haben, jedoch finde ich hier irgendwie keine Stelle an der ich so etwas abgreifen könnte. Gibt es da überhaupt eine Chance? |
AW: Event bei Z-Order Änderung o.ä.
Dann zeichne doch nicht außerhalb der Komponente?
Du kannst dir ja z.B. ein TLabel als Unterkomponente nehmen und dieses entsprechend positionieren. ![]() |
AW: Event bei Z-Order Änderung o.ä.
Genau das mache ich doch.
|
AW: Event bei Z-Order Änderung o.ä.
Du musst doch nur tätig werden, wenn die Labels unter der Hauptkomponente liegen, also wenn der Z-Index (hmmm, kleiner oder größer) ist. Dann würde ich auf den Z-Index einwirken, sonst nicht. Und neu gezeichnet wird dann eh.
|
AW: Event bei Z-Order Änderung o.ä.
Mein Problem ist ja, dass ich keinen guten... "Zeitpunkt" finde um genau diese Prüfung vorzunehmen (ob mein Label unter der Kompo liegt oder nicht). Im Paint geht's ja schon mal nicht, weil es eine Endlosrekursion erzeugt (und für meinen Geschmak viel zu unnötig oft wäre). Drücke ich mich so undeutlich aus? (Ernste Frage. Ich hatte gehofft mein Problem deutlich dargestellt zu haben - mag nicht immer gelingen wenn man selbst grad tief drin steckt.)
|
AW: Event bei Z-Order Änderung o.ä.
In eine Rekursion solltest Du ja eigentlich nur reinlaufen wenn ungeprüft die ZOrder änderst.
So ganz klar ist mir auch nicht warum Du wenn Du ein GraphicControl verwendest Du noch externe Labels benötigst, statt die Komponente entsprechend zu "vergrößern" und die Labels selbst zu zu malen. |
AW: Event bei Z-Order Änderung o.ä.
Aber gerade wenn ich die Z-Order ändere wird doch ein Neuzeichnen ausgelöst. Mache ich das also im Paint, löse ich wieder ein Paint aus, unabhängig davon ob das Neuordnen notwendig war oder nicht. Ein Mal reicht, und ich hänge in der Schleife fest.
Warum mit Labels? Es geht um eine Balkenanzeige, die Skalen links und oder rechts bzw. oben/unten hat. Die Skalengrenzen (Minimum/Maximum) werden zur Laufzeit aus einer DB versorgt, d.h. ich weiss zur Designtime nicht, wie breit mein Text werden wird. Ich möchte aber meine Balken am Rest auf der Form ausrichten, und zwar wirklich pixelgenau mit dem Balken. Dessen Breite/Höhe würde sich ja sonst verändern. (Vorher war es genau so, jetzt kam aber die Versorgung der Min/Max zur Laufzeit hinzu, womit das auf ein Mal Thema wurde.) Ebenso haben die Balken eine Anzeige des aktuellen Wertes als Zahl. Dessen Position kann allseitig des Balkens sein, oder aber auch darin zentriert. Auch hier wurde es ein Label, da bei Platzierung ausserhalb des Balkens der Balken selbst nicht in seiner Größe geändert werden darf. Und das Problem dass ich habe manifestiert sich nun bei diesem Label, wenn es auf dem Balken zentriert dargestellt wird: Balken.BringToFront -> Label ist auf ein Mal hinten. |
AW: Event bei Z-Order Änderung o.ä.
Cih wollte gerade keine Komponente schreiben, die Simulation hier mit Paintbox und Label überlappend sollte sich IMHO aber identisch verhalten.
Delphi-Quellcode:
Function ZOrderFirst(TestComp,MainComponent:TControl):Boolean; var i:Integer; p:TWinControl; found:Boolean; begin found := false; p := Maincomponent.Parent; if not assigned(p) then Exit; for i := 0 to p.Controlcount - 1 do begin if (p.Controls[i]= TestComp) or (p.Controls[i]= MainComponent) then begin Result := p.Controls[i]= TestComp; break; end; end; end; procedure TForm5.Button1Click(Sender: TObject); begin Paintbox1.BringToFront; end; procedure TForm5.Button2Click(Sender: TObject); begin Label1.BringToFront; end; procedure TForm5.PaintBox1Paint(Sender: TObject); var c:TCanvas; begin c := TPaintBox(sender).Canvas; c.Brush.Color := clBlue; c.Ellipse(TPaintBox(sender).ClientRect); if ZOrderFirst(Label1,TPaintBox(sender)) then begin Label1.BringToFront; end; end; |
AW: Event bei Z-Order Änderung o.ä.
@Bummi :thumb:
Eigentlich könnte man nach dem Label1.BringToFront die OnPaint Methode verlassen, denn es wird ja direkt neu gezeichnet. @Medium Wenn der Z-Index nicht stimmt, dann muss neu gezeichnet werden. Das wird ja auch durch das Umordnen provoziert. Wenn der Z-Index stimmt, dann wird nichts umgeordnet und damit auch kein Neuzeichnen provoziert. So undeutlich schreiben wir doch gar nicht ;) |
AW: Event bei Z-Order Änderung o.ä.
Du durchläufst p.ControlCount (Parent-Beziehung) und greifst dann auf p.Components (Owner-Beziehung) zu. Ist das Absicht?
|
AW: Event bei Z-Order Änderung o.ä.
Hui, das schaut nett aus. Ich glaube, ich hatte auch einen halben Meter zu kurz gedacht vorhin. Schade, dass Komponenten nicht standardmäßig ihren Index in der Parent-Liste bei sich tragen, weil so muss ich teilweise eine ganze Menge durchnudeln um es zu finden, was in jedem Repaint irgendwie unschön aussieht. Praktisch dürfte das glaube ich aber kaum zu merken sein. Probier ich morgen mal umzusetzen, danke!
Nebenfrage: Die Labels werden mit der Balken-Komponente als Owner erstellt, und bekommen dann den Parent der Balken-Kompo selbst auch als Parent. Tauchen die dann überhaupt in der Controls- bzw. Components-Liste des Formulars auf? Edit: Rote Box - das sollte diese Frage beantwortet haben :) |
AW: Event bei Z-Order Änderung o.ä.
Zitat:
|
AW: Event bei Z-Order Änderung o.ä.
Das kann aber fatal enden, deshalb meine Frage ;)
|
AW: Event bei Z-Order Änderung o.ä.
@DeddyH , nein , danke für den Hinweis, habe es korrigiert.
|
AW: Event bei Z-Order Änderung o.ä.
@Sir Rufo
Zitat:
|
AW: Event bei Z-Order Änderung o.ä.
Problem gelöst! Allerdings ein Eckchen aufwendiger :mrgreen:
Zu erreichen war ja, dass meine Sub-Controls in Z-Richtung immer möglichst nahe an meinem eigentlichen Control (Balken) liegen, zumindest aber so, dass folgendes gewährleistet ist: - alle Lables müssen immer über dem Balken liegen - alle Controls, die den Balken verdecken, sollen auch die Labels verdecken - alle Controls, die durch den Balken verdeckt werden, sollen auch hinter den Labels liegen Letztlich also so, als wären die Labels (und Skalenstriche, die ich ebenfalls als Subkomponente gebaut habe weil sie ausserhalb liegen sollen) fester Bestandteil des Balkens, und lägen in der selben Z-Plane wie dieser. Ärgerlich ist mal wieder, dass CodeGear (ja, D2007 :)) ja recht freizügig mit private-Deklarationen in der VCL war. Um o.g. zu erreichen, wäre die Methode TControl.SetZOrderPosition() ideal gewesen, aber eben leider private. Gut, kann man sich abgucken und kopieren, aber leider ist auch das darin benötigte Feld "FControls", sowie die Methode InvalidateControl() private. Und die Methode PaletteChanged() ist protected :? Also musste ein ekeliger Hack her, womit das ganze erstmal nur gesichert in meiner Version klappt. Das ist zunächst auch okay, weil wir D2007 hier überall haben, und die Komponente nur für den hausinternen Gebrauch ist. Lange Rede, kurzer Pin: Die Hack-Klasse:
Delphi-Quellcode:
Dann waren InvalidateControl() und SetZOrderPosition() zu re-implementieren, und zwar so, dass es als Prozedur/Funktion geht, nicht Methode:
type
// Liegt in der selben Unit wie TKATBalken, wodurch dank Friend-Beziehung Zugriff auf private Felder geht // Feld-Layout ist identisch mit dem von TWinControl THackedControl = class(TControl) private FAlignLevel: Word; FBevelEdges: TBevelEdges; FBevelInner: TBevelCut; FBevelOuter: TBevelCut; FBevelKind: TBevelKind; FBevelWidth: TBevelWidth; FBorderWidth: TBorderWidth; FPadding: TPadding; FBrush: TBrush; FDefWndProc: Pointer; FDockClients: TList; FDockManager: IDockManager; FHandle: HWnd; FImeMode: TImeMode; FImeName: TImeName; FObjectInstance: Pointer; FParentWindow: HWnd; FTabList: TList; FControls: TList; FWinControls: TList; FTabOrder: Integer; FTabStop: Boolean; FCtl3D: Boolean; FShowing: Boolean; FUseDockManager: Boolean; FDockSite: Boolean; FParentCtl3D: Boolean; FOnDockDrop: TDockDropEvent; FOnDockOver: TDockOverEvent; FOnEnter: TNotifyEvent; FOnExit: TNotifyEvent; FOnGetSiteInfo: TGetSiteInfoEvent; FOnKeyDown: TKeyEvent; FOnKeyPress: TKeyPressEvent; FOnKeyUp: TKeyEvent; FOnUnDock: TUnDockEvent; FOnAlignInsertBefore: TAlignInsertBeforeEvent; FOnAlignPosition: TAlignPositionEvent; FMouseInClient: Boolean; FMouseControl: TControl; public function PaletteChanged(Foreground: Boolean): Boolean; override; end; implementation function THackedControl.PaletteChanged(Foreground: Boolean): Boolean; begin result := inherited PaletteChanged(Foreground); end;
Delphi-Quellcode:
Und dann meine Helfer-Methoden:
procedure InvalidateControl(IsVisible, IsOpaque: Boolean; self: TControl; aParent: THackedControl);
var Rect: TRect; function BackgroundClipped: Boolean; var R: TRect; List: TList; I: Integer; C: TControl; begin Result := True; List := aParent.FControls; I := List.IndexOf(Self); while I > 0 do begin Dec(I); C := List[I]; with C do if C.Visible and (csOpaque in ControlStyle) then begin IntersectRect(R, Rect, BoundsRect); if EqualRect(R, Rect) then Exit; end; end; Result := False; end; begin if (IsVisible or (csDesigning in self.ComponentState) and not (csNoDesignVisible in self.ControlStyle)) and (self.Parent <> nil) and self.Parent.HandleAllocated then begin Rect := self.BoundsRect; InvalidateRect(self.Parent.Handle, @Rect, not (IsOpaque or (csOpaque in self.Parent.ControlStyle) or BackgroundClipped)); end; end; procedure SetZOrderPosition(Position: Integer; self: TControl; aParent: THackedControl); var I, Count: Integer; ParentForm: TCustomForm; begin if aParent <> nil then begin I := aParent.FControls.IndexOf(Self); if I >= 0 then begin Count := aParent.FControls.Count; if Position < 0 then Position := 0; if Position >= Count then Position := Count - 1; if Position <> I then begin aParent.FControls.Delete(I); aParent.FControls.Insert(Position, Self); InvalidateControl(self.Visible, True, self, aParent); if not (csLoading in self.ComponentState) then begin ParentForm := ValidParentForm(Self); if csPalette in ParentForm.ControlState then THackedControl(ParentForm).PaletteChanged(True); end; end; end; end; end;
Delphi-Quellcode:
Mit "CorrectZOrders" werden dann genau die o.g. Bedingungen hergestellt. Vor allem aber wird nichts geändert wenn diese schon bestehen, so dass dies nicht mehr zu einer Endlosrekursion wird. Zwar finde ich das ganze im Paint noch immer deutlich zu oft, zumal dann ja zumindest immer noch die Prüfungen laufen, aber es scheint einfach keine bessere Stelle zu geben. Läuft prima, auch auf ziemlich gut gefüllten Forms!
function GetControlParentIndex(aControl: TControl): Integer;
var i: Integer; begin result := -1; if not Assigned(aControl.Parent) then Exit; for i := 0 to aControl.Parent.ControlCount-1 do begin if aControl.Parent.Controls[i] = aControl then begin result := i; Exit; end; end; end; function RectsOverlap(aRect1, aRect2: TRect): Boolean; begin result := not((aRect1.Right < aRect2.Left) or (aRect1.Left > aRect2.Right) or (aRect1.Bottom < aRect2.Top) or (aRect1.Top > aRect2.Bottom)); end; procedure TKATBalken.CorrectSubControlZOrder(aSubControl: TControl; aBaseIndex: Integer); var i, subIndex: Integer; begin subIndex := GetControlParentIndex(aSubControl); if subindex<0 then Exit; if subIndex<aBaseIndex then begin SetZOrderPosition(aBaseIndex+1, aSubControl, THackedControl(Parent)); end else begin for i := 0 to Parent.ControlCount-1 do begin if RectsOverlap(Parent.Controls[i].BoundsRect, aSubControl.BoundsRect) then begin if ((i>aBaseIndex) and (i<subIndex)) or ((i<aBaseIndex) and (i>subIndex)) then begin SetZOrderPosition(aBaseIndex+1, aSubControl, THackedControl(Parent)); Exit; end; end; end; end; end; procedure TKATBalken.CorrectZOrders; var i, selfIndex: Integer; begin selfIndex := GetControlParentIndex(self); CorrectSubControlZOrder(ValueLabel, selfIndex); CorrectSubControlZOrder(ScaleLeft, selfIndex); CorrectSubControlZOrder(ScaleRight, selfIndex); for i := 0 to High(ScaleLabelsLeft) do begin CorrectSubControlZOrder(ScaleLabelsLeft[i], selfIndex); CorrectSubControlZOrder(ScaleLabelsRight[i], selfIndex); end; end; procedure TKATBalken.Paint; begin inherited; CorrectZOrders; // Restlicher Code... Danke nochmals an alle :dp: |
AW: Event bei Z-Order Änderung o.ä.
ich will ja nicht unken, aber ich bin immer noch der Meinung dass es sinnvoller wäre alles in einer Komponente abzufackeln, Alignments, Ausrichtungen und Längen lassen sich ja auch berechnen.
|
AW: Event bei Z-Order Änderung o.ä.
Nicht, wenn die Breite der Skalentexte zur Runtime erst bekannt wird. Dann ist das Control längst platziert, und ich will im Code des eigentlichen Projektes keine Fetzen rumfliegen haben, die die Neuausrichtung machen müssen. Zudem ist es so auch noch etwas gefälliger im Umgang im Designer, da der ja doch recht große Freiraum zwischen den Skalentexten nicht mehr zum Balken gehört, und man die nicht mehr so oft aus Versehen erwischt, wenn man eigentlich ein anderes in der näheren Umgebung haben wollte. Für unsere Zwecke erscheint mir das zunächst als guter Weg, es sei denn da hat noch jemand konkrete Killer-Argumente die ich bisher nicht bedacht habe.
|
AW: Event bei Z-Order Änderung o.ä.
Ich mag TLabeledEdit nicht, weil es sich völlig anders verhält als andere Controls.
Es beansprucht mehr Platz als es die Abfrage (Width,Height) vermuten lässt. Align ist auch nicht sinnvoll möglich. Es ist also ein Konstrukt, was den Benutzer in einigen Punkten überrascht, und solche Ü-Eier gelten im Allgemeinen als schlechter Code. Einen Vorteil kann ich da auch nicht entdecken, nur Faulheit. Es geht nicht darum, dass eine Komponente TLabeledEdit unnütz ist, sondern darum, dass diese schlecht implementiert ist. Die Gründe dafür sind mir egal weil man sich alles schönreden kann. |
AW: Event bei Z-Order Änderung o.ä.
Nun, da das Teil nur für einen sehr speziellen Zweck eingesetzt wird (Prozessvisualisierung, ähnlich WinCC, nur dass wir quasi Formulare+Kompos dazu missgebrauchen), bei dem Alignment ohnehin bestenfalls zu einem unvorhersehbarem Verhalten führen würde, da so viele Einzelteile und Verbindungen zwischen diesen existieren. Praktisch nicht machbar, also auch kein Thema. Vorläufig bin ich auch der einzige, der damit arbeitet. Die Lage der Teile muss auch im Code niemals angefasst werden. Es mag nicht deinem Geschmack entsprechen, aber für Einsatzgebiet empfinde ich das als sehr praktisch. Diese Subjektivität ist für mich jetzt kein Killer-Argument :)
Warum ist Faulheit schlecht? Bzw. an welcher Stelle empfindest du das als faul? Wäre es dir dann lieber in ~10 Formularen mit je 6-30 solcher Balken alle Beschriftungen von Hand mit Labels zu machen, und diese dann auch noch von Hand auf Werte aus einer DB zu setzen UND noch korrekt zu positionieren da die Zahlenbreite sich ggf. ändert? Da es für alle Balken gleich abläuft, ist das so doch der Inbegriff von Modularität und funktional orientierter Kapselung. Ich kann leider echt nicht folgen. |
AW: Event bei Z-Order Änderung o.ä.
@Medium
könntest Du gegf. 2 Bilder(Screenshots) mit unterschiedlichen Extremen bei Deiner Komponente anhängen. Es fällt mir immer noch schwer mir vorzustellen warum Du das so gelöst hast. |
AW: Event bei Z-Order Änderung o.ä.
Liste der Anhänge anzeigen (Anzahl: 1)
Ich hab hier einfach mal ein Screenshot aus einem realen Projekt anonymisiert angehängt. Die Balken sind zwar alle auf 0, aber man sollte erkennen wo sie liegen. hier ist alles recht größzügig, aber es gibt auch Tanks die deutlich kleiner dargestellt werden, in denen noch diverse weitere Anzeigen sind, die teils zwischen die Beschriftungen gesetzt werden müssen damit alles passt. Wenn die Balken dann breiter anklickbar sind, als der eigentliche Balken ist, wirds ein elendes Rumgeschubse wenn man Anzeigen darunter ändern muss. (Glaub mir, ich kenn das ja schon ;)) Zudem werden dann auch ggf. Repaints ausgelöst, die gar nicht sein müssten. Bei der Menge an Komponenten vereinzelt ein wenn auch leicht merkbarer Gewinn. (Das Bild hier ist wirklich vergleichsweise aufgeräumt noch, die Anlage ist aber auch ein Neubau gewesen. Über die Jahre wird da vieeel geändert und zugefügt. Da tut Reserve gut :))
Erstellt sind die Bilder größtenteils aus meiner eigenen Komponentensammlung (Tanks, Leitungszüge, Ventile, etc.) die bereits für unsere DB Struktur geeignet vorbereitet sind, um sie möglichst effizient (und wartungsarm bei Änderung/Erweiterung) laufend mit neuen Daten zu versorgen. Zudem eine Hand voll Edits und Kleinkram. Einsatzort sind Steuerwarten in Industrieanlagen, und unsere Software ist immer eine maßgeschneiderte Individuallösung (jedoch immer auf ähnlicher Basis bzgl. DB, Kommunikation mit den Steuerungen, und Komponenten). Ist halt eben schon recht speziell, weswegen "End-User-Best-Practices" nicht unbedingt an allen Stellen greifen. Und da die Komponenten ohne das zugehörige garnicht so kleine Subsystem an Serverprogrammen und DB und Infrastruktur praktisch keinen Wert haben, ist es auch ziemlich unwahrscheinlich, dass die mal jemand fremdes in der Hand haben wird. Die Balken lassen sich übrigens mit Skalen links, recht, beidseitig oder ohne darstellen. Horizontal oder vertikal, und die Zahl in der mitte könnte auch links/rechts/oben/unten vom Balken stehen. Dort wo der Balken nicht ausgefüllt ist, ist die Kompo transparent, so dass ich sie hübsch auf beliebige Untergründe setzen kann. (Hier ausnahmsweise mit nem weissen Shape hinterlegt.) |
AW: Event bei Z-Order Änderung o.ä.
Im ersten Moment dachte ich das ganze bis auf die Wincontrol basierten Elemente sein eine Komponente, die sich aus DB-Infos zeichnet, aber nach dem bisherigen Kontext geht es die 3 Füllstandanzeiger?
Die bedenken bezüglich Anordnung und Klickverhalten teile ich weiterhin nicht, das sind IMHO ein paar sinnvoll zu entwerfende Properties, das Verhalten auf Klick/Mousedown lässt sich ja auf einen Fang beschränken. Aber ich will daraus jetzt auch keine Philosophie machen, wenn es so funktioniert wie es umgesetzt ist. Danke für den Einblick .... |
Alle Zeitangaben in WEZ +1. Es ist jetzt 17:40 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz