|
Antwort |
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:
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:
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.
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; 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:
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).
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; 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:
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.
procedure TMainForm.Button1Click(Sender: TObject);
begin FSettings.Caption := 'Current time: ' + DateTimeToStr(Now); end; 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:
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?
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; 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:
Toll, nun steht der Titel des Forms auch im Edit drin... und wenn ich dort drin änder passiert nix!
...
TBinding.Create(FSettings, 'Caption', Edit3, 'Text'); 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:
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:
Update 10.04.2012:
“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 |
Delphi 11 Alexandria |
#2
Hi Stevie,
sorgst Du gerade dafür, dass es weniger Argumente für einen Umstieg von Delphi auf .net gibt??? Ich habe es mir noch noch nicht angesehen und werde das mal am Wochenende tun, bezweifle aber, dass ich die Details wirklich nachvollziehen kann. Trotzdem mal ein paar Fragen: Das DataBinding war ja immer ein Pro-Net-Argument. Jetzt würde dann eine Einrichtung der Bindings wohl nur noch in der Delphi-IDE fehlen? Nach meiner (derzeitigen) Einschätzung wird die Binding-Option jedoch auch überbewertet, oder ist zumindest allein noch kein sehr wirksames Arbeitsmittel. Wichtiger ist es m.E. Objekte leicht zu definieren und den Export/Import und Abfrage der Daten zu regeln. Die Binding-Technologie ist sicher sehr interessant und hilfreich, aber eben nur ein Punkt bei der Arbeit mit Objekten. Wie komfortabel die Objekte aber erzeugt und mit Daten gefüllt werden können, ist aber damit ja noch nicht gelöst. Ebenso nicht die Abfrage von Daten (Selektionen aus Listen). Da wäre ein "LINQ to Objects" für Delphi sicher noch interessanter.
Zitat:
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.
Ich weiß, mein Beitrag ist etwas unscharf gefasst - entspricht aber damit meinem derzeitigen Kenntnisstand des Themas Außerdem hast Du eine Antwort verdient (auch wenn es eben erst mal nur solch eine ist) Ich bin gerade auch dabei, ein Projekt komplett mit Objekten aufzubauen und eine Anbindung an Ableitungen von Standardkomponenten zu regeln. Der Nachteil ist sicher, dass den Standardcontrols zusätzliche Eigenschaften und Methoden verpasst werden müssen und die Datenobjekte von einem Basisobjekt abgeleitet werden müssen. Andererseits passt so alles sehr gut zusammen und kann aufeinander abgestimmt werden. Einen wirklichen Nachteil kann ich darin letztlich auch nicht erkennen, zumal auch die Datenspeicherung in dem Basisobjekt definiert ist. PS: Wie (ungefähr) regelt eigentlich die IDE selbst die Bearbeitung von Objekteigenschaften im Objektinspektor? Da muss ja eine ähnliche Bindung realisiert sein? Kann das jemand grob erklären? |
Zitat |
Delphi 10.1 Berlin Enterprise |
#3
sorgst Du gerade dafür, dass es weniger Argumente für einen Umstieg von Delphi auf .net gibt???
Deshalb baue ich viele Sachen nach bzw versuche es. Und bei den meisten Dingen gelingt das auch (siehe meine anderen Posts in jüngerer Zeit). Liegt daran, dass Delphi XE die erste Version ist, die nicht alle Nase lang die Generics (unerlässlich für das meiste, was ich veranstalte) auseinander fliegen lässt beim Kompilieren und halt die erweiterte RTTI seit 2010. Das DataBinding war ja immer ein Pro-Net-Argument. Jetzt würde dann eine Einrichtung der Bindings wohl nur noch in der Delphi-IDE fehlen?
Nach meiner (derzeitigen) Einschätzung wird die Binding-Option jedoch auch überbewertet, oder ist zumindest allein noch kein sehr wirksames Arbeitsmittel.
Wichtiger ist es m.E. Objekte leicht zu definieren und den Export/Import und Abfrage der Daten zu regeln.
Die Binding-Technologie ist sicher sehr interessant und hilfreich, aber eben nur ein Punkt bei der Arbeit mit Objekten. Wie komfortabel die Objekte aber erzeugt und mit Daten gefüllt werden können, ist aber damit ja noch nicht gelöst. Ebenso nicht die Abfrage von Daten (Selektionen aus Listen). Da wäre ein "LINQ to Objects" für Delphi sicher noch interessanter. Was darfs noch sein? "LINQ to XML" vielleicht? Ein erster Prototyp von "LINQ to SQL" (bzw eher ne Art Entity Framework "light" Nachbau) ist auch schon in der Mache, da haperts aber für die Generierung von vernünftigen SQL Statements (abgesehen von nem full table select) noch etwas. Vor allem Mangels ExpressionTrees der delegates, die in .Net "einfach" in SQL umgebaut werden. Bisher gehen einfache Abfragen aber komplette Codegenerierung für Datenklassen über Metadaten aus nem SQL Server Datenbank Schema ist bereits implementiert.
Zitat:
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.
Ich weiß, mein Beitrag ist etwas unscharf gefasst - entspricht aber damit meinem derzeitigen Kenntnisstand des Themas Außerdem hast Du eine Antwort verdient (auch wenn es eben erst mal nur solch eine ist)
PS: Wie (ungefähr) regelt eigentlich die IDE selbst die Bearbeitung von Objekteigenschaften im Objektinspektor? Da muss ja eine ähnliche Bindung realisiert sein? Kann das jemand grob erklären?
P.S.: Die von mir angeschnittenen Projekte und noch einiges andere sind Teil eines größeren MVVM Prototypens - natürlich auch fein bei .Net (inklusive diversen Codeplex Projekten) gespickt - die liefern halt gute Vorlagen.
Stefan
Geändert von Stevie (29. Apr 2011 um 18:49 Uhr) |
Zitat |
Delphi 2010 Professional |
#4
da haperts aber für die Generierung von vernünftigen SQL Statements (abgesehen von nem full table select) noch etwas. Vor allem Mangels ExpressionTrees der delegates, die in .Net "einfach" in SQL umgebaut werden. Bisher gehen einfache Abfragen aber komplette Codegenerierung für Datenklassen über Metadaten aus nem SQL Server Datenbank Schema ist bereits implementiert.
Wenn du wissen willst, was wie gefiltert wird, könntest du IDispatch-Implementierungen bauen, die vom User als OleVariant benutzt werden (aka C# dynamic). Auf die Art solltest du alle Calls abgreifen können. Du musst halt immer nach jeder Operation ein neues IDispatch zurückgegeben, welches alle vorher aufgezeichneten Operation kennt. Habe Delphis komische Syntax für Lambdas nicht im Kopf, aber stelle dir eine extension method vor, die Delphis Gegenstück zu Func<OleVariant, Boolean> schluckt. Der OleVariant, der dort übergeben wird, wäre also nicht das jeweilige Item der Liste. Nope, du würdest die anonym. Method sofort ausführen, da DEIN OleVariant ja alle auf ihn angewandten Operationen aufzeichnet.
Delphi-Quellcode:
YourTable.Where(function(row : OleVariant) : Boolean;
begin exit((row.SomeColumn = 1) and (row.SomeOtherColumn <> 'Test' ); end).Select(procedure(const row, result : OleVariant); begin result.ResultField1 := row.Field1; result.Abc := 1; result.Def := row.Field2; end);
Code:
Die Where Clause:
SELECT t.Field2 as ResultField1, 1 as Abc, t.Field2 as Def
from ... where t.SomeColumn = 1 and t.SomeOtherColumn <> 'Test' OleVariant (OV) 1: Ergebnis von row.SomeColumn OV 2: Ergebnis von OV1 = 1 OV 3: Ergebnis von row.SomeOtherColumn OV 4: Ergebnis von OV3 <> 'Test' OV 5: Ergebnis von OV2 and OV4 Sollte IMHO möglich sein. Natürlich gehen auf der Basis noch ganz andere Sachen, für die in .Net ExpressionTrees benutzt werden. (Mocking Setups!) Aber mache all das nur solange es dir wirklich Spass macht. Du kannst Delphi nicht in etwas Modernes verwandeln. Jede dieser Lösung wird einen schalen Beigeschmack haben, und dem Dev, der es einsetzt wird dabei ständig ein kleines Männchen dezent mit einem Holzhammer von innen an die Schädeldecke schlagen um dabei unaufhörlich "HACK! HACK!" brüllen....
Robert Giesecke
Geändert von Elvis (29. Apr 2011 um 23:08 Uhr) |
Zitat |
Delphi 10.1 Berlin Enterprise |
#5
da haperts aber für die Generierung von vernünftigen SQL Statements (abgesehen von nem full table select) noch etwas. Vor allem Mangels ExpressionTrees der delegates, die in .Net "einfach" in SQL umgebaut werden. Bisher gehen einfache Abfragen aber komplette Codegenerierung für Datenklassen über Metadaten aus nem SQL Server Datenbank Schema ist bereits implementiert.
Wenn du wissen willst, was wie gefiltert wird, könntest du IDispatch-Implementierungen bauen, die vom User als OleVariant benutzt werden (aka C# dynamic).
Auf die Art solltest du alle Calls abgreifen können. Du musst halt immer nach jeder Operation ein neues IDispatch zurückgegeben, welches alle vorher aufgezeichneten Operation kennt. Habe Delphis komische Syntax für Lambdas nicht im Kopf, aber stelle dir eine extension method vor, die Delphis Gegenstück zu Func<OleVariant, Boolean> schluckt. Der OleVariant, der dort übergeben wird, wäre also nicht das jeweilige Item der Liste. Nope, du würdest die anonym. Method sofort ausführen, da DEIN OleVariant ja alle auf ihn angewandten Operationen aufzeichnet.
Delphi-Quellcode:
OleVariant (OV) 1: Ergebnis von row.SomeColumn
YourTable.Where(function(row : OleVariant) : Boolean;
begin exit((row.SomeColumn = 1) and (row.SomeOtherColumn <> 'Test' ); end); OV 2: Ergebnis von OV1 = 1 OV 3: Ergebnis von row.SomeOtherColumn OV 4: Ergebnis von OV3 <> 'Test' OV 5: Ergebnis von OV2 and OV4 Sollte IMHO möglich sein. Natürlich gehen auf der Basis noch ganz andere Sachen, für die in .Net ExpressionTrees benutzt werden. (Mocking Setups!) Edit: Hast du eventuell ein kleines Beispiel, ob und wie ich die Operatoren für nen OleVariant überschreiben kann? Edit2: Hab was gefunden in der Unit VarCmplx.pas (die seh ich heute zum ersten mal in meinem Leben ) Aber mache all das nur solange es dir wirklich Spass macht. Du kannst Delphi nicht in etwas Modernes verwandeln.
Jede dieser Lösung wird einen schalen Beigeschmack haben, und dem Dev, der es einsetzt wird dabei ständig ein kleines Männchen dezent mit einem Holzhammer von innen an die Schädeldecke schlagen um dabei unaufhörlich "HACK! HACK!" brüllen....
Stefan
Geändert von Stevie (29. Apr 2011 um 23:40 Uhr) |
Zitat |
Delphi 2006 Professional |
#6
Hi Stevie,
ich find's prima, dass sich jemand drum kümmert, Delphi ein wenig moderner zu machen und innovative Konzepte, wie z. B. das WPF-Databinding von .NET in Abwandlung nach Delphi zu portieren (INotifyPropertyChanged find' ich übrigens klasse , obwohl's manchmal nervt). Und: Ja, .NET-Entwickler haben (manchmal auch) innovative Konzepte, die sich durchsetzen! Wieso also diese nicht umsetzen, wenn man die Möglichkeiten hat? Seinerzeit wurden die Zeos-Komponenten ja auch aus der Java-Welt portiert ... Und: Ja, D7 ist Steinzeit - Ich merk's jedes Mal wieder! So! Mehr sach' ich nicht dazu Grüße und weiter so M.
Michael Seeger
|
Zitat |
Delphi 10.1 Berlin Enterprise |
#7
Ich hab den Data Bindings nun ein Designtime Package verpasst, so dass man sie mit dem Objektinspector editieren kann.
Stefan
Geändert von Stevie (18. Jun 2011 um 19:30 Uhr) |
Zitat |
4. Mai 2011, 07:47
Dieses Thema wurde am "04. May 2011, 07:47 Uhr" von "mkinzler" aus dem Forum "Object-Pascal / Delphi-Language" in das Forum "Software-Projekte der Mitglieder" verschoben.
|
Delphi XE8 Enterprise |
#9
Hallo Stevie,
ich probiere gerade Dein Data Bindings aus. stahli hat mich hier darauf aufmerksam gemacht, dass das Data Bindding vielleicht etwas für mich wäre. Ich habe aber ein Problem mit der folgenden Procedure :
Delphi-Quellcode:
Und zwar mit der Zeile AWriter.WriteProperties(LBinding);
. Hier spuckt mir der Compiler die Fehlermeldung [DCC Error] System.Bindings.pas(684): E2362 Cannot access protected symbol TWriter.WriteProperties aus. Diese Meldung bekomme ich, wenn ich das Designtime-Package unter Delphi 2010 lade und installieren möchte und wenn ich die Datei System.Bindings.pas manuell einbinde. Mache ich hier etwas falsch oder habe ich etwas vergessen.
procedure TBindingGroup.WriteBindings(AWriter: TWriter);
var LAncestor: TPersistent; LBinding: TBindingBase; begin LAncestor := AWriter.Ancestor; AWriter.Ancestor := nil; try TWriterHack(AWriter).WriteValue(vaCollection); for LBinding in FBindings do begin AWriter.WriteListBegin(); AWriter.WriteProperties(LBinding); AWriter.WriteListEnd(); end; AWriter.WriteListEnd(); finally AWriter.Ancestor := LAncestor; end; end;
Rolf Warnecke
|
Zitat |
Delphi 10.1 Berlin Enterprise |
#10
Die Version im Post oben ist etwas veraltet Hol dir einfach die aktuellste Version aus dem svn. Ich werde aber im Laufe des Wochenendes eine aktuelle Version hier hochladen.
Edit: Aktuelle Archive hochgeladen
Stefan
Geändert von Stevie (18. Jun 2011 um 19:30 Uhr) |
Zitat |
Ansicht |
Linear-Darstellung |
Zur Hybrid-Darstellung wechseln |
Zur Baum-Darstellung wechseln |
ForumregelnEs 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
|
|
Nützliche Links |
Heutige Beiträge |
Sitemap |
Suchen |
Code-Library |
Wer ist online |
Alle Foren als gelesen markieren |
Gehe zu... |
LinkBack |
LinkBack URL |
About LinkBacks |