AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Projekte Data Binding
Thema durchsuchen
Ansicht
Themen-Optionen

Data Binding

Ein Thema von Stevie · begonnen am 28. Apr 2011 · letzter Beitrag vom 28. Apr 2017
Antwort Antwort
Seite 2 von 2     12   
Benutzerbild von Stevie
Stevie
Registriert seit: 12. Aug 2003
Wer schon mit .Net zu tun hatte, wird sie kennen und schätzen gelernt haben: Data Bindings.
Man kann dort nahezu alles aneinander binden. Sei es, um Listen von Objekten in Trees, Lists oder Grids darzustellen oder Eigenschaften von Objekten in Text-, Check- oder Comboboxen editierbar zu machen.
Oder sogar die Sichtbarkeit oder das Aussehen einzelner Controls zu beinflussen.

Wie das genau funktioniert, kann man in der MSDN nachlesen.

Ja und? In Delphi gibts DataSet, DataSource und diverse datensensitive Controls, um das zu bewerkstelligen!
Richtig, die Sache hat nur einen Haken: Man kann nicht einfach ein 08/15 Objekt daran hängen. Sicher, es gibt TSnapObjectDataset oder spezielle Controls, mit denen man das machen kann. Hat aber den entscheidenden Nachteil, dass man immer entweder besondere Controls oder Komponenten haben muss oder seine bindbaren Objekte von einer Basisklasse ableiten muss.

Ich hab mir mal einige Dinge von der .Net Implementierung abgeschaut und das ganze in Delphi mit folgenden Zielsetzung nachgebaut:
  • Standard Komponenten und Controls müssen unterstützt werden (z.B. TEdit)
  • wenig bis keine extra Implementierung für bindbare Objekte
  • minimaler Sourcecode, um Objekte aneinander zu binden

Ok, fangen wir an. Was brauchen wir?

Ein Binding braucht immer eine Source und ein Target (beides ein Objekt) und die Namen der zu bindenden Eigenschaften.
Für unser erstes Beispiel bauen wir uns eine einfache Klasse, die 2 Eigenschaften hat:

Delphi-Quellcode:
type
  TSettings = class
  private
    FCaption: string;
    FColor: TColor;
    procedure SetCaption(const Value: string);
    procedure SetColor(const Value: TColor);
  public
    property Caption: string read FCaption write SetCaption;
    property Color: TColor read FColor write SetColor;
  end;

implementation

procedure TSettings.SetCaption(const Value: string);
begin
  FCaption := Value;
end;

procedure TSettings.SetColor(const Value: TColor);
begin
  FColor := Value;
end;
Wir haben hier schonmal explizite Setter Methoden - die brauchen wir später noch. Aber für das erste Beispiel würde auch in direkter Zugriff auf die Feldvariable genügen.

So, was machen wir nun mit unserem TSettings Objekt? Wir erzeugen 2 Bindings, die auf den Titel und die Farbe unserer MainForm gehen.

Delphi-Quellcode:
procedure TMainForm.FormCreate(Sender: TObject);
begin
  FSettings := TSettings.Create;
  FSettings.Caption := 'Binding demo';
  FSettings.Color := clGray;
  TBinding.Create(FSettings, 'Caption', MainForm, 'Caption');
  TBinding.Create(FSettings, 'Color', MainForm, 'Color');
end;
Dazu genügt es, die Unit System.Bindings einzubinden. Auch um das Freigeben der Binding Objekte braucht man sich nicht kümmern solange mindestens eins der beiden verbundenen Objekte von TComponent abgeleitet ist (in unserem Fall ist es das MainForm).

Starten wir die Anwendung und sehen, was passiert. Titel und Farbe des MainForms ist entsprechend der Werte aus unserem TSettings Objekts gesetzt worden. Toll, oder?
Naja, da hätts auch das explizite Zuweisen der Werte getan... Klar, aber der eigentliche Clou der Bindings kommt erst noch!

Packen wir mal einen Button auf die Form und setzen dort die Eigenschaft unseres Settings Objekts:

Delphi-Quellcode:
procedure TMainForm.Button1Click(Sender: TObject);
begin
  FSettings.Caption := 'Current time: ' + DateTimeToStr(Now);
end;
Da tut sich erstmal nix, aber wir sind auch noch nicht fertig. Gehen wir zurück zu unserer TSettings Klasse und passen sie etwas an.
Wir leiten sie mal von einem Basis Objekt ab, welches ein Interface implementiert - und zwar INotifyPropertyChanged. Auch das ist wieder brav von .Net abgekupfert.
Aha, nun brauchen wir also unsere Setter Methoden für die Eigenschaften.

So sieht unserer modifizierte Klasse nun aus:

Delphi-Quellcode:
type
  TSettings = class(TPropertyChangedBase)
  private
    FCaption: string;
    FColor: TColor;
    procedure SetCaption(const Value: string);
    procedure SetColor(const Value: TColor);
  public
    property Caption: string read FCaption write SetCaption;
    property Color: TColor read FColor write SetColor;
  end;

implementation

procedure TSettings.SetCaption(const Value: string);
begin
  FCaption := Value;
  DoPropertyChanged('Caption');
end;

procedure TSettings.SetColor(const Value: TColor);
begin
  FColor := Value;
  DoPropertyChanged('Color');
end;
Tadaa, und auf Knopfdruck wird nun auch der Titel unserer MainForm geändert. Naja, ganz putzig, aber so richtig rocken tut's noch nicht, oder?

Na gut, werfen wir mal ein TEdit auf die Form und binden es ebenfalls im Konstruktor des MainForms an die Caption Eigenschaft unseres TSettings Objekts:

Delphi-Quellcode:
...
TBinding.Create(FSettings, 'Caption', Edit3, 'Text');
Toll, nun steht der Titel des Forms auch im Edit drin... und wenn ich dort drin änder passiert nix!
Stimmt, es fehlt noch was. Als letzte Unit (genauer gesagt nach den Units, in denen die Controls drin sind, an die ich binden möchte) im uses des Interface Teils des Forms muss die Unit System.Bindings.Controls.
Dort befinden sich einige Ableitungen von Standard VCL Controls mit dem gleichen Namen, welche das INotifyPropertyChanged Interface implementieren. Und dadurch, dass sie als letzte (oder zumindest nach den entsprechenden Units steht) werden beim Erstellen des Forms Objekte von den abgeleiteten Klassen erstellt. Das heißt im Klartext, es müssen keine speziellen Controls oder Komponenten auf der Form verbaut werden, damit man das Data Binding nutzen kann.

Starten wir mal die Anwendung und tippern ein wenig im Edit rum. Wow, der Titel des Forms ändert sich!

Um es nochmal zu betonen:
  • keine speziellen Controls nötig (nur das Subclassing von Controls, die man Binding fähig machen möchte)
  • keine speziellen Events nötig
  • als read-once Source können direkte Ableitungen von TObject benutzt werden (lediglich für die Benachrichtigung von Eigenschaftsänderungen ist das Interface nötig)
  • minimaler Sourcecode, um Objekte, Komponenten und Controls zu verbinden.

Im angehangenden Beispiel sind noch einige weitere Bindings und Controls auf der Form, mit welchen man einige Eigenschaften verändern kann.
Das ganze wurde unter Delphi XE entwickelt. Delphi 2010 könnte auch funktionieren, aber da waren die Generics noch ein wenig verbuggt. Alle Versionen darunter sind leider nicht unterstützt. Das liegt am regen Gebrauch von Generics und neuer RTTI.

Außerdem findet ihr in den benötigten Units noch weitere Dinge wie zum Beispiel Multicast Events oder den NotificationHandler, der dafür zuständig ist, dass die TBinding Objekte ohne Zutun freigegeben werden.

In Planung und teilweise schon realisiert sind Bindings für Listen in dementsprechenden Controls (z.B. ListBox)

Anregungen, Kritik und Fragen sind herzlich willkommen und sehr erwünscht.


P.S.: Crosspost bei Delphi-Forum

Update 18.06.2011:
  • Source der Library aktualisiert (enthält den kompletten Code meiner DSharp Library)
  • aktuelle Samples für data binding hinzugefügt

Update 10.04.2012:
  • veraltete Archive entfernt (aktuelle und supportete Version im svn Repository)
“Simplicity, carried to the extreme, becomes elegance.” Jon Franklin

Delphi Sorcery - DSharp - Spring4D - TestInsight

Geändert von Stevie (10. Apr 2012 um 20:05 Uhr) Grund: Anhang von späteren Beitrag angehängt
 
Jens01
 
#11
  Alt 10. Apr 2012, 18:15
Hallo Stevie,
nach längerer Zeit habe ich DSharp noch mal wieder getestet.
Ich nutze die AdvStringgrids von TMS. Damit funktioniert das irgendwie nicht so ganz.
In der VCLControls-Unit habe ich mir auf Grundlage von TStringgrid eine Klasse für die TAdvStringgrids gebaut. Wenn ich aber das Grid mit Daten gefüllt habe, gibt es eine Exception in der Paint-Methode vom AdvStringGrid.
Code:
if not Assigned(inherited Objects[c,r]) then
in dieser Zeile bei Cellproperties knallts mit "ListenIndex überschreitet Maximum". Könnte dies Objects doppelt belegt sein? Oder fällt Dir dazu etwas ein?
Gruss Jens

Edit:
Habe noch mal geguck. Ich glaube, dass beim Setzen des Cell-Wertes mit Cells[] in SetCells die CellProperties (TMS) mit Daten belegt werden. Wahrscheinlich wird das bei der Belegung über DSharp nicht gemacht....

Geändert von Jens01 (10. Apr 2012 um 18:41 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von Stevie
Stevie

 
Delphi 10.1 Berlin Enterprise
 
#12
  Alt 10. Apr 2012, 20:14
Hi Jens,

bin mir nicht ganz sicher, ob du die aktuelle Version aus dem svn Repository nutzt (hab mal das Posting oben dahingehend aktualisiert).

Dort wird die Unit DSharp.Bindings.CollectionView.VCLAdapters.pas am ehesten interessant sein, genauer gesagt die Klasse TCollectionViewStringGridAdapter.

Das Design sieht so aus, dass die meisten Controls, das für das Anzeige von Listen zuständig sind, eine Aggregation auf ein von TCollectionView abgeleitetes Objekt haben. Diese Klasse kümmert sich um das Anzeigen der Liste in bestimmten Formen (z.B. für eine Repräsentation von TStrings oder im TStringGrid).

Ich kenn ehrlich gesagt den internen Aufbau des TAdvStringGrids nicht, aber wenn es irgendwie von TStringGrid abgeleitet ist, müsste es eigentlich kein Problem sein, mit minimalen oder garkeinen Anpassungen den TCollectionViewStringGridAdapter dafür zu benutzen.
Stefan
  Mit Zitat antworten Zitat
Jens01
 
#13
  Alt 10. Apr 2012, 22:07
Zitat:
bin mir nicht ganz sicher, ob du die aktuelle Version aus dem svn Repository nutzt
Nutze ich.
Zitat:
Ich kenn ehrlich gesagt den internen Aufbau des TAdvStringGrids nicht, aber wenn es irgendwie von TStringGrid abgeleitet ist, müsste es eigentlich kein Problem sein, mit minimalen oder garkeinen Anpassungen den TCollectionViewStringGridAdapter dafür zu benutzen.
Das Ding ist von TStringgrid abgeleitet.
Wenn ich die Exception ein paar mal durchbestätige, dann zeigt er die Daten in den Cellen auch an, aber..naja, so eine Exception in Paint ist doch schon lästig. Bin mir auch nicht sicher, ob es an DSharp oder TMS liegt. Aber das genaue Problem bekomme ich nicht geriffen...
Schade
  Mit Zitat antworten Zitat
Benutzerbild von Stevie
Stevie

 
Delphi 10.1 Berlin Enterprise
 
#14
  Alt 10. Apr 2012, 22:58
Ich schau mal, ob mich die Trial vom TAdvStringGrid weiterbringt, den Fehler zu finden. Kannst du mir deine Anpassungen diesbezüglich zukommen lassen?

Nachtrag: Hab das Problem gefunden - TAdvStringGrid überschreibt die Objects Property und es scheint, dass intern noch zusätzliche Objekte für Zelleneigenschaften oder so gehalten werden. Hab mal ne neue Interceptor Klasse inklusive CollectionView Adapter für das TAdvStringGrid erstellt und commited.
Stefan

Geändert von Stevie (11. Apr 2012 um 11:03 Uhr)
  Mit Zitat antworten Zitat
Jens01
 
#15
  Alt 11. Apr 2012, 13:07
TAdvStringGrid funktioniert!
Danke...
  Mit Zitat antworten Zitat
Benutzerbild von RWarnecke
RWarnecke

 
Delphi XE8 Enterprise
 
#16
  Alt 8. Jul 2012, 23:01
Hallo Stefan,

kann ich das von Dir hier vorgestellte DataBinding auch in Interfaces einsetzen um somit die Kommunikation zwischen Hauptprogramm und DLL zu vereinfachen ?
Rolf Warnecke
  Mit Zitat antworten Zitat
Benutzerbild von Stevie
Stevie

 
Delphi 10.1 Berlin Enterprise
 
#17
  Alt 9. Jul 2012, 01:16
Direkt auf Interfaces binden geht aktuell nicht. Es werden nur Bindings zwischen 2 Objekt Instanzen unterstützt.
Für den Fall, dass du auf ein Interface binden willst, müsstest du dir dann einen Wrapper schreiben.
Stefan
  Mit Zitat antworten Zitat
Benutzerbild von RWarnecke
RWarnecke

 
Delphi XE8 Enterprise
 
#18
  Alt 9. Jul 2012, 08:09
Schade und genau davon habe ich keine Ahnung. Ich wollte für eine nagel neues Programm genau das gleiche PlugIn-System nutzen, wie für Code-Orakel und dann mit Data Bindings arbeiten.
Rolf Warnecke
  Mit Zitat antworten Zitat
bernhard_LA

 
Delphi 11 Alexandria
 
#19
  Alt 28. Apr 2017, 10:42
würde gerne TDatabinding verwenden, wird es ein DSharp auch für Delphi XE10.2 geben


Delphi-Quellcode:
Checking project dependencies...
Compiling DSharp.Core.dproj (Debug, Win32)
brcc32 command line for "DSharp.Core.vrc"
c:\program files (x86)\embarcadero\studio\18.0\bin\cgrc.exe -c65001 "DSharp.Core.vrc" -foDSharp.Core.res
......
[dcc32 Error] DSharp.Core.Reflection.pas(2884): E2361 Cannot access private symbol TRttiObject.FParent
[dcc32 Error] DSharp.Core.Reflection.pas(2885): E2361 Cannot access private symbol TRttiObject.FHandle
[dcc32 Fatal Error] DSharp.ComponentModel.DataAnnotations.pas(83): F2063 Could not compile used unit 'DSharp.Core.Reflection.pas'
Failed
Elapsed time: 00:00:00.9

Geändert von bernhard_LA (28. Apr 2017 um 10:46 Uhr)
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 2 von 2     12   


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 18:41 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