Anbei ein Dingens, in das man verschiedenste Daten hineinschmeißen kann, und es konvertiert die Daten nach String und hängt sie aneinander - also es erzeugt einen Datasstring.
Und rückwärts kann es das auch: schmeiß ihm einen Datastring hin, dann restauriert es alle Daten, von denen dieser String genommen wurde.
Sehr praktisch, wenn man WindowState, Bounds, Checkbox-States, Splitter-Distances, Spaltenbreiten und ähnliches speichern und restaurieren will - also den Zustand eines Forms zum Zeitpunkt des Schließens.
ComplexConverter nutzt dabei 4 Trickse:
Die Benutzung ist recht einfach: Zunächstmal
Das EventArg stellt v.a. 2 Methoden bereit:
My.Settings.save viele viele Textboxen (und folgende)
Hier ein komplexeres Beispiel (aus beiliegender Sample-Solution):
Sieht viel aus, aber tut auch eine Menge, nämlich persistiert werden:
(später Nachtrag:)
Vergleiche mit Serialisierung
Zunächst einmal: ComplexConverter befasst sich mit komplexen, nicht serialisierbaren Objekten (hier: ein Form).
Man müsste also Custom-Serialisierung anwenden, d.h. das Objekt serialisierbar machen.
Das bedeutet vor allem, man muss Speichern und Laden selbst in die Hand nehmen.
Hier tritt nun der prinzipielle Unterschied auf, dass bei CustomSerialisierung Speichern und Laden in zwei getrennten Methoden zu behandeln ist:
ComplexConverter muss nur einmal hinlangen, in seinem bidirektionalem Event, und auch nur quasi "mit dem Finger draufzeigen": e.ConvertValue(Me.Bounds) - String-Schlüssel, TypAngabe, Casts sind nicht erforderlich.
Ein weiterer prinzipieller Unterschied ist, dass Deserialisierung immer ein neues Objekt erzeugt.
Also Deserialisierung ist ausserstande, ein bestehendes Objekt anhand geladener Daten zu rekonfigurieren (was ComplexConverter eben macht).
Daher muss Serialisierung, um zu Rekonfigurieren, jedes Daten-Item noch ein drittes Mal anfassen, nämlich vom deserialisierten (neu erzeugten) Objekt sind die Daten hinüber zu kopieren ins zu rekonfigurierende. Anschließend verfällt das deserialisierte Objekt - es wird nicht mehr benötigt.
In seinem Beispiel umgeht Hal2000 diesen dritten Schritt, indem er das AnwendungsFramework deaktiviert. So kann er das normale Erstellen des MainForms entfallen lassen und das Deserialisat gleich als MainForm verwenden.
Schlauer Trick, die Re-Konfiguration in diesem Falle zu umgehen: Im Hal2000-Beispiel Zeilen#7-#23
Ein anderer Serialisierungs-Weg wäre natürlich, besondere serialisierbare Objekte zu schaffen.
Dabei muss man aber ebenfalls jedes Daten-Item drei mal anfassen:
So, und nun distanziere ich mich von der im folgenden aufgeführten Troll-Diskussion, die imo in einem Tutorial nichts verloren hat: Ein Tutorial sollte ein Konzept vorstellen und erläutern. Dazu sind Anmerkungen willkommen, und auch "gute Fragen", die zu weiteren Erläuterungen und größerer Klarheit führen.
Ein Tutorial sollte sich aber nicht mit eingeworfenen unhaltbaren Behauptungen beschäftigen müssen.
(my 5 ct)
Und rückwärts kann es das auch: schmeiß ihm einen Datastring hin, dann restauriert es alle Daten, von denen dieser String genommen wurde.
Sehr praktisch, wenn man WindowState, Bounds, Checkbox-States, Splitter-Distances, Spaltenbreiten und ähnliches speichern und restaurieren will - also den Zustand eines Forms zum Zeitpunkt des Schließens.
ComplexConverter nutzt dabei 4 Trickse:
- es holt sich fürs zu speichernde Objekt einen TypConverter. Also es kann alles persistieren, wofür es einen TypConverter gibt, der nach String konvertieren kann.
- Es feuert ein Event, in dem die Konvertierung erfolgt, und zwar in beide Richtungen . Weil dank des
ByRef
-Features kann eine übergebene Variable sowohl ausgelesen werden als auch neu gesetzt. - Dank des doppel-funktionalen Events muss er nicht merken, welcher Wert wo hingehört, denn der Code der die Werte liest ist der Code, der die Werte schreibt, und damit ist ohne jede Zusatzinformation sichergestellt, dass jeder Wert genau dahin zurück kommt, wo er hergenommen wurde .
- Rollback: Bevor ComplexConverter die Werte schreibt, feuert er sein Event erstmal im Lese-Modus, und liest den Status Quo in einen Backup-String. Schlägt anschließend das Restaurieren fehl (etwa neue zu persistierende Items wurden in den Event-Code aufgenommen), so werden alle Werte aus dem Backup wieder so gemacht, wies vorher war.
Die Benutzung ist recht einfach: Zunächstmal
Withevents
deklarieren und instanzieren, und das Event behandeln.Das EventArg stellt v.a. 2 Methoden bereit:
ConvertValue(ByRef item As Object)
, um einen Wert zu konvertieren.ConvertList(Of T As New)(liste As T)
ConvertList muss den Typ kennen und erzeugen können, damit eine Liste restauriert werden kann. Es legt dann beim Restoren zunächst mal unitinialisierte Items an (etwa leere Treenodes in einer TreenodeCollection), und im weiteren können ja mit.ConvertValue()
auch die Eigenschaften dieser Nodes restauriert werden.
My.Settings.save viele viele Textboxen (und folgende)
Hier ein komplexeres Beispiel (aus beiliegender Sample-Solution):
VB.NET-Quellcode
- Public Class Form1
- Private WithEvents _Memory As New ComplexConverter
- Private Sub Form1_Load(ByVal sender As Object, ByVal e As EventArgs) Handles MyBase.Load
- ' My.Settings.Memory ist in den Anwendungseinstellungen eingerichtet
- _Memory.ApplyDataString(My.Settings.Memory)
- End Sub
- Private Sub Form1_FormClosing(ByVal sender As Object, ByVal e As FormClosingEventArgs) Handles Me.FormClosing
- My.Settings.Memory = _Memory.CreateDataString()
- End Sub
- ''' <summary>komplexe Konvertierung für dieses Form</summary>
- Private Sub _Memory_Convert(ByVal sender As Object, ByVal e As ComplexConverter.EventArg) Handles _Memory.Convert
- ' Form-Properties konvertieren
- e.ConvertValue(Me.WindowState)
- e.ConvertValue(Me.Bounds)
- ' Treeview konvertieren
- EnumerateNodes(TreeView1.Nodes, e)
- ' ListView-Spaltenbreiten konvertieren
- For Each Col As ColumnHeader In Me.ListView1.Columns
- e.ConvertValue(Col.Width)
- Next
- ' ListView-Items konvertieren
- e.ConvertList(Of ListViewItem)(ListView1.Items)
- For Each LVI As ListViewItem In ListView1.Items
- ' ListView-SubItems konvertieren
- e.ConvertList(Of ListViewItem.ListViewSubItem)(LVI.SubItems)
- For Each LVSI As ListViewItem.ListViewSubItem In LVI.SubItems
- e.ConvertValue(LVSI.Text)
- Next
- Next
- End Sub
- Private Sub EnumerateNodes(ByVal Nodes As TreeNodeCollection, ByVal e As ComplexConverter.EventArg)
- e.ConvertList(Of TreeNode)(Nodes)
- For Each Nd As TreeNode In Nodes
- e.ConvertValue(Nd.Text)
- EnumerateNodes(Nd.Nodes, e)
- ' Workaround, da Treenode.IsExpanded readonly, also nicht direkt restaurierbar
- If e.IsStoring Then
- e.ConvertValue(Nd.IsExpanded)
- Else
- If e.GetValue(Of Boolean)() Then Nd.Expand()
- End If
- Next
- End Sub
Sieht viel aus, aber tut auch eine Menge, nämlich persistiert werden:
- Form.WindowState
- Form.Bounds
- rekursiv alle Treenodes eines Treeviews, und zwar mit
Treenode.Text
undTreenode.IsExpanded
also der Treeview ist hinterher wieder genauso aufgeklappt wie er verlassen wurde - alle Spaltenbreiten einer Listview
- alle ListviewItem, inklusive ihrer SubItems
(später Nachtrag:)
Vergleiche mit Serialisierung
Zunächst einmal: ComplexConverter befasst sich mit komplexen, nicht serialisierbaren Objekten (hier: ein Form).
Man müsste also Custom-Serialisierung anwenden, d.h. das Objekt serialisierbar machen.
Das bedeutet vor allem, man muss Speichern und Laden selbst in die Hand nehmen.
Hier tritt nun der prinzipielle Unterschied auf, dass bei CustomSerialisierung Speichern und Laden in zwei getrennten Methoden zu behandeln ist:
- Speichern: hierfür ist der
ISerializable.GetObjectData()
- Schnittstellen-Member zu implementieren: Im 1.Spoiler des Hal2000-Beispiels, zeilen#34-#36 - Laden: hierfür ist ein besonderer Konstruktor zu implementieren: Im Hal-Beispiel zeilen#42-45
ComplexConverter muss nur einmal hinlangen, in seinem bidirektionalem Event, und auch nur quasi "mit dem Finger draufzeigen": e.ConvertValue(Me.Bounds) - String-Schlüssel, TypAngabe, Casts sind nicht erforderlich.
Ein weiterer prinzipieller Unterschied ist, dass Deserialisierung immer ein neues Objekt erzeugt.
Also Deserialisierung ist ausserstande, ein bestehendes Objekt anhand geladener Daten zu rekonfigurieren (was ComplexConverter eben macht).
Daher muss Serialisierung, um zu Rekonfigurieren, jedes Daten-Item noch ein drittes Mal anfassen, nämlich vom deserialisierten (neu erzeugten) Objekt sind die Daten hinüber zu kopieren ins zu rekonfigurierende. Anschließend verfällt das deserialisierte Objekt - es wird nicht mehr benötigt.
In seinem Beispiel umgeht Hal2000 diesen dritten Schritt, indem er das AnwendungsFramework deaktiviert. So kann er das normale Erstellen des MainForms entfallen lassen und das Deserialisat gleich als MainForm verwenden.
Schlauer Trick, die Re-Konfiguration in diesem Falle zu umgehen: Im Hal2000-Beispiel Zeilen#7-#23
Ein anderer Serialisierungs-Weg wäre natürlich, besondere serialisierbare Objekte zu schaffen.
Dabei muss man aber ebenfalls jedes Daten-Item drei mal anfassen:
- Die Hilfsklasse muss geschrieben werden, und pro DatenItem einen geeigneten Platz dafür bereitstellen
- zum Speichern muss man vom Objekt ins HilfsObjekt übertragen
- zum Rekonfigurieren die Werte vom HilfsObjekt ins Ziel-Objekt rück-übertragen
So, und nun distanziere ich mich von der im folgenden aufgeführten Troll-Diskussion, die imo in einem Tutorial nichts verloren hat: Ein Tutorial sollte ein Konzept vorstellen und erläutern. Dazu sind Anmerkungen willkommen, und auch "gute Fragen", die zu weiteren Erläuterungen und größerer Klarheit führen.
Ein Tutorial sollte sich aber nicht mit eingeworfenen unhaltbaren Behauptungen beschäftigen müssen.
(my 5 ct)
Dieser Beitrag wurde bereits 10 mal editiert, zuletzt von „ErfinderDesRades“ ()