Bei einer Komponente ein Panel zwischenschalten
Hallo.
Ich möchte eine Komponente ableiten, sie jedoch bezüglich des Parents in ein Panel zeichnen. Das eigentliche Objekt soll also in einem Panel liegen, das zwischengeschaltet ist. Eine Wrapper-Klasse möchte ich nicht verwenden, da alle Dinge, die ich benötige direkt abgeleitet werden sollen. Ansonsten würde z.B. die Schreibarbeit zum Einen ins unendliche gehen, zum Anderen gäbe es dann keine IS-A-Beziehung mehr. Ich habe folgenden Code geschrieben, jedoch erhalte ich im Create-Teil stets eine AV. Was ist da falsch?
Delphi-Quellcode:
Gruß
type
TPaneledImage = class(TImage) private FPanel: TPanel; FParent: TWinControl; FVisible: boolean; procedure SetVisible(Value: boolean); protected procedure SetParent(AParent: TWinControl); override; public constructor Create(AOwner: TComponent); override; destructor Destroy; override; property Parent: TWinControl read FParent write SetParent; procedure SetBounds(ALeft, ATop, AWidth, AHeight: Integer); override; property Visible: boolean read FVisible write SetVisible; end; constructor TPaneledImage.Create(AOwner: TComponent); begin FPanel := TPanel.Create(AOwner); inherited Create(FPanel); // Eigenschaften für das Image im Panel festlegen Parent := FPanel; Align := alClient; Visible := true; end; destructor TPaneledImage.Destroy; begin FPanel.Free; // OK? inherited; end; procedure TPaneledImage.SetBounds(ALeft, ATop, AWidth, AHeight: Integer); begin // Das Image im Panel soll bei (0, 0) bleiben. Nur das Panel bewegt sich. inherited SetBounds(0, 0, AWidth, AHeight); FParent.Left := ALeft; FParent.Top := ATop; FParent.Width := AWidth; FParent.Height := AHeight; end; procedure TPaneledImage.SetParent(AParent: TWinControl); begin if FParent <> AParent then begin FParent := AParent; FPanel.Parent := AParent; end; inherited SetParent(FPanel); // inherited Parent := FPanel; end; procedure TPaneledImage.SetVisible(Value: boolean); begin if FVisible <> Value then begin FVisible := Value; FPanel.Visible := Value; end; end; blackdrake |
Re: Bei einer Komponente ein Panel zwischenschalten
In welcher Zeile knallt es denn genau? Ich rate mal und sage, beim inherited, oder?
|
Re: Bei einer Komponente ein Panel zwischenschalten
Die Exception findet nach dem Create; statt. Es ist also keine exakte Zeile vom Compiler markiert. Auch die Exception-Meldungen sind sehr vielfälig (am Anfang kommt zum Beispiel eine externe Exception, danach die AV und danach der Windows-Programmabsturz).
|
Re: Bei einer Komponente ein Panel zwischenschalten
hi,
also wie ich das sehe hat deddyh recht (einfach mal den source genommen und in eine leere anwendung gepackt) Die exception (zumindest die erste) findet im inherited create statt. Hier wird nämlich setbounds ausgelöst, in welchem du das noch nicht gesetzte FParent nutzen willst. Ich weis auch gar nicht, wie man das sinnvoll lösen soll. Wenn du das Panel nachher freigeben willst, knallts ja auch, weil das panel ja auch das PaneledImage freigeben will (innerhalb des destroys von selbigem ;) ) Aber vielleicht gibt es da eine sinnvolle Lösung Gruß angos |
Re: Bei einer Komponente ein Panel zwischenschalten
Hallo.
Um eine sinnvolle Lösung wäre ich sehr dankbar, denn ich möchte wirklich gerne von TImage vererben und lediglich ein Panel dazwischenschalten, damit ich ein WinControl-Handle zum Zeichnen bekomme. Das mit der Exception verstehe ich nicht ganz:
Delphi-Quellcode:
Bei ** ist ja das FPanel bereits mittels Create() erstellt worden. Wieso soll dann ** fehlschlagen? Und welches SetBounds (von TPaneledImage oder TPanel) wird warum aufgerufen? Ich kann den Gedankengang noch nicht ganz nachvollziehen, wieso es fehlschlägt.
constructor TPaneledImage.Create(AOwner: TComponent);
begin FPanel := TPanel.Create(AOwner); inherited Create(FPanel); // ** // Eigenschaften für das Image im Panel festlegen Parent := FPanel; Align := alClient; Visible := true; end; Beim Destroy (hat aber nichts mit dem Create-Fehler zu tun) könnte es zum Problem kommen, wenn das TPanel freigegeben wird, da es das TPaneledImage own'ed und somit mit freigibt, bevor der Rest des Destructors von TPaneledImage aufgerufen wird. Gruß blackdrake |
Re: Bei einer Komponente ein Panel zwischenschalten
Hat denn keiner eine Lösung für das Problem?
|
Re: Bei einer Komponente ein Panel zwischenschalten
Naja, der genannte Fehler an sich sollte sich ja leicht beheben lassen indem du prüfst, ob FParent nil ist:
Delphi-Quellcode:
Allerdings sehe ich nicht so recht wie das ansonsten klappen könnte die Objekthierarchie im Konstruktor so hinzubiegen wie du dir das vorstellst. Was dabei passiert müsste ich einmal testen, aber ich sehe da andere Probleme.
if FParent <> nil then
begin FParent.Left := ALeft; FParent.Top := ATop; FParent.Width := AWidth; FParent.Height := AHeight; end; // EDIT:
|
Re: Bei einer Komponente ein Panel zwischenschalten
Hallo.
Ich habe den Code nochmal ganz genau analysiert, Diagramme gemacht (Stichwort Hirnknoten) und einiges verbessert. Nun klappt es ohne AV beim Create. Im Destroy habe ich aber aufgrund der Owner Probleme. Selbst wenn das Destroy funktioniert, kommt die AV am Programmende. Ich habe verschiedene Varianten durchprobiert und im Quellcode kurz kommentiert. Könnt ihr mir bitte weiterhelfen?
Delphi-Quellcode:
Create-Versuch {1}
type
TPaneledImage = class(TImage) private FPanel: TPanel; procedure SetVisible(Value: boolean); function GetParent: TWinControl; function GetOwner: TComponent; reintroduce; function GetVisible: boolean; protected procedure SetParent(AParent: TWinControl); override; public constructor Create(AOwner: TComponent); override; destructor Destroy; override; procedure SetBounds(ALeft, ATop, AWidth, AHeight: Integer); override; property Parent: TWinControl read GetParent write SetParent; property Visible: boolean read GetVisible write SetVisible; property Owner: TComponent read GetOwner; end; constructor TPaneledImage.Create(AOwner: TComponent); begin // Host-Panel erstellen FPanel := TPanel.Create(AOwner); // Eigenschaften für das Panel FPanel.BevelOuter := bvNone; // Das eigentliche Image erstellen inherited Create(FPanel); {1} // inherited Create(AOwner); {2} // inherited Create(Self); {3} // Unveränderliche Eigenschaften für das Image im Panel festlegen inherited SetParent(FPanel); Align := alClient; Visible := true; end; destructor TPaneledImage.Destroy; begin // Zuerst das Panel freigeben. Das Panel wird zuerst Nil gesetzt, dann freigegeben. // Das Panel wird widerum uns zuerst freigeben, weswegen dieses Destroy ein zweites // Mal aufgerufen wird und die korrekte Freigabe von TObject einleitet. if Assigned(FPanel) then begin FreeAndNil(FPanel) // Diese Zeile wird 1x zuerst aufgerufen end else begin // Diese Zeile wird 1x danach aufgerufen inherited; // Hier wird zusätzlich einmal TPaneledImage.SetParent aufgerufen end; // Aber NACH Destroy gibts eine AV! end; function TPaneledImage.GetOwner: TComponent; begin result := FPanel.Owner; end; function TPaneledImage.GetParent: TWinControl; begin result := FPanel.Parent; end; function TPaneledImage.GetVisible: boolean; begin result := FPanel.Visible; end; procedure TPaneledImage.SetBounds(ALeft, ATop, AWidth, AHeight: Integer); begin // Das Image im Panel soll bei (0, 0) bleiben. Nur das Panel bewegt sich. inherited SetBounds(0, 0, AWidth, AHeight); FPanel.Left := ALeft; FPanel.Top := ATop; FPanel.Width := AWidth; FPanel.Height := AHeight; end; procedure TPaneledImage.SetParent(AParent: TWinControl); begin if not Assigned(FPanel) then exit; // Verhindern einer AV bei Destroy if FPanel.Parent <> AParent then begin FPanel.Parent := AParent; end; end; procedure TPaneledImage.SetVisible(Value: boolean); begin if FPanel.Visible <> Value then begin FPanel.Visible := Value; end; end; procedure TForm1.Button1Click(Sender: TObject); var x: TPaneledImage; begin x := TPaneledImage.Create(Self); try x.Parent := Self; x.AutoSize := true; x.Picture.LoadFromFile('C:\WINDOWS\Präriewind.BMP'); finally x.Free; // Verursacht eine AV, wenn Programm später beendet wird end; end; Self gehört Self.Panel Self.Panel gehört Self.Owner Create-Versuch {2} Self gehört Self.Owner Self.Panel gehört Self.Owner Create-Versuch {3} Self gehört Self (hä? :drunken: ) Self.Panel gehört Self.Owner Weitere Idee 4 Self gehört Self.Owner Self.Panel gehört Self Weitere Idee 5 Self gehört Self.Panel Self.Panel gehört Self Gruß blackdrake |
Re: Bei einer Komponente ein Panel zwischenschalten
Wenn du den Owner des Panels auf den übergebeben setzt und den deines Images auf das Panel, dann brauchst du dich um die Freigabe nicht mehr zu kümmern.
Delphi-Quellcode:
Das Parent brauchst du mit diesem SetParent nicht in Create setzen, denn das passiert nach der Erzeugung ohnehin erneut. Deshalb bringt das dort rein gar nix.
constructor TPaneledImage.Create(AOwner: TComponent);
begin FPanel := TPanel.Create(AOwner); inherited Create(FPanel); // Eigenschaften für das Image im Panel festlegen Align := alClient; end; procedure TPaneledImage.SetParent(AParent: TWinControl); begin FPanel.Parent := AParent; if csDestroying in ComponentState then inherited SetParent(AParent) else inherited SetParent(FPanel); end; Den Destruktor brauchst du so wie hier geschrieben gar nicht schreiben. Bei dem SetBounds machst du einen Denkfehler, wenn Left usw. einzeln gesetzt werden, schau dir dann die Werte einmal an. |
Re: Bei einer Komponente ein Panel zwischenschalten
Liste der Anhänge anzeigen (Anzahl: 1)
Hallo.
Vielen Dank für deine Antwort. Es funktioniert leider immer noch nicht. Jetzt bekomme ich einen Stacküberlauf. Ich hatte gestern sehr lange daran gearbeitet und etliche Workarounds beim SetBounds() durchgeführt, sodass das mit den Algignments, Stretch, AutoSize etc alles einigermaßen funktioniert (aber es scheint noch Ausnahmen zu geben). Beispielsweise musste ich in SetBounds() Width und Height verändern, damit AutoSize funktioniert. Jedoch durfte ich Left und Top nicht dort verändern, da sonst ein Image wieder bei (0, 0) war, wenn ich den Parent geändert habe. Das ganze ist extrem kompliziert... Diesbezüglich bin ich für Verbesserungsvorschläge dankbar! Ich habe mal das komplette Testprogramm + aktuellem Codestand gepostet.
Delphi-Quellcode:
Gruß
type
TPaneledImage = class(TImage) private function GetVisible: boolean; procedure SetVisible(const Value: boolean); function GetParent: TWinControl; procedure SetLeft(const Value: Integer); procedure SetTop(const Value: Integer); function GetLeft: Integer; function GetTop: Integer; function GetHeight: Integer; function GetWidth: Integer; procedure SetHeight(Value: Integer); procedure SetWidth(Value: Integer); function GetAlign: TAlign; procedure SetAlign(Value: TAlign); protected Panel: TPanel; procedure SetParent(AParent: TWinControl); override; public // Color... // Handle constructor Create(AOwner: TComponent); override; destructor Destroy; override; procedure SetBounds(ALeft: Integer; ATop: Integer; AWidth: Integer; AHeight: Integer); override; property Visible: boolean read GetVisible write SetVisible; property Parent: TWinControl read GetParent write SetParent; property Left: Integer read GetLeft write SetLeft; property Top: Integer read GetTop write SetTop; property Width: Integer read GetWidth write SetWidth; property Height: Integer read GetHeight write SetHeight; property Align: TAlign read GetAlign write SetAlign; end; { TPaneledImage } constructor TPaneledImage.Create(AOwner: TComponent); begin (* inherited Create(AOwner); Panel := TPanel.Create(Self); // Eigenschaften für Panel Panel.BevelOuter := bvNone; Panel.Color := clRed; // Eigenschaften für Image inherited Align := alClient; inherited SetParent(Panel); *) Panel := TPanel.Create(Self); inherited Create(Panel); // Eigenschaften für Panel Panel.BevelOuter := bvNone; Panel.Color := clRed; // Eigenschaften für Image (* inherited *) Align := alClient; end; destructor TPaneledImage.Destroy; begin (* if Assigned(Panel) then begin inherited SetParent(Panel.Parent); FreeAndNil(Panel); end; *) inherited; end; function TPaneledImage.GetVisible: boolean; begin result := Panel.Visible; end; procedure TPaneledImage.SetVisible(const Value: boolean); begin if Panel.Visible <> Value then Panel.Visible := Value; end; procedure TPaneledImage.SetBounds(ALeft: Integer; ATop: Integer; AWidth: Integer; AHeight: Integer); begin // Das Bild im Panel ist immer bei (0, 0) // inherited SetBounds(0, 0, AWidth, AHeight); inherited SetBounds(ALeft, ATop, AWidth, AHeight); if Assigned(Panel) then begin // Panel.Left := ALeft; // Panel.Top := ATop; Panel.Width := AWidth; Panel.Height := AHeight; end; end; function TPaneledImage.GetParent: TWinControl; begin result := Panel.Parent; end; procedure TPaneledImage.SetParent(AParent: TWinControl); begin // TEST (* if not Assigned(Panel) then exit; if Panel.Parent <> AParent then Panel.Parent := AParent; *) Panel.Parent := AParent; if csDestroying in ComponentState then inherited SetParent(AParent) else inherited SetParent(Panel); end; procedure TPaneledImage.SetLeft(const Value: Integer); begin inherited Left := 0; if Panel.Left <> Value then Panel.Left := Value; end; procedure TPaneledImage.SetTop(const Value: Integer); begin inherited Top := 0; if Panel.Top <> Value then Panel.Top := Value; end; procedure TPaneledImage.SetHeight(Value: Integer); begin // *** Es könnte passieren, dass wir aufgrund von AutoSize die größe nicht verändenr dürfen! inherited Align := alNone; inherited Height := Value; Value := inherited Height; inherited Align := alClient; if Panel.Height <> Value then Panel.Height := Value; end; procedure TPaneledImage.SetWidth(Value: Integer); begin // *** Es könnte passieren, dass wir aufgrund von AutoSize die größe nicht verändenr dürfen! inherited Align := alNone; inherited Width := Value; Value := inherited Width; inherited Align := alClient; if Panel.Width <> Value then Panel.Width := Value; end; procedure TPaneledImage.SetAlign(Value: TAlign); begin if Panel.Align <> Value then begin Panel.Align := Value; if (Value = alNone) and AutoSize then begin Width := Picture.Width; Height := Picture.Height; end; end; end; function TPaneledImage.GetLeft: Integer; begin result := Panel.Left; end; function TPaneledImage.GetTop: Integer; begin result := Panel.Top; end; function TPaneledImage.GetHeight: Integer; begin result := Panel.Height; end; function TPaneledImage.GetWidth: Integer; begin result := Panel.Width; end; function TPaneledImage.GetAlign: TAlign; begin result := Panel.Align; end; blackdrake |
Alle Zeitangaben in WEZ +1. Es ist jetzt 00:34 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