Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Ein Object in TObjectList<T> löschen mit Ereignis (https://www.delphipraxis.net/171341-ein-object-tobjectlist-t-loeschen-mit-ereignis.html)

RWarnecke 1. Nov 2012 14:33

Delphi-Version: XE2

Ein Object in TObjectList<T> löschen mit Ereignis
 
Hallo zusammen,

ich habe folgende Klassen erstellt :
Delphi-Quellcode:
type
  TModifyActionList = (lnNone, lnAdded, lnChange, lnDelete);

  TDegree = class
  private
    FOnChange     : TNotifyEvent;
    FOnDelete     : TNotifyEvent;
    FModifyAction : TModifyActionList;
    FID           : Integer;
    FDegree       : string;
    procedure SetID(const Value: Integer);
    procedure SetDegree(const Value: string);
  public
    property ID           : Integer          read FID           write SetID;
    property Degree       : string           read FDegree       write SetDegree;
    property ModifyAction : TModifyActionList read FModifyAction write FModifyAction;
    property OnChange     : TNotifyEvent     read FOnChange     write FOnChange;
    property OnDelete     : TNotifyEvent     read FOnDelete     write FOnDelete;
  end;

  TCustomObjectList<T: class> = class(TObjectList<T>)
  private
    FOnChange    : TNotifyEvent;
    FOnDelete    : TNotifyEvent;
    FModifyAction : TModifyActionList;
    FNewRecord   : Boolean;
    procedure DeleteRecord(Klasse: T);
  protected
    function getItem(Index: Integer): T; virtual;
    procedure setItem(Index: Integer; Objekt: T); virtual;
    procedure Notify(const Item: T; Action: TCollectionNotification); override;
  public
    function Add(Objekt: T): Integer; virtual;
    function NewRecord(Objekt: T): Integer; virtual;
    function Remove(Objekt: T): Integer; virtual;
    function IndexOf(Objekt: T): Integer; virtual;
    procedure Insert(Index: Integer; Objekt: T); virtual;
    property Items[index: Integer]: T        read getItem      write setItem; default;
    property ModifyAction : TModifyActionList read FModifyAction write FModifyAction;
    property OnChange    : TNotifyEvent     read FOnChange    write FOnChange;
    property OnDelete    : TNotifyEvent     read FOnDelete    write FOnDelete;
  end;

{...}

{ TDegree }

procedure TDegree.SetDegree(const Value: string);
begin
  FDegree := Value;
  if (Assigned(FOnChange)) and (FModifyAction = lnChange) then
    FOnChange(Self);
end;

procedure TDegree.SetID(const Value: Integer);
begin
  FID := Value;
  if (Assigned(FOnChange)) and (FModifyAction = lnChange) then
    FOnChange(Self);
end;

{ TCustomObjectList<T> }

function TCustomObjectList<T>.Add(Objekt: T): Integer;
begin
  FNewRecord := False;
  Result := inherited Add(Objekt);
end;

procedure TCustomObjectList<T>.DeleteRecord(Klasse: T);
begin
  // To do something
end;

function TCustomObjectList<T>.getItem(Index: Integer): T;
begin
  Result := T(inherited Items[Index]);
end;

function TCustomObjectList<T>.IndexOf(Objekt: T): Integer;
begin
  Result := inherited IndexOf(Objekt);
end;

procedure TCustomObjectList<T>.Insert(Index: Integer; Objekt: T);
begin
  inherited Insert(Index, Objekt);
end;

function TCustomObjectList<T>.NewRecord(Objekt: T): Integer;
begin
  FNewRecord := True;
  Result := inherited Add(Objekt);
end;

procedure TCustomObjectList<T>.Notify(const Item: T;
  Action: TCollectionNotification);
begin
  if (Assigned(FOnChange)) and (FNewRecord) and (Action <> cnRemoved) then
  begin
    FNewRecord := False;
    FOnChange(Self);
  end;
  if Action = cnRemoved then
  begin
    FOnDelete(Item);
  end;
  inherited Notify(Item, Action);
end;

function TCustomObjectList<T>.Remove(Objekt: T): Integer;
begin
  Result := inherited Remove(Objekt);
end;

procedure TCustomObjectList<T>.setItem(Index: Integer; Objekt: T);
begin
  inherited Items[Index] := Objekt;
end;
Das funktioniert auch alles soweit ganz gut, bis auf den Teil mit FOnDelete. Warum bekomme ich jedesmal bei der Zeile
Delphi-Quellcode:
FOnDelete(Item);
eine AccessViolation ? Wie kann ich das beheben und was habe ich falsch gemacht ?

Sir Rufo 1. Nov 2012 14:38

AW: Ein Object in TObjectList<T> löschen mit Ereignis
 
Prüf doch mal mit
Delphi-Quellcode:
if Assigned( FOnDelete ) then
ab, ob da was zugewiesen ist ;)

RWarnecke 1. Nov 2012 14:56

AW: Ein Object in TObjectList<T> löschen mit Ereignis
 
Habe ich das aus der OH für Assigned richtig verstanden, wenn ich folgende Zeilen Code chreibe :
Delphi-Quellcode:
  Degree := TDegree.Create;
  Degree.OnChange := TableEntryChange;
  Degree.OnDelete := TableEntryDelete;
  Degree.ID := FieldByName('ID').AsInteger;
  Degree.Degree := FieldByName('Degree').AsString;
  DegreeList.Add(Degree);
Dann gibt mir
Delphi-Quellcode:
Assigned(FOnDelete)
innerhalb der Klasse zurück, ob die Zeile
Delphi-Quellcode:
Degree.OnDelete := TableEntryDelete;
ausgeführt wurde oder nicht ? Wenn ja, dann ist es leider nicht der Fall.
Delphi-Quellcode:
Assigned(FOnDelete)
gibt immer False zurück. Warum ?

Der obere Code-Iteil wird in einer Schleife mehrmals wiederholt.

DeddyH 1. Nov 2012 15:03

AW: Ein Object in TObjectList<T> löschen mit Ereignis
 
Eigentlich sollte Assigned nach der Methodenzuweisung true zurückliefern. Bau doch mal temporär einen Setter ein, setz dort einen Haltepunkt und schau, ob der angesprungen wird. Ich sehe in dem Code keinen Grund, wieso das nicht so sein sollte.

RWarnecke 1. Nov 2012 15:23

AW: Ein Object in TObjectList<T> löschen mit Ereignis
 
Bei den Klassen geht er in den Setter und weißt auch den richtigen Wert zu. Kann es vielleicht daran liegen, dass erst der Delete-Befehl ausgeführt wird und er dann ins Notify springt von TCustomObjectList<T: class> ?

DeddyH 1. Nov 2012 15:32

AW: Ein Object in TObjectList<T> löschen mit Ereignis
 
Ich habe es gerade nicht im Kopf, aber möglicherweise ist das Item zwischen dem Delete und dem Notify bereits freigegeben, das würde die AV erklären.

RWarnecke 1. Nov 2012 16:12

AW: Ein Object in TObjectList<T> löschen mit Ereignis
 
Liste der Anhänge anzeigen (Anzahl: 1)
Ich habe es jetzt mal getestet. Wenn ich bei der Zeile
Delphi-Quellcode:
if Action = cnRemoved then
einen Breakpoint setze, steht der Count von der ObjectList um einen reduziert. Das heißt für mich, dass erst das Objekt gelöscht wird und dann Notify ausgeführt wird. Um das Ergebnis noch zu verifizieren, habe ich die Procedure Delete mit in meine abgeleitet Klasse übernommen. Es ist wirklich so, das erst die Delete Procedure ausgeführt wird und dann das Notify. Ich habe jetzt mal zusätzlich in der Delete-Procedure eine Abfrage auf Assigned(FOnDelete) gemacht. Auch hier wird ein False ausgegeben, wie kann das sein ?

Meine TCustomObjectList<T: class> sieht jetzt so aus :
Delphi-Quellcode:
  TCustomObjectList<T: class> = class(TObjectList<T>)
  private
    FOnChange    : TNotifyEvent;
    FOnDelete    : TNotifyEvent;
    FModifyAction : TModifyActionList;
    FNewRecord   : Boolean;
    procedure SetOnDelete(const Value: TNotifyEvent);
    procedure SetOnChange(const Value: TNotifyEvent);
  protected
    function getItem(Index: Integer): T; virtual;
    procedure setItem(Index: Integer; Objekt: T); virtual;
    procedure Notify(const Item: T; Action: TCollectionNotification); override;
  public
    function Add(Objekt: T): Integer; virtual;
    procedure Delete(Index: Integer); virtual;
    function NewRecord(Objekt: T): Integer; virtual;
    function Remove(Objekt: T): Integer; virtual;
    function IndexOf(Objekt: T): Integer; virtual;
    procedure Insert(Index: Integer; Objekt: T); virtual;
    property Items[index: Integer]: T        read getItem      write setItem; default;
    property ModifyAction : TModifyActionList read FModifyAction write FModifyAction;
    property OnChange    : TNotifyEvent     read FOnChange    write SetOnChange;
    property OnDelete    : TNotifyEvent     read FOnDelete    write SetOnDelete;
  end;

{...}

{ TCustomObjectList<T> }

function TCustomObjectList<T>.Add(Objekt: T): Integer;
begin
  FNewRecord := False;
  Result := inherited Add(Objekt);
end;

procedure TCustomObjectList<T>.Delete(Index: Integer);
begin
  if Assigned(FOnDelete) then
    FOnDelete(Self);
  inherited Delete(Index);
end;

function TCustomObjectList<T>.getItem(Index: Integer): T;
begin
  Result := T(inherited Items[Index]);
end;

function TCustomObjectList<T>.IndexOf(Objekt: T): Integer;
begin
  Result := inherited IndexOf(Objekt);
end;

procedure TCustomObjectList<T>.Insert(Index: Integer; Objekt: T);
begin
  inherited Insert(Index, Objekt);
end;

function TCustomObjectList<T>.NewRecord(Objekt: T): Integer;
begin
  FNewRecord := True;
  Result := inherited Add(Objekt);
end;

procedure TCustomObjectList<T>.Notify(const Item: T;
  Action: TCollectionNotification);
begin
  if (Assigned(FOnChange)) and (FNewRecord) and (Action <> cnRemoved) then
  begin
    FNewRecord := False;
    FOnChange(Self);
  end;
  inherited Notify(Item, Action);
end;

function TCustomObjectList<T>.Remove(Objekt: T): Integer;
begin
  Result := inherited Remove(Objekt);
end;

procedure TCustomObjectList<T>.setItem(Index: Integer; Objekt: T);
begin
  inherited Items[Index] := Objekt;
end;

procedure TCustomObjectList<T>.SetOnChange(const Value: TNotifyEvent);
begin
  FOnChange := Value;
end;

procedure TCustomObjectList<T>.SetOnDelete(const Value: TNotifyEvent);
begin
  FOnDelete := Value;
end;
Bei
Delphi-Quellcode:
if Assigned(FOnDelete) then
steht der Count von TCustomObjectList<TDegree> noch auf der vollen Anzahl Objekte. Entweder verstehe ich hier etwas nicht oder es läuft irgendwas schief.

Edit:
Im Anhang meine DegreeList, nach dem Befüllen mit Daten. So wie ich das sehe, ist doch alles korrekt oder ?

himitsu 1. Nov 2012 16:42

AW: Ein Object in TObjectList<T> löschen mit Ereignis
 
Zitat:

eine AccessViolation
Und was für eine?

Was sagt denn der debugger, wenn du dir kurz vor dem aufruf den Wert dieser Variable anzeigen läßt?

RWarnecke 1. Nov 2012 16:55

AW: Ein Object in TObjectList<T> löschen mit Ereignis
 
Zitat:

Zitat von himitsu (Beitrag 1189378)
Und was für eine?

---------------------------
Debugger Exception Notification
---------------------------
Project Project14.exe raised exception class $C0000005 with message 'access violation at 0x00000000: read of address 0x00000000'.
---------------------------
Break Continue Help
---------------------------
Zitat:

Zitat von himitsu (Beitrag 1189378)
Was sagt denn der debugger, wenn du dir kurz vor dem aufruf den Wert dieser Variable anzeigen läßt?

Welchen Wert meinst Du ?

himitsu 1. Nov 2012 17:17

AW: Ein Object in TObjectList<T> löschen mit Ereignis
 
Delphi-Quellcode:
  if (Assigned(FOnChange)) and (FNewRecord) and (Action <> cnRemoved) then // hier hast'e geprüft
  begin
    FNewRecord := False;
    FOnChange(Self);
  end;
  if Action = cnRemoved then // hier nicht
  begin
    FOnDelete(Item); // hier haltepunkt hin und schauen was in FOnDelete steht, *1
  end;
1) aber vemutlich wirklich ein "nil".
Jedenfalls sieht die Exception danach aus, da dort zur Adresse 0 (nil) gesprungen wurde und weil dort kein ausführbarer Code steht, knallt es.
(aber natürlich kann es auch noch innerhalb einer verlinkten Methode knallen, aber da sollte der Debugger ebenfalls hilfreich sein)

Der schon genannte Trick mit dem Setter würde zmindestens zeigen, ob, wann und wo dieses Property verändert wird.

RWarnecke 1. Nov 2012 17:29

AW: Ein Object in TObjectList<T> löschen mit Ereignis
 
Im Beitrag 7, siehst Du im Anhang, wie der Setter von FOnDelete arbeitet. Ebenfalls im gleichen Beitrag hatte ich geschrieben, dass ein Assigned(FOnDelete) immer False ergibt.

Ich habe noch ein bisschen weiter getestet. Wenn ich die TCustomObjectList<T: class> für die Klasse TDegree folgendermaßen erstelle :
Delphi-Quellcode:
  DegreeList := TCustomObjectList<TDegree>.Create;
  DegreeList.OnDelete := TableEntryDelete;
Funktioneren beide Quelltexte aus den Beiträgen 1 und 7 und die Funktion für FOnDelete wird ausgeführt. Lasse ich diese Zeile
Delphi-Quellcode:
DegreeList.OnDelete := TableEntryDelete;
weg, dann wird FOnDelete nichtmehr ausgeführt oder es wird die Access Violation angezeigt, wenn die Abfrage auf Assigned(FOnDelete) fehlt. Deshalb gehe ich davon aus, das das FOnDelete im Notify auf das FOnDelete in der TCustomObjectList<T: class> zeigt.

Ich stelle mir jetzt nur die Frage, warum FOnChange so funktioniert und FOnDelete nicht ?

stahli 1. Nov 2012 18:00

AW: Ein Object in TObjectList<T> löschen mit Ereignis
 
Hat sich Dein Compiler irgendwie verschluckt?
Hast Du einen Compilerpoint (möglicher Breakpoint) in der Zeile?
Hast Du mal Projekt bereinigen/bneu erzeugen versucht?

RWarnecke 1. Nov 2012 18:09

AW: Ein Object in TObjectList<T> löschen mit Ereignis
 
Zitat:

Zitat von stahli (Beitrag 1189394)
Hat sich Dein Compiler irgendwie verschluckt?

Weiß ich nicht.
Zitat:

Zitat von stahli (Beitrag 1189394)
Hast Du einen Compilerpoint (möglicher Breakpoint) in der Zeile?

Wie meinst Du das ?
Zitat:

Zitat von stahli (Beitrag 1189394)
Hast Du mal Projekt bereinigen/bneu erzeugen versucht?

Ich habe bis jetzt immer vor jedem Compilieren die DCU-Dateien und die EXE gelöscht.

stahli 1. Nov 2012 18:31

AW: Ein Object in TObjectList<T> löschen mit Ereignis
 
Ich meinte den kleinen blauen Punkt vor jeder Befehlszeile (auf den man zum Setzen eines Breakpoints klickt).
Gelegentlich fehlten die bei mir schon mal, wenn der Compiler mit meinen Units nicht zurecht kam. :stupid:

Es gibt noch die Projektoptionen Bereinigen, Erzeugen und ggf. Installieren (bei Packages).
Vielleicht hilft davon ja etwas.

RWarnecke 1. Nov 2012 18:39

AW: Ein Object in TObjectList<T> löschen mit Ereignis
 
Zitat:

Zitat von stahli (Beitrag 1189400)
Ich meinte den kleinen blauen Punkt vor jeder Befehlszeile (auf den man zum Setzen eines Breakpoints klickt).
Gelegentlich fehlten die bei mir schon mal, wenn der Compiler mit meinen Units nicht zurecht kam. :stupid:

Ich habe die Klassen in einer ganz normalen Unit. Wenn ich STRG+F9 drücke, erscheinen in dieser Unit keine blauen Punkte. Ist das schlimm ?

Trotzdem steht ja immer noch meine Frage im Raum, warum FOnChange so funktioniert und FOnDelete nicht. Ist mein Sourcecode denn überhaupt richtig oder habe ich einen Fehler gemacht ?

Sir Rufo 1. Nov 2012 18:56

AW: Ein Object in TObjectList<T> löschen mit Ereignis
 
Zitat:

Zitat von RWarnecke (Beitrag 1189388)
Im Beitrag 7, siehst Du im Anhang, wie der Setter von FOnDelete arbeitet. Ebenfalls im gleichen Beitrag hatte ich geschrieben, dass ein Assigned(FOnDelete) immer False ergibt.

Ich habe noch ein bisschen weiter getestet. Wenn ich die TCustomObjectList<T: class> für die Klasse TDegree folgendermaßen erstelle :
Delphi-Quellcode:
  DegreeList := TCustomObjectList<TDegree>.Create;
  DegreeList.OnDelete := TableEntryDelete;
Funktioneren beide Quelltexte aus den Beiträgen 1 und 7 und die Funktion für FOnDelete wird ausgeführt. Lasse ich diese Zeile
Delphi-Quellcode:
DegreeList.OnDelete := TableEntryDelete;
weg, dann wird FOnDelete nichtmehr ausgeführt oder es wird die Access Violation angezeigt, wenn die Abfrage auf Assigned(FOnDelete) fehlt. Deshalb gehe ich davon aus, das das FOnDelete im Notify auf das FOnDelete in der TCustomObjectList<T: class> zeigt.

Ich stelle mir jetzt nur die Frage, warum FOnChange so funktioniert und FOnDelete nicht ?

Wenn du irgendwo hingreifst (weil da was sein soll), da aber nichts ist, dann kommt eine AV. Das sollte jedem klar sein.

Dein Code aus Beitrag 1
Delphi-Quellcode:
procedure TCustomObjectList<T>.Notify(const Item: T;
  Action: TCollectionNotification);
begin
  if                       // Wenn
    Assigned( FOnChange )  // dem Event was zugewiesen ist
  and                      // und
    ( FNewRecord )         // FNewRecord wahr ist
  and                      // und
    ( Action <> cnRemoved ) // die Aktion <> Entfernt ist
  then                     // dann
    begin
      FNewRecord := False;
      FOnChange(Self);
    end;

  if                   // Wenn
    Action = cnRemoved // die Aktion = Entfernt ist
  then                 // dann
    begin
      FOnDelete(Item); // knallt es hier, wenn FOnDelete = nil ist !!!!!
    end;

  inherited Notify(Item, Action); // hier wird die Objekt-Instanz freigegeben, wenn OwnsObjects = True
end;
Du prüfst, ob FOnChange zugewiesen ist, aber du prüfst nicht, ob FOnDelete zugewiesen ist und wunderst dich, dass es knallt, wenn du FOnDelete nichts zuweist?

Warum hast du das Überprüfen weggelassen? Das kann ich irgendwie überhaupt nicht nachvollziehen, und schon gar nicht die Verwunderung, dass da mal eine AV kommt. Ich würde es bei dem Code eher erwarten und mich wundern wenn keine kommt.

Bzgl. der Frage, wann die Objekt-Instanz freigegeben wird: In der überschriebenen Notify-Methode von TObjectList<T>. Wenn du also die Notify-Methode überschreibst und erst am Ende das
Delphi-Quellcode:
inherited
gibst, dann ist die Objekt-Instanz noch da.

Sir Rufo 1. Nov 2012 18:58

AW: Ein Object in TObjectList<T> löschen mit Ereignis
 
Zitat:

Zitat von RWarnecke (Beitrag 1189402)
Zitat:

Zitat von stahli (Beitrag 1189400)
Ich meinte den kleinen blauen Punkt vor jeder Befehlszeile (auf den man zum Setzen eines Breakpoints klickt).
Gelegentlich fehlten die bei mir schon mal, wenn der Compiler mit meinen Units nicht zurecht kam. :stupid:

Ich habe die Klassen in einer ganz normalen Unit. Wenn ich STRG+F9 drücke, erscheinen in dieser Unit keine blauen Punkte. Ist das schlimm ?

Trotzdem steht ja immer noch meine Frage im Raum, warum FOnChange so funktioniert und FOnDelete nicht. Ist mein Sourcecode denn überhaupt richtig oder habe ich einen Fehler gemacht ?

Wenn dort keine blauen Punkte erscheinen, dann sind diese Codeteile nicht compiliert worden, werden also auch nicht aufgerufen. Wenn du die nicht brauchst, dann ist das nicht schlimm :)

stahli 1. Nov 2012 19:11

AW: Ein Object in TObjectList<T> löschen mit Ereignis
 
Ich habe das Problem so verstanden, dass Rolf die Methoden definitiv zuweist:
Delphi-Quellcode:
Degree := TDegree.Create;
Degree.OnChange := TableEntryChange;
Degree.OnDelete := TableEntryDelete;
aber OnDelete dennoch nil ist.

In dem Fall kann eigentlich ja nur der Compiler ein Problem haben.

Wenn in Deiner Klasse keine Befehlszeilenpunkte stehen (und Deine Klasse benutzt Du doch!?) dann ist das bestimmt ein Problem mit dem Compiler.
Hast Du Deine Unit irgendwie kopiert und IDE und Compiler greifen auf unterschiedliche Versionen zu?

Ich würde mal schrittweise die relevanten Stellen durchlaufen...

Sir Rufo 1. Nov 2012 19:31

AW: Ein Object in TObjectList<T> löschen mit Ereignis
 
Zitat:

Zitat von stahli (Beitrag 1189413)
Ich habe das Problem so verstanden, dass Rolf die Methoden definitiv zuweist:
Delphi-Quellcode:
Degree := TDegree.Create;
Degree.OnChange := TableEntryChange;
Degree.OnDelete := TableEntryDelete;
aber OnDelete dennoch nil ist.

Es ist sehr schön, wenn er dem Objekt Events zuweist, aber was hat das mit der Liste zu tun?

TDegree ist ein einfaches Objekt und befindet sich nachher in der Liste, ist aber nicht die Liste selber ;)

EDIT:

Im Beitrag #7 da hat er ein schönes Bild angehängt, wo man auch sehr schön sehen kann, das FOnDelete den Wert nil hat, und den Items (TDegree) die Events zugewiesen wurden (<>nil) :)

RWarnecke 1. Nov 2012 19:36

AW: Ein Object in TObjectList<T> löschen mit Ereignis
 
Zitat:

Zitat von Sir Rufo (Beitrag 1189421)
Es ist sehr schön, wenn er dem Objekt Events zuweist, aber was hat das mit der Liste zu tun?

TDegree ist ein einfaches Objekt und befindet sich nachher in der Liste, ist aber nicht die Liste selber ;)

EDIT:

Im Beitrag #7 da hat er ein schönes Bild angehängt, wo man auch sehr schön sehen kann, das FOnDelete den Wert nil hat :)

Ich glaube jetzt habe ich es verstanden. Korrigiere mich, wenn ich falsch liege. Das OnChange funktioniert deshalb, weil die Methode von OnChange nur das Objekt ändert und nicht die Liste. Mein OnDelete ändert aber meine Liste und deshalb muss meine Methode für OnDelete expliziet der ObjectList zugewiesen werden. Richtig oder falsch ?

Sir Rufo 1. Nov 2012 21:29

AW: Ein Object in TObjectList<T> löschen mit Ereignis
 
Zitat:

Zitat von RWarnecke (Beitrag 1189422)
Zitat:

Zitat von Sir Rufo (Beitrag 1189421)
Es ist sehr schön, wenn er dem Objekt Events zuweist, aber was hat das mit der Liste zu tun?

TDegree ist ein einfaches Objekt und befindet sich nachher in der Liste, ist aber nicht die Liste selber ;)

EDIT:

Im Beitrag #7 da hat er ein schönes Bild angehängt, wo man auch sehr schön sehen kann, das FOnDelete den Wert nil hat :)

Ich glaube jetzt habe ich es verstanden. Korrigiere mich, wenn ich falsch liege. Das OnChange funktioniert deshalb, weil die Methode von OnChange nur das Objekt ändert und nicht die Liste. Mein OnDelete ändert aber meine Liste und deshalb muss meine Methode für OnDelete expliziet der ObjectList zugewiesen werden. Richtig oder falsch ?

Da wir hier - du ausgenommen - nicht wissen, was sich hinter den Methoden verbirgt, geschweige denn was du vorhast, kann ich diese Frage nicht beantworten.

Aber evtl. hilft dir folgendes:

Wenn du möchtest, dass ich das Fenster schließe, dann musst du mir das sagen und nicht Erna Rübenkohl aus Recklinghausen, auch wenn die mal ein Foto von mir gesehen hat.

DeddyH 2. Nov 2012 07:04

AW: Ein Object in TObjectList<T> löschen mit Ereignis
 
Zitat:

Zitat von Sir Rufo (Beitrag 1189436)
Wenn du möchtest, dass ich das Fenster schließe, dann musst du mir das sagen und nicht Erna Rübenkohl aus Recklinghausen, auch wenn die mal ein Foto von mir gesehen hat.

Das wird die arme Erna aber traurig machen :lol:

RWarnecke 2. Nov 2012 07:15

AW: Ein Object in TObjectList<T> löschen mit Ereignis
 
Das OnChange soll dann ausgeführt werden, wenn ein Objekt hinzugefügt oder geändert wird. Das OnDelete soll dann ausgeführt werden, wenn ein Object aus der ObjectList gelöscht wird.

Sir Rufo 2. Nov 2012 07:49

AW: Ein Object in TObjectList<T> löschen mit Ereignis
 
Zitat:

Zitat von RWarnecke (Beitrag 1189455)
Das OnChange soll dann ausgeführt werden, wenn ein Objekt hinzugefügt oder geändert wird. Das OnDelete soll dann ausgeführt werden, wenn ein Object aus der ObjectList gelöscht wird.

Beide Klassen haben die Eigenschaft OnDelete und OnChange. Deine Erklärung ist da nicht präzise, von welcher Klasse du jetzt das OnDelete/OnChange meinst.

Das ist auch der Grund warum es nicht so funktioniert, wie du das erwartest. ;)

Jonas Shinaniganz 2. Nov 2012 08:30

AW: Ein Object in TObjectList<T> löschen mit Ereignis
 
Zitat:

Das OnDelete soll dann ausgeführt werden, wenn ein Object aus der ObjectList gelöscht wird.
Delphi-Quellcode:
TListe = class (TObjectList<TForm1>)
public
  procedure Delete(Index: Integer); overload;
end;

procedure TListe.Delete(Index: Integer);
begin
  inherited Delete(Index);
  ShowMessage('es wurde was gelöscht'); // oder OnDelete ??
end;
Delphi-Quellcode:
  Liste := TListe.Create;
  Liste.Add(Form1);
  Liste.Delete(0); // Es wurde was gelöscht

So hab Ich es jetzt verstanden... Also jetzt meldet die Liste was

Sir Rufo 2. Nov 2012 08:39

AW: Ein Object in TObjectList<T> löschen mit Ereignis
 
Zitat:

Zitat von Jonas Shinaniganz (Beitrag 1189459)
Zitat:

Das OnDelete soll dann ausgeführt werden, wenn ein Object aus der ObjectList gelöscht wird.
[...]
So hab Ich es jetzt verstanden... Also jetzt meldet die Liste was

Es ist aber nicht eindeutig, da sowohl TDegree und TCustomObjectList OnDelete haben. Ja welches denn jetzt?

Zumal die Begrifflichkeiten streng genommen falsch sind:

TDegree.OnDelete = Wenn die Instanz (TDegree) aus dem Speicher entfernt wird

TCustomObjectList.OnDelete = Wenn die Instanz (TCustomObjectList) aus dem Speicher entfernt wird
... ach nee, wenn ein Item aus der Liste geworfen wird ... aber für die Liste ist es doch nur eine Änderung an sich selber ;)

Jonas Shinaniganz 2. Nov 2012 09:01

AW: Ein Object in TObjectList<T> löschen mit Ereignis
 
Ein OnDelete welches so gedacht ist, dass es vom Objekt selbst kommt.. quasi ein OnDestroy.. naja das könnte dann höchstens ein "OnBeforeDelete" werden...

destructor Destroy;
begin
OnDelete;
//kram...
inherited;
end;

Aber du willst ja ein "OnItemDelete" um der Sache mal ein eindeutigeres Wort zu geben.

Oder?

Edit:

Die Liste soll ja deine Objekte verwalten... je nachdem wie du arbeitest brauchen dann deine Objekte garkein OnDelete AKA OnDestroy. Außer deine Objekte sind wiederum von einer Klasse die vielleicht eine Liste oder etwas mit einem sinnigen Delete hat...

stahli 2. Nov 2012 09:05

AW: Ein Object in TObjectList<T> löschen mit Ereignis
 
Vielleicht sollte die Ereignisbehandlung der Liste über "OnItemDelete" laufen...

EDIT: Oups, überholt ohne einzuholen... ;-)

RWarnecke 2. Nov 2012 17:23

AW: Ein Object in TObjectList<T> löschen mit Ereignis
 
So sehen jetzt meine beiden Klassen aus und funktionierten so wie Sie sollen :
Delphi-Quellcode:
  TModifyActionList = (lnNone, lnChange, lnDelete);

  TDegree = class
  private
    FOnChange     : TNotifyEvent;
    FModifyAction : TModifyActionList;
    FID           : Integer;
    FDegree       : string;
    procedure SetID(const Value: Integer);
    procedure SetDegree(const Value: string);
    procedure SetOnChange(const Value: TNotifyEvent);
  public
    property ID           : Integer          read FID           write SetID;
    property Degree       : string           read FDegree       write SetDegree;
    property ModifyAction : TModifyActionList read FModifyAction write FModifyAction;
    property OnChange     : TNotifyEvent     read FOnChange     write SetOnChange;
  end;

  TCustomObjectList<T: class> = class(TObjectList<T>)
  private
    FOnChange    : TNotifyEvent;
    FOnItemDelete : TNotifyEvent;
    FModifyAction : TModifyActionList;
    FNewRecord   : Boolean;
    procedure SetOnItemDelete(const Value: TNotifyEvent);
    procedure SetOnChange(const Value: TNotifyEvent);
  protected
    function getItem(Index: Integer): T; virtual;
    procedure setItem(Index: Integer; Objekt: T); virtual;
    procedure Notify(const Item: T; Action: TCollectionNotification); override;
  public
    function Add(Objekt: T): Integer; virtual;
    function NewRecord(Objekt: T): Integer; virtual;
    function Remove(Objekt: T): Integer; virtual;
    function IndexOf(Objekt: T): Integer; virtual;
    procedure Insert(Index: Integer; Objekt: T); virtual;
    property Items[index: Integer]: T        read getItem      write setItem; default;
    property ModifyAction : TModifyActionList read FModifyAction write FModifyAction;
    property OnChange    : TNotifyEvent     read FOnChange    write SetOnChange;
    property OnItemDelete : TNotifyEvent     read FOnItemDelete write SetOnItemDelete;
  end;

{...}

{ TCustomObjectList<T> }

function TCustomObjectList<T>.Add(Objekt: T): Integer;
begin
  FNewRecord := False;
  Result := inherited Add(Objekt);
end;

function TCustomObjectList<T>.getItem(Index: Integer): T;
begin
  Result := T(inherited Items[Index]);
end;

function TCustomObjectList<T>.IndexOf(Objekt: T): Integer;
begin
  Result := inherited IndexOf(Objekt);
end;

procedure TCustomObjectList<T>.Insert(Index: Integer; Objekt: T);
begin
  inherited Insert(Index, Objekt);
end;

function TCustomObjectList<T>.NewRecord(Objekt: T): Integer;
begin
  FNewRecord := True;
  Result := inherited Add(Objekt);
end;

procedure TCustomObjectList<T>.Notify(const Item: T;
  Action: TCollectionNotification);
begin
  if (Assigned(FOnChange)) and (FNewRecord) and (Action <> cnRemoved) then
  begin
    FNewRecord := False;
    FOnChange(Self);
  end;
  if Action = cnRemoved then
  begin
    FOnItemDelete(Item);
  end;
  inherited Notify(Item, Action);
end;

function TCustomObjectList<T>.Remove(Objekt: T): Integer;
begin
  Result := inherited Remove(Objekt);
end;

procedure TCustomObjectList<T>.setItem(Index: Integer; Objekt: T);
begin
  inherited Items[Index] := Objekt;
end;

procedure TCustomObjectList<T>.SetOnChange(const Value: TNotifyEvent);
begin
  FOnChange := Value;
end;

procedure TCustomObjectList<T>.SetOnItemDelete(const Value: TNotifyEvent);
begin
  FOnItemDelete := Value;
end;

{ TDegree }

procedure TDegree.SetDegree(const Value: string);
begin
  FDegree := Value;
  if (Assigned(FOnChange)) and (FModifyAction = lnChange) then
    FOnChange(Self);
end;

procedure TDegree.SetID(const Value: Integer);
begin
  FID := Value;
  if (Assigned(FOnChange)) and (FModifyAction = lnChange) then
    FOnChange(Self);
end;

procedure TDegree.SetOnChange(const Value: TNotifyEvent);
begin
  FOnChange := Value;
end;
So wird jetzt die ObjectList erstellt und die Liste befüllt :
Delphi-Quellcode:
  DegreeList := TCustomObjectList<TDegree>.Create;
  DegreeList.OnItemDelete := TableEntryDelete;

  { Dieser Teil wird in einer Schleife ein paar Mal wiederholt }
  Degree := TDegree.Create;
  Degree.OnChange := TableEntryChange;
  Degree.ID := FieldByName('ID').AsInteger;
  Degree.Degree := FieldByName('Degree').AsString;
  DegreeList.Add(Degree);
Jetzt ist es so, wenn ein Object aus der Liste gelöscht wird, also mit
Delphi-Quellcode:
DegreeList.Delete(0)
, dann wird das OnItemDelete zusätzlich mit ausgeführt. Wird jetzt der Inhalt eines Objectes geändert mit
Delphi-Quellcode:
DegreeList.Items[0].Degree := AdvEdt_12.Text;
so wird über die Variable ModifyAction=lnChange das OnChange-Ereignis der Klasse aufgerufen. Somit funktioniert das Konstrukt so wie ich es haben möchte.

Ich hoffe es ist jetzt ein bisschen klarer, was ich erreichen wollte. Wenn es noch Verbesserungsvorschläge gibt, dann nur her damit. Ich bin immer bereit noch etwas zu lernen.


Alle Zeitangaben in WEZ +1. Es ist jetzt 19:58 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