Silverlight und WPF Bindings – leicht missbraucht?

Manchmal ist es doch überraschend, an welchen Stellen Bindings verwendet werden können. So handelt es sich bei der Width Eigenschaft einer ColumnDefinition im Grid um eine DependencyProperty, die man dynamisch binden kann. Mit ganz witzigen Effekten, wie ich nun beschreiben möchte – hier eine schnelle Lösung, elegant wird es erst bei Einsatz von ValueConvertern.

In einem privaten Projekt werden zu einer Buslinie die Haltestellen und deren Abstand eingegeben. Ich wollte nun so einen so schönen Zeitstrahl haben, wie man ihn hier in der Gegend auf den ausgegängten Busfahrplänen findet.

Zeitstrahl

Im Programm sind bereits die Haltestelle als Objekte (Knotenpunkt) vorhanden, die INotifyPropertyChanged implementieren. Eine dieser Eigenschaften ist die Fahrzeit zwischen zwei Haltestellen in Minuten. Ich habe nun eine neue Eigenschaft der Art GridLength hinzugefügt – wie gesagt, ein ValueConverter würde eine sauberere Trennung der Softwareschichten bedeuten. Diese Eigenschaft meldet entweder als Breite 0 Pixel (wenn die Fahrzeit 0 ist) oder die (positive) Fahrzeit mit dem GridUnitType.Star – entsprechend der XAML Texteingabe 1*, 2*, 3* etc.

Ein UserControl (FahrplanAnzeigen) wird nun an eine Liste von Haltestellen gebunden – die Liste selbst wird nur sehr faul überwacht und Änderungen führen zu einem vollständigen Neuaufbau: in einem echten Stück Software würde man das sicher optimieren. Für jede Haltestelle wird eine ColumnDefinition erzeugt und die beschrieben Eigenschaft über die BindingOperations an die Width gebunden.

Silverlight sorgt nun mit ein bisschen XAML Definition automatisch dafür, dass die Haltepunkte proportional über den Zeitstrahl verteilt werden. Reaktionen bei Änderung der Größe sind nicht notwendig. Mehr noch: ändert man die Fahrzeit eines Haltepunktes im Formular, so passt sich der Zeitstrahl über das Binding ohne irgendwelche weitere Interaktion des Programms an. Das sieht schon recht witzig aus – vor allem für ein geschenktes Feature.

Happy Coding

Jochen

Silverlight 5 vs. ANSI (Windows-1252) Encoding

Für eine kleine Auftragsarbeit im Familienkreis will ich mit Silverlight 5 eine XML Datei lesen und schreiben – als Ersatz für den Schedule Wizzard des Bus Simulator 2012 Spiels, der unter Windows XP nicht richtig funktioniert. Wenn man allerdings versucht, eine lokale Datenstrom in ein XDocument zu laden oder ein solches zu schreiben, so gibt es eine kleine Überraschung: von Hause aus unterstützt Silverlight das Windows Encoding und esp. die 8-Bit Windows Codepage 1252 nicht.

Tatsächlich sind die hier verwendeten Dateien genau betrachtet auch unzulässig, allerdings drücke ich doch allzu gerne beide Auge zu, da ich selbst schon oft in diese Falle gelaufen bin. Hier werden XML Dateien verwendet, in deren ?xml Instruktion kein Encoding angegeben ist, die aber tatsächlich in der 8-Bit Codepage 1252 von Windows verfasst sind. Das heißt zum Beispiel, dass das €uro Symbol mit dem Wert 128 hinterlegt ist – entsprechend auch die Umlaute. Silverlight wird eine solche Datei mit 1252er Umlauten erst gar nicht laden und beim Speichern das UTF-8 Format verwenden – inklusive einem korrekten Encoding in der ?xml Instruktion. Damit kommt aber das Spiel nicht zurrecht.

Ich habe ohne langes Suchen eine kleine eigene Implementierung verwendet, die ich hier kurz vorstellen will – recht trivial und meiner Ansicht nach dem Zweck angemessen. Die Implementierung selbst findet man in diesem Archiv.

Erst einmal habe ich mir (mit Hilfe vom richtigen .NET und dem Encoding.GetEncoding(1252)) eine Abbildungsvorschrift zwischen Windows 1252 Bytes und 16-Bit UNICODE Zeichen erstellt und diese in Silverlight eingebunden (man siehe hierzu Fahrplan.cs im Archiv). Beim Laden wird die gesamte Datei (die für die konkrete Anwendung extrem klein ist) binär als byte-Feld unverändert eingelesen. Mit Hilfe der Abbildungsvorschrift wird pro byte das zugehörige UNICODE Zeichen als char erstellt, i.e. 128 gibt € und so weiter. Dieses char-Feld bildet dann eine Zeichenkette via new string(feld) und über die System.IO.StringReader Klasse kann das XDocument klaglos erstellt werden.

Das Speichern ist ein wenig trickreicher. Hier wird das XDocument erst einmal im Speicher mit Hilfe einer MemoryStream Instanz gespeichert. Der Einsatz von XmlWriter und XmlWriterSettings erlaubt es dabei, das Encoding auf Encoding.Unicode zu fixieren. Die UNICODE char Zeichen im Speicher werden nun alle über die Abbildungsvorschrift in Windows 1252 Bytes umgesetzt – ist dies nicht möglich, wird ein ? als Ersetzungszeichen verwendet. Der erste char wird dabei ignoriert, hier würde in der Datei die BOM (Byte-Order-Mark) stehen, die eine Identifikation des Dateiinhaltes als UNICODE ermöglichen soll. Somit wird aus einem ursprünglichen UNICODE char-Feld einfach ein byte-Feld in Windows 1252 Codierung, das dann binär in eine lokale Datei gespeichert wird. Aber Vorsicht: hier ist in der ?xml Instruktion immer noch das Encoding als UTF-16 festgelegt, was zumindest von der Originalformatierung abweicht. Im Programmcode werden die entsprechenden Bytes beim Schreiben der Datei entfernt.

Etwas ekelig, aber nicht so schlimm, wie es sich anhört. Der eigentliche Code zum Lesen und Schreiben sind wenige Zeile und die lange Abbildungsvorschrift kann man sich leicht automatisiert erstellen. Besser als ein eigenes Encoding, oder?

Soweit zum Thema: Was Sie eigentlich nie über Silverlight wissen wollten aber gezwungen waren sich damit auseinander zu setzen 🙂

Jochen