![]() |
Bei TList, Record auf Datenänderung reagieren
Hallo zusammen,
ich habe diesen Teil des ![]()
Delphi-Quellcode:
Das funktioniert alles soweit nach dem Tutorial. Nun stelle ich mir die Frage, wie kann ich jetzt auf Änderung in den Daten reagieren um diese Änderung dann in eine Datenbank zu schreiben ?
type
PGesellschaftsname = ^TGesellschaftsname; TGesellschaftsname = record StammNr : Integer; Gesellschaftername : string; Zeichnungskapital : Double; ZweiteZahlung : TDateTime; ZahlenderBetrag : Double; end; var Gesellschafter : PGesellschaftsname; GesellschafterListe : TList; |
Re: Bei TList, Record auf Datenänderung reagieren
|
Re: Bei TList, Record auf Datenänderung reagieren
Moin Moin Ralph,
danke erstmal für Deine Antwort. Nur verstehe ich noch nicht ganz wie mir das Beispiel weiterhelfen soll, da ich kein Grid habe, sondern nur verstreute Werte in der Anwendung. Diese verstreuten Werte sind lediglich nur in Editfeldern oder ListViews untergebracht. |
Re: Bei TList, Record auf Datenänderung reagieren
Hi,
wie wär's, wenn Du statt des Records eine Class mit Properties und Gettern und Settern verwendest, und dann ein OnChange Event implementierst ? Gruss |
Re: Bei TList, Record auf Datenänderung reagieren
Zitat:
Edit: Ok, jetzt weiß ich was Du meinst. Ich habe dabei nur ein Problem, wie verbinde ich eine Klasse mit TList, da ich durch die TList ja auch mehrere Werte speichern kann. Bei einer Klasse kann ich das ja so ohne weitere ja nicht oder ? Und dann kommt das zweite Problem, ich habe noch nie ein OnChange Event geschrieben. |
Re: Bei TList, Record auf Datenänderung reagieren
Zitat:
Bei Records wird im Allgemeinen nur DIREKT der wert geändert und es gibt keine Möglichkeit über eine Änderung informiert zu werden (außer wenn den Speicherbereich sperrt, wo man sich bei Zugriff eine Systemexception auslösen lassen kann, welche man dann passend behandelt ... ist aber nicht grad optimal). Alternativ kann man seit Delphi 2006 / TDE auch Records mit Properties versehen und hätte so dann den nötigen Einstiegspunkt, um sowas wie OnChange zu implementieren. |
Re: Bei TList, Record auf Datenänderung reagieren
Hi,
Das geht dann (fast) genauso, wie mit deinem Record. Du erstellst doch mehrere Records (z.B. mit Getmem) und fügst sie der Liste hinzu. Erstelle stattdessen mehrere Instanzen der Klasse und füge sie der Liste hinzu.
Delphi-Quellcode:
und wenn es zum Aufräumen kommt, nicht vergessen die Objekte der Liste freizugeben. Nur nicht mit FreeMem, sondern mit Free.
type
tMyClass = class private FWert: integer; FOnChange: tNotifyEvent; procedure SetWert(Value:integer); function GetWert:integer; public property Wert: integer read GetWert write SetWert; property OnChange: tNotifyEvent read FOnChange write FOnChange; end; procedure tMyClass.SetWert(Value:integer); begin FWert := Value; if Assigned(FonChange) then FOnChange(Self); end; function tMyClass.GetWert:integer; begin result := FWert; end; ... aClass := tMyClass.Create; aClass.OnChange := WertAenderung; aList.Add(aClass); ... procedure tMyForm.WertAenderung(Sender:tObject); begin showmessage('aha es hat sich was geändert'); end; P.S. Jetzt kommen gleich wieder die Einwände eine TOjectList zu nehmen, weil man das dann der Liste überlassen kann, aber es geht hier um das Verständnis und nicht um die Eleganz. :D |
Re: Bei TList, Record auf Datenänderung reagieren
Hallo Thomas,
danke erstmal für Deine ausführliche Antwort. Ich werde mir jetzt mal Dein Beispiel zu gemüte führen und ausprobieren. Zitat:
|
Re: Bei TList, Record auf Datenänderung reagieren
Hallo Rolf,
naeher betrachtet geht es in dem Link ja um eine TList, dass Daten fuer ein Grid gespeichert werden ist eigentlich nebensaechlich. Um ueber Aenderungen informiert zu werden, wird fuer TList ein OnChange-Event implementiert. Aber es gibt ja nun schon einige andere Hinweise, die den gleichen / einen aehnlichen Weg aufzeigen. |
Re: Bei TList, Record auf Datenänderung reagieren
Ich habe das ganze jetzt mal umgebaut und ausprobiert nach dem Vorschlag von Thomas. Das mit dem Eintrag der Daten funktioniert so weit. Meine Deklaration sieht jetzt so aus :
Delphi-Quellcode:
Nur kapiere ich das irgendwie mit dem Pointern noch nicht. Wie kann ich jetzt einen Wert aus der Klasse TGesellschaftsname auslesen ? Ich habe das vorher mit dem Record so gemacht :
type
TGesellschaftsname = class private FOnChange : TNotifyEvent; FStammNr : Integer; FGesellschaftername : string; FZeichnungskapital : Double; FZweiteZahlung : TDateTime; FZahlenderBetrag : Double; procedure SetStammNr(Value: Integer); procedure SetGesellschaftername(Value: String); procedure SetZeichnungskapital(Value: Double); procedure SetZweiteZahlung(Value: TDateTime); procedure SetZahlenderBetrag(Value: Double); function GetStammNr:Integer; function GetGesellschaftername:String; function GetZeichnungskapital:Double; function GetZweiteZahlung:TDateTime; function GetZahlenderBetrag:Double; public property StammNr : Integer read GetStammNr write SetStammNr; property Gesellschaftername : string read GetGesellschaftername write SetGesellschaftername; property Zeichnungskapital : Double read GetZeichnungskapital write SetZeichnungskapital; property ZweiteZahlung : TDateTime read GetZweiteZahlung write SetZweiteZahlung; property ZahlenderBetrag : Double read GetZahlenderBetrag write SetZahlenderBetrag; property OnChange : TNotifyEvent read FOnChange write FOnChange; end; var Gesellschafter : TGesellschaftsname; GesellschafterListe : TList;
Delphi-Quellcode:
Nur mit der Klasse bekomme ich jetzt eine EAccessViolation. Wen ich jetzt hinten das Zeichen ^ weglasse passiert garnichts. Genauso mit dieser Zeile :
TGesellschaftsname(GesellschafterListe[LV_Gesellschaftsform.Selected.Index]^).Gesellschaftsname
Delphi-Quellcode:
Was mache ich hier falsch ?
TGesellschaftsname(GesellschafterListe.Items[LV_Gesellschaftsform.Selected.Index]).Gesellschaftername
|
Re: Bei TList, Record auf Datenänderung reagieren
Hallo,
ohne Dach (^) sollte es "eigentlich" klappen. Um genau den Fehler finden zu können, müsste etwas mehr Code her ;-) Du arbeitest bei Klassen sowieso immer mit Adressen, auch wenn Du es nicht direkt mitbekommst. Deshalbt benötigst Du auch keine P-Referenz auf eine Instanz (So sehe ich das zumindest ;). Du erzeugst vermutlich so:
Delphi-Quellcode:
Anschließend wirst Du vermutlich irgendwo so den Instanzzeiger speicher:
Gesellschafter := TGesellschafter.Create;
GesellschafterListe := TList.Create;
Delphi-Quellcode:
Und hiermit greifst Du auf ein Element zu:
GesellschafterListe.Add(Gesellschafter);
Delphi-Quellcode:
Tipp: In einer Listview kannst Du auch mit ListItem.Data eine Referenz auf ein Objekt speichern.
GesellschafterListe[Index];
Grüße :-) Michael |
Re: Bei TList, Record auf Datenänderung reagieren
Zitat:
Zitat:
Zitat:
Delphi-Quellcode:
Nur es funktioniert nicht :gruebel: Da war das mit dem Record ja doch einfacher.
with TGesellschaftsname(GesellschafterListe[LV_Gesellschaftsform.Selected.Index]) do
begin Edit1.Text := Gesellschaftername; { u.s.w. für die anderen Properties } end; Edit: Bevor die Frage kommt, was passiert. Mit diesem With-Do passiert garnichts. Es kommt kein Fehler und es wird auch nichts angezeigt. |
Re: Bei TList, Record auf Datenänderung reagieren
Hi,
versuche bitte mal folgendes:
Wenn mehr Source da ist, kann auch besser geholfen werden. Poste doch am besten mal die komplette(n) Klasse(n) bzw. das Projekt. Grüße :-) Michael :-) |
Re: Bei TList, Record auf Datenänderung reagieren
Hallo Michael,
danke für Deine Hilfe. Ich habe mittlerweile die Lösung selber gefunden. In der TList war überall der letzte Datensatz gespeichert. Das lag daran, dass ich die Klasse nicht für jeden Eintrag neu erstellt habe. Wie ich das Problem gelöst hatte, habe ich mich noch in die TObjectList eingelesen und nun darauf alles umgestellt. Die TObjetList funktioniert sogar noch ein bisschen besser als die TList, von der Speicherverwaltung her. Das ganze sieht nun so aus. Meine Klassen-Deklaration aus Beitrag #10 und dann der folgende Sourcecode :
Delphi-Quellcode:
Das funktioniert alles so wie es soll. Ich habe jetzt nur noch eine Frage, muss ich für jedes Object, welches ich erstelle das OnChange-Ereignis neu zuweisen oder kann ich es auch einmal für die ganze ObjectListe zuweisen ?
{...}
var GesellschafterListe : TObjectList; // so erstelle ich die TObjectListe GesellschafterListe := TObjectList(true); // so mache ich das mit jedem neuen Object für die Liste GesellschafterListe.Add(TGesellschaftsname.Create); TGesellschaftsname(GesellschafterListe.Last).OnChange := WertAenderung; TGesellschaftsname(GesellschafterListe.Last).StammNr := StrToInt(LV_Kundendaten.Items.Item[LV_Kundendaten.Selected.Index].SubItems.Strings[1]); TGesellschaftsname(GesellschafterListe.Last).Gesellschaftername := Edt_Gesellschaftsname.Text; // so gebe ich die TObjectListe wieder frei GesellschafterListe.Free {...} procedure TMainForm.WertAenderung(Sender: TObject); begin ShowMessage('Hier wird auf die Änderung reagiert'); end; |
Re: Bei TList, Record auf Datenänderung reagieren
Hi Rolf,
ich definiere Eventhandler zu eigenen Klassen immer pro Instanz. Aber versuche Dich mal in Class Properties einzulesen. Damit sollte es global möglich sein. Grüße nach Stuttgart :cheer: Michael |
Re: Bei TList, Record auf Datenänderung reagieren
Oder:
Ableitung von TObjectList erstellen, da das OnChange Event dranpacken. Im Konstruktor von TGesellschaftsname gibst du die Liste mit. TGesellschaftsname kennt dann seine Liste und kann dann das Event an der Liste aufrufen (und sich vorher selbst der Liste hinzufügen), was du ja nur einmal im Programm an der Liste zuweisen musst. So wird die Liste eine Art Manager für die einzelnen Objekte, und es ist, finde ich, nicht unwahrscheinlich, dass man das gut gebrauchen kann, zum Beispiel für eine Funktion, die mit allen Objekten in der Liste irgendwas machen soll. So hingetippselt, hoffe das funktioniert bzw. ist irgendwie unter Umständen hilfreich...
Delphi-Quellcode:
interface
type TGesellschafterListe = class; TGesellschafterListeChangeEvent = procedure(Sender: TObject; Obj: TGesellschaftsname) of object; TGesellschaftsname = class private FList: TGesellschafterListe; public procedure TriggerChange; constructor Create(List: TGesellschafterListe); end; TGesellschafterListe = class(TObjectList) private FOnChange: TGesellschafterListeChangeEvent; public property OnChange: TGesellschafterListeChangeEvent read FOnChange write FOnChange; end; implementation procedure TGesellschaftsname.Create(List: TGesellschafterListe); begin inherited; FList := List; FList.Add(Self); end; procedure TGesellschaftsname.TriggerChange; begin if Assigned(FList) then if Assigned(FList.OnChange) then FList.OnChange(FList, Self); end; procedure GesellschafterListeOnChange(Sender: TObject; Obj: TGesellschaftsname); begin // Schnack, hier hat sich das Objekt "Obj" verändert end; var GesellschafterListe : TGesellschafterListe; begin GesellschafterListe := TGesellschafterListe(True); GesellschafterListe.OnChange := GesellschafterListeOnChange; TGesellschaftsname.Create(GesellschafterListe); TGesellschaftsname.Create(GesellschafterListe); TGesellschaftsname.Create(GesellschafterListe); GesellschafterListe.Free; end; |
Re: Bei TList, Record auf Datenänderung reagieren
Hallo Alexander,
leider komme ich mit Deinem Beispiel nicht ganz klar. Dort tauchen zu viele Fehler auf. Deshalb habe ich mich jetzt mal selber dran versucht und bin zu folgendem Ergebnis gekommen :
Delphi-Quellcode:
Das funktioniert auch soweit. Nur was muss ich machen, dass ich im OnChange Ereignis auf die Werte der Klasse zugreifen kann, die gerade in die ObjectListe eingefügt wurde ?
type
TForm8 = class(TForm) Btn_3: TButton; procedure Btn_3Click(Sender: TObject); private { Private declarations } procedure ListeChange(Sender: TObject); public { Public declarations } end; var Form8: TForm8; implementation {$R *.dfm} type TKlasse = class private FWert1 : Integer; FWert2 : string; public property Wert1 : Integer read FWert1; property Wert2 : string read FWert2; end; TCustomObjectList = class(TObjectList) private FOnChange : TNotifyEvent; protected function getItem(index: Integer): TKlasse; virtual; procedure setItem(index: Integer; Objekt: TKlasse); virtual; public function Add(Objekt: TKlasse):Integer; virtual; function Remove(Objekt: TKlasse):Integer; virtual; function IndexOf(Objekt: TKlasse):Integer; virtual; procedure Insert(index: Integer; Objekt: TKlasse); virtual; property Items[index: Integer]: TKlasse read getItem write setItem; default; property OnChange: TNotifyEvent read FOnChange write FOnChange; end; procedure TForm8.Btn_3Click(Sender: TObject); var Liste: TCustomObjectList; begin Liste := TCustomObjectList.Create(True); Liste.OnChange := ListeChange; Liste.Add(TKlasse.Create); Liste.Add(TKlasse.Create); Liste.Add(TKlasse.Create); Liste.Add(TKlasse.Create); Liste.Free; end; procedure TForm8.ListeChange(Sender: TObject); begin ShowMessage('Ein neues Object hinzugefügt !!'); end; { TCustomObjectList } function TCustomObjectList.Add(Objekt: TKlasse): Integer; begin Result := inherited Add(Objekt); if Assigned(FOnChange) then FOnChange(Self) end; function TCustomObjectList.getItem(index: Integer): TKlasse; begin Result := TKlasse(inherited Items[Index]); end; function TCustomObjectList.IndexOf(Objekt: TKlasse): Integer; begin Result := inherited IndexOf(Objekt); end; procedure TCustomObjectList.Insert(index: Integer; Objekt: TKlasse); begin inherited Insert(Index, Objekt); end; function TCustomObjectList.Remove(Objekt: TKlasse): Integer; begin inherited Remove(Objekt); end; procedure TCustomObjectList.setItem(index: Integer; Objekt: TKlasse); begin inherited Items[index] := Objekt; end; |
Re: Bei TList, Record auf Datenänderung reagieren
Hallo Rolf,
du koenntest z.B. anstelle des simplen Notify-Events einen eigenen Event-Typ definieren und dann den/die veraenderten Werte in deinem speziellen OnChange() mitliefern. |
Re: Bei TList, Record auf Datenänderung reagieren
Moin Ralph und frohes neues Jahr, kurz gefragt, wie schreibt man sich sein eigenes Event ? Stecke nämlich noch in den Kinderschuhen bei solchen Sachen. Geht das vielleicht in Richtung Windows Messages ?
|
Re: Bei TList, Record auf Datenänderung reagieren
|
Re: Bei TList, Record auf Datenänderung reagieren
So sieht mein Versuch aus. Nur leider schaffe ich es nicht,
Delphi-Quellcode:
Das ChangeEvent wird nicht ausgeführt, wenn ich einen Wert ändere. Wenn ich NewRecord ausführe, dann wird das Change-Event ausgeführt, aber in Obj stehen keine Werte aus der Klasse. Kann mir da jemand bitte weiterhelfen, denn ich weiß nichtmehr weiter.
TGesellschafterChangeEvent = procedure(Sender: TObject; Obj: TGesellschaftsname) of object;
TGesellschafterObjListe = class(TObjectList) private FOnChange : TGesellschafterChangeEvent; protected function getItem(Index: Integer): TGesellschaftsname; virtual; procedure setItem(Index: Integer; Objekt: TGesellschaftsname); virtual; public function Add(Objekt: TGesellschaftsname): Integer; virtual; function NewRecord(Objekt: TGesellschaftsname): Integer; virtual; function Remove(Objekt: TGesellschaftsname): Integer; virtual; function IndexOf(Objekt: TGesellschaftsname): Integer; virtual; procedure Insert(Index: Integer; Objekt: TGesellschaftsname); virtual; property Items[index: Integer]: TGesellschaftsname read getItem write setItem; default; property OnChange: TGesellschafterChangeEvent read FOnChange write FOnChange; end; {...} procedure TMainForm.GesellschafterListeChange(Sender: TObject; Obj: TGesellschaftsname); begin with DM_Main.UniQuery_Temp do begin SQL.Text := 'EXECUTE PROCEDURE SP_NEW_GESELLSCHAFTER (:stammnr, :name, :B1, :B2, :D2);'; ParamByName('StammNr').AsInteger := Obj.StammNr; ParamByName('Name').AsString := Obj.Gesellschaftername; ParamByName('B1').AsFloat := Obj.Zeichnungskapital; ParamByName('B2').AsFloat := Obj.ZahlenderBetrag; ParamByName('D2').AsDateTime := Obj.ZweiteZahlung; Execute; end; end; {...} function TGesellschafterObjListe.Add(Objekt: TGesellschaftsname): Integer; begin Result := inherited Add(Objekt); end; function TGesellschafterObjListe.getItem(Index: Integer): TGesellschaftsname; begin Result := TGesellschaftsname(inherited Items[Index]); end; function TGesellschafterObjListe.IndexOf(Objekt: TGesellschaftsname): Integer; begin Result := inherited IndexOf(Objekt); end; procedure TGesellschafterObjListe.Insert(Index: Integer; Objekt: TGesellschaftsname); begin inherited Insert(Index, Objekt); end; function TGesellschafterObjListe.NewRecord(Objekt: TGesellschaftsname): Integer; begin Result := inherited Add(Objekt); if Assigned(FOnChange) then FOnChange(Self, Objekt); end; function TGesellschafterObjListe.Remove(Objekt: TGesellschaftsname): Integer; begin Result := inherited Remove(Objekt); end; procedure TGesellschafterObjListe.setItem(Index: Integer; Objekt: TGesellschaftsname); begin inherited Items[Index] := Objekt; if Assigned(FOnChange) then FOnChange(Self, Objekt); end; |
Re: Bei TList, Record auf Datenänderung reagieren
Wozu ist denn alles als virtual deklariert?
Bringt zu diesem Zeitpunkt eh nix mehr. Wird auch überall TGesellschafterObjListe verwendet und nicht TObjectList/TList? TList kennt deine Änderungen ja nicht. Ich würde eher Notify über schreiben und dort das OnChange-Event auslösen, da kommen alle Meldungen (hinzugefügt/gelöscht) vorbei.
Delphi-Quellcode:
protected
procedure Notify(Ptr: Pointer; Action: TListNotification); override; |
Re: Bei TList, Record auf Datenänderung reagieren
Zitat:
ich kann Dir leider nicht ganz folgen mit dem Notify. Kannst Du mir das bitte mal an einem praktischen Beispiel erklären ? |
Re: Bei TList, Record auf Datenänderung reagieren
TObjectList nutzt dieses z.B. um Objekte freizugeben
Delphi-Quellcode:
du überschreibst es und macht z.B. sowas
procedure TObjectList.Notify(Ptr: Pointer; Action: TListNotification);
begin if OwnsObjects then if Action = lnDeleted then TObject(Ptr).Free; inherited; end;
Delphi-Quellcode:
PS: OnChange reagiert so nur auf Änderungen der Liste,
procedure TGesellschafterObjListe.Notify(Ptr: Pointer; Action: TListNotification);
begin if (Action = lnAddted) and Assigned(FOnChange) then FOnChange(Self, TGesellschaftsname(Ptr)); inherited; end; wenn auch z.B. auf Änderungen/Zuweisungen in/an .Zeichnungskapital reagert werden soll, dann muß dieses in TGesellschaftsname abgefangen und an die TGesellschafterObjListe weitergereicht werden. |
Re: Bei TList, Record auf Datenänderung reagieren
Zitat:
Zitat:
|
Re: Bei TList, Record auf Datenänderung reagieren
Zitat:
Delphi-Quellcode:
if Assigned(FList) then
FList.DoChange(Self);
Delphi-Quellcode:
procedure TGesellschafterObjListe.DoChange(Sender: TGesellschaftsname);
begin if Assigned(FOnChange) then FOnChange(Self, Sender); end; procedure TGesellschafterObjListe.Notify(Ptr: Pointer; Action: TListNotification); begin if Action = lnAddted then DoChange(TGesellschaftsname(Ptr)); inherited; end; |
Re: Bei TList, Record auf Datenänderung reagieren
Ich habe doch nochmal eine Frage zu Notify. Ich habe die Procedure genau so geschrieben wie Du in Deinem Beitrag. Wenn ich jetzt in dem ChangeEvent die Werte mitprotokollieren, sind alle Werte aus der Klasse leer. Entferne ich die Abfrage auf die Action, dann bekomme ich in dem Protokoll erst die leeren Werte angezeigt und danach dann die Werte die ich eingefügt habe in umgekehrter Reihenfolge. Also den letzten zuerst u.s.w. Kann man das noch irgendwie bereinigen ?
Zitat:
Zitat:
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 00:32 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