![]() |
Delphi-Version: XE
Anzeigen wenn sich in TObjectList ein Eintrag geändert hat
Hallo zusammen,
ich möchte in einer TObjectList erkennen, wenn sich ein Object in der Liste geändert hat. Dazu habe ich folgendes (vereinfachtes) Konstrukt angedacht:
Delphi-Quellcode:
Bei der Beschriftung der Eigenschaft Namen wird automatisch das Chanded-Property gesetzt
// ***************************************************************************************************************************************
// Die Basisliste mit dem Property Changed TBaseObjectList<T: class> = class(TObjectList<T>) private FChanged: Boolean; public constructor Create(AOwnsObjects: Boolean = True); overload; property Changed: TClass read FChanged write FChanged; end; // *************************************************************************************************************************************** // Die Basisklasse mit den zwei fixen Propteries TBase = class private FChanged: Boolean; FParentObject: TObject; procedure SetNr(Value: Integer); procedure SetChanged(const Value: Boolean); public property Changed: Boolean read FChanged write SetChanged; property ParentObject: TObject read FParentObject write FParentObject; constructor Create; destructor Destroy; override; end; // *************************************************************************************************************************************** // Als Beispiel eine auf ein eigenes Property abgespeckte Klasse TNamen = class(TBase) private FNamen: String; procedure SetNamen(const Value: String); public property Namen: String read FNamen write SetNamen; constructor Create; destructor Destroy; override; end; // *************************************************************************************************************************************** // Und die zu diesem Beispiel passende ObjectList TNamenList = TBaseObjectList<TNamen>;
Delphi-Quellcode:
Und jetzt soll über die Änderung des Changed-Property auch das Changed-Flag der entsprechenden Liste gesetzt werden. (Falls die Klasse einer Liste angehört), Dazu wurde dem Object bei der Erstellung die Liste als ParentObject mit gegeben
procedure TNamen.SetNamen(const Value: String);
begin if FNamen <> Value then begin FNamen := Value; Changed := True; end; end;
Delphi-Quellcode:
Das Einlesen der Daten in die Liste würde so aussehen
procedure TBase.SetChanged(const Value: Boolean);
begin if FChanged <> Value then begin FChanged := Value; if (FParentObject is TBaseObjectList) then // Prüfen, ob der Parent des Wertes eine TBaseObjectList ist Das geht so aber leider nicht TBaseObjectList(FParentObject).Changed := true; // Und wenn ja, die Liste auch auf Changed setzen end; end;
Delphi-Quellcode:
Jetzt stehen alle Werte und die Liste auf Changed = False
procedure TForm15.btCreateListClick(Sender: TObject);
var i:Integer; lNamen:TNamen; begin lNamenList := TNamenList.Create; for i:=0 to 5 do begin lNamen.Create; lNamenList.Add(lNamen); lNamen.ParentObject := lNamenList; lNamen.Namen := Format('Name%d', [i]); lNamen.Changed := False; end; lNamenList.Changed:=False; end; Und wenn ich so den Werte eines Eintrags ändere, steht dieser Wert auf Changed und es sollte auch die Liste auf Changed = True stehen
Delphi-Quellcode:
Aktuell gehe ich bei jedem Save-Vorgang durch die verschiedenen Listen, aber es wäre eben resourcenschonender wenn ich gleich an der Liste selber sehen würde, ob ein Wert geändert wurde.
procedure TForm15.btChangeNameClick(Sender: TObject);
begin lNamenList[3].Namen := 'Neuer Name'; // Jetzt ist lNamenList[3].Changed = True und jetzt soll auch lNamenList.Changed = True sein end; Gibte es eine Möglichkeit das zu realisiseren Vielen Dank Gerd |
AW: Anzeigen wenn sich in TObjectList ein Eintrag geändert hat
Zitat:
Delphi-Quellcode:
Also nur wenn der neue Wert True ist, auch Changed der Liste auf True setzen?
procedure TBase.SetChanged(const Value: Boolean);
begin if FChanged <> Value then begin FChanged := Value; if Value and <FParentObject is TBaseObjectList) then // <<< Änderung hier TBaseObjectList(FParentObject).Changed := true; end; end; Kann sein, dass es in Deinem Anwendungsfall keinen Unterschied macht, kommt mir aber falsch vor. Zitat:
Mir ist nicht ganz klar, was Du damit meinst: Zitat:
Das ginge z.B., indem man zusätzlich noch eine ChangedObjects Liste mit Pointern auf die geänderten Objekte pflegt. Dann braucht man beim Speichern nur die ChangedObjects Liste durchgehen, die Objekte speichern und ChangedObjects löschen. Folgendes ist dabei zu beachten:
Es ginge noch mit etwas geringerem Speicherverbrauch ohne eine ChangedObjects Liste, indem man einen Changed Counter führt und dann beim Abspeichern der Liste die Einträge nur so lange durch geht, bis man genau diese Anzahl Änderungen gefunden und gespeichert hat. Das spart im Durchschnitt etwas Zeit. Wenn man Pech hat, wurde aber der letzet Eintrag geändert und man muss alle Einträge durchgehen, bis man den geänderten gefunden hat. Das kann man nochmal etwas optimieren, indem man den kleinste und den größten Index der geänderten Objekte pflegt, allerdings ist das ziemlich kompliziert, falls auch Einträge hinzugefügt und gelöscht werden, denn dann muessen jeweils auch diese beiden Indexe angepasst werden. |
AW: Anzeigen wenn sich in TObjectList ein Eintrag geändert hat
Zitat:
Delphi-Quellcode:
nicht akzeptiert. Genauso wenig wie die Zuweisung
if (FParentObject is TBaseObjectList) then
Delphi-Quellcode:
. Und da suche ich nach einer Möglichkeit, wie ich auf die BasisListe per TypeCast zugreifen kann.
TBaseObjectList(FParentObject).Changed := true
Das mit dem Counter oder der zusätzlichen Liste sind Möglichkeiten, machen die Listenverwaltung aber nicht einfacher. In meiner Anwendung gibt es 61 TObjectList und von der ein oder anderen noch mehrere Instanzen. |
AW: Anzeigen wenn sich in TObjectList ein Eintrag geändert hat
Versuch's mal so:
Delphi-Quellcode:
if (FParentObject is TBaseObjectList<TBase>) then
TBaseObjectList<TBase>(FParentObject).Changed := true; |
AW: Anzeigen wenn sich in TObjectList ein Eintrag geändert hat
Konsequenterweise sollte man die Liste auch so deklarieren:
Delphi-Quellcode:
TBaseObjectList<T: TBase>
|
AW: Anzeigen wenn sich in TObjectList ein Eintrag geändert hat
Vielen Dank
Mit den Änderungen lässt sich das Testprojekt jetzt compilieren. Aber zwei Dinge treten noch auf
Ich bin nicht sicher, ob ich den Zusatz richtig verstanden habe Zitat:
Delphi-Quellcode:
TBaseObjectList<T: class> = class(TObjectList<T>)
TBaseObjectList<T: TBase> = class(TObjectList<T>) TBaseObjectList<TBase: class> = class(TObjectList<TBase>) |
AW: Anzeigen wenn sich in TObjectList ein Eintrag geändert hat
Problem ist, dass bei den Generics die Vererbung bissl "komisch" arbeitet und die Hierarchie nicht immer so aussieht, wie man denken könnte. :wall:
Aber ich hätte auch gedacht, dass es bei diesen 3 Varianten hätte gehen sollen, da du eigentlich auf den gleichen selben Typ prüfst und der ja gleich sein müsste. :gruebel: Wobei 1 und 3 das Selbe ist, von den Typen her, da sich nur der Bezeichner unterscheidet. (ob es A oder B heißt, macht keinen Unterschied) [EDIT] Das TBase in
Delphi-Quellcode:
war doch das TXyz aus
is ...<TXyz>
Delphi-Quellcode:
und nicht das "einzelne"
TBaseObjectList<TXyz: class> = ...
Delphi-Quellcode:
?
TBase = class
Aber bei
Delphi-Quellcode:
ist diese Prüfung auch nicht nötig, da bereits der Compiler die Prüfung übernimmt und du somit einfach "blind" hart casten kannst, weil es nichts Anderes sein kann.
TBaseObjectList<T: TBase> = class(...
|
AW: Anzeigen wenn sich in TObjectList ein Eintrag geändert hat
So sollte es auf der sicheren Seite sein:
Delphi-Quellcode:
Dann kannst du auch wieder auf TBaseObjectList prüfen und casten:
type
TBase = class; TBaseObjectList = class(TObjectList<TBase>) private FChanged: Boolean; public constructor Create(AOwnsObjects: Boolean = True); overload; property Changed: Boolean read FChanged write FChanged; end; TBaseObjectList<T: TBase> = class(TBaseObjectList);
Delphi-Quellcode:
procedure TBase.SetChanged(const Value: Boolean);
begin if FChanged <> Value then begin FChanged := Value; if (FParentObject is TBaseObjectList) then TBaseObjectList(FParentObject).Changed := True; end; end; |
AW: Anzeigen wenn sich in TObjectList ein Eintrag geändert hat
Liste der Anhänge anzeigen (Anzahl: 1)
Ich bin nicht sicher ob jetzt ein Teil meiner Anforderung verloren gegangen ist.
So wie TBase die Basisklasse für meine kommenden Objekte ist, sollte TBaseObjectList die Basisklasse für meine kommenden Listen sein Eine der kommenden Klasse incl. Liste könnte dann so aussehen
Delphi-Quellcode:
Wenn ich es so definiere
// ***************************************************************************************************************************************
// Als Beispiel eine auf ein eigenes Property abgespeckte Klasse TNamen = class(TBase) private FNamen: String; procedure SetNamen(const Value: String); public property Namen: String read FNamen write SetNamen; constructor Create; destructor Destroy; override; end; // *************************************************************************************************************************************** // Und die zu diesem Beispiel passen ObjectList TNamenList<T: TNamen> = class(TBaseObjectList); var lNamenList: TNamenList;
Delphi-Quellcode:
sagt Delphi bei Zugriff auf
TNamenList<T: TNamen> = class(TBaseObjectList)
Delphi-Quellcode:
dass er Namen nicht kennt
lNamenList[3].Namen
Wenn ich es so definiere
Delphi-Quellcode:
sagt Delphi bei Zugriff auf
TNamenList = TBaseObjectList<TNamen>;
Delphi-Quellcode:
dass TNamen und TBase incompatibel sind.
lNamen := lNamenList[3];
Wenn ich per Typecast
Delphi-Quellcode:
auf die Liste zugreife funktioniert die Änderung von lNamen.Namen und die Liste wird korrekt auf Changed gesetzt. Eigentlich wollte ich aber ohne TypeCast auskommen, zumal es viele Zugriffe gibt die direkt auf
lNamen := TNamen(lNamenList[3]);
Delphi-Quellcode:
zugreifen. Und beim Beenden des Programms kommt weiterhin der EAccessViolation.
lNamenList[3].Namen
Das hier verstehe ich nicht ganz
Delphi-Quellcode:
Soll es wirklich zwei mal TBaseObjectList heißen?
TBaseObjectList = class(TObjectList<TBase>)
private ... end; TBaseObjectList<T: TBase> = class(TBaseObjectList); Ich habe mal mein Testcode angehängt. Hängt vielleicht nur noch an einer Kleinigkeit |
AW: Anzeigen wenn sich in TObjectList ein Eintrag geändert hat
Ja, stimmt. Der generische Part der TObjectList<T> geht damit verloren. Wenn du das nicht nachbilden und trotzdem bei dem Ansatz mit dem ParentObject bleiben willst, kannst du entweder den HardCast auf TObjectList<TBase> vom Anfang machen oder über über ein OnChanged-Event oder ein Interface arbeiten.
Eine ganz andere Alternative wäre auch ein Message-Mechanismus:
Delphi-Quellcode:
const
cNotifyChanged = 1; type TBase = class; TBaseObjectList<T: TBase> = class(TObjectList<T>) private FChanged: Boolean; protected procedure NotifyChanged(var Message: TDispatchMessage); message cNotifyChanged; public property Changed: Boolean read FChanged write FChanged; end; ... procedure TBase.SetChanged(const Value: Boolean); var msg: TDispatchMessage; begin if FChanged <> Value then begin FChanged := Value; msg.MsgID := cNotifyChanged; FParentObject.Dispatch(msg); end; end; ... procedure TBaseObjectList<T>.NotifyChanged(var Message: TDispatchMessage); begin Changed := True; end; |
Alle Zeitangaben in WEZ +1. Es ist jetzt 15:47 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