WPF Evaluation: Filter und View

Eine Frage ist natrürlich, was (welcher Ausschnitt der gesamten Programmzeitschrift) dem Anwender wie (etwa die Sortierung) angezeigt werden soll. Für das Beispiel habe mich mich entschieden, dass die Anzeige sortiert nach dem Startzeitpunkt und gruppiert erst nach Datum und dann nach der vollen Stunde erfolgen soll. Für die Evaluation sind diese Entscheidungen auch festgelegt, produktiv muss man da natürlich freier sein. Tatsächlich ist die vorhandene Implementierung bei Anzeige der gesamten Programmzeitschrift (mit einigen Tausend Einträgen) auch zu langsam, aber das macht für das Beispiel erst einmal nichts – zudem wie gleich beschrieben oft eine Filterung aktiv sein wird.

Ich habe mich entschieden, die Art der Anzeige in einer eigene Klasse zu implementieren. Diese wird dann an die Liste via XAML angehängt (ItemsSource=“{Binding Source={StaticResource GuideView}}“) – die Anzeigeklasse ist wieder einmal eine globale Ressource. Interessant ist im Beispiel das Zusammenspiel mit der Filterbedingung.

Zur Auswahl von Teilen der Programmzeitschrift gibt es eine Filterklasse, in der die üblichen Parameter gesetzt werden können. Ich habe diese auf der untersten WPF Ebene implementiert, die sowohl dynamische Eigenschaften (DependencyProperty mit Bindungen) als auch Ereignisse (RoutedEvent) erlaubt: dem FrameworkElement. Eine Instanz der Klasse wird als globale Ressource angeboten. Dazu noch ein paar weitere Details, der Filter selbst ist trivial und nicht wirklich der Rede wert.

Die eindeutige Filterinstanz wird nun auf zweierlei Art verwendet. Zum Einen binden sich verschiedene Oberflächenelemente direkt an die relvanten Filtereigenschaften – etwa das Eingabefeld mit dem Freitextsuchfeld. Interessant ist dabei, dass bei den Bindungen (Text=“{Binding Source={StaticResource GuideFilter}, Path=TextFilter, Mode=OneWayToSource}“) alternierend OneWayToSource und TwoWay (SelectedItem=“{Binding Source={StaticResource GuideFilter}, Path=Time, Mode=TwoWay}“) verwendet wird: im Allgemeinen wäre OneWayToSource die korrekte Wahl, ich möchte aber einige (wenige) Auswahlelement direkt mit den Voreinstellungen belegen, die nur der Filter selbst kennen muss.

Zur Reaktion auf Änderungen der Filterbedingun bindet sich der CollectionSourceView an die Ereignisse, die bei Veränderungen von der Filterinstanz aufgerufen werden – die Verbindung erfolgt aus Basis der globalen Ressourcen (<app:ProgramGuideView x:Key=“GuideView“ GuideFilter=“{StaticResource GuideFilter}“ Source=“{Binding Source={StaticResource TheGuide}, Path=Events, IsAsync=true, Mode=OneTime}“ />). Die Sicht auf die Programmzeitschrift aktualisiert sich dann selbst, gefolgt von der entsprechenden Aktualisierung in der Benutzeroberfläche. Tatsächlich habe ich es nicht so richtig hinbekommen, der Sicht zu sagen, sie solle sich einfach erneuern. Daher im Code der kleine Trick mit dem NullFilterEvent und dem m_Changing Flag, die dafür sorgen, dass die Filter Eigenschaft des Views verändert wird, ohne dass dabei eine mehrfache Auswertung stattfindt – was stark auf die Laufzeit geht.

Soweit dazu

Jochen

full.jpg

WPF Evaluation: Spaß mit der StaticMarkupExtension

Sehr interessant fand ich auch die Möglichkeiten des Einsatzes der x:Static Markup Erweiterung, hier am Beispiel der Konfiguration. Die Konfiguration der Anwendung ist .NET konform in den Settings gespeichert und es wird ein Dialog angeboten, der jeden einzelnen Eintrag zum Ändern anbietet – etwa die URL zum SOAP Web Service des VCR.NET Recording Service. Im Beispiel wird der DataContext des Dialogs direkt an diese Einstellungen gebunden (DataContext=“{x:Static app:Properties.Settings.Default}“). Die einzelnen Formularelemente binden sich dann relativ an die Einzeleintragungen (Text=“{Binding Path=ServerUri, Mode=TwoWay, UpdateSourceTrigger=Explicit}“), wobei ich aber bewußt die explizite Aktualisierung verwende, da bei einem Abbruch keine Veränderungen vorgenommen werden sollen. Das geht in der einfachen Implementierung nur teilweise, dazu gleich mehr.

config.jpg

Ergänzend sieht man im Programmcode des Dialogs, wie diese Aktualisierung auch auf einen Rutsch dynamisch vorgenommen wird, ohne dass jedes Formularelement explizit angesprochen werden muss. Konkret bedeutet das, dass ein neuer Konfigurationseintrag letztlich nur in der XAML mit einem Formularelement eingetragen werden muss um gepflegt zu werden: es ist kein zusätzlicher Programmcode notwendig.

So wie es implementiert ist, funktioniert es allerdings nicht wirklich: an einigen Konfigurationseinträgen hängen Prüfungen (ValidationRule) und nur wenn alle Prüfungen Erfolg melden, kann das Formular gespeichert werden. Bei einer Gesamtprüfung werden aber alle erfolgreichen Eingaben bereits in die Konfiguration übertragen und sind damit zumindest für die weitere Nutzung der Anwendung verändert – auf Platte gespeichert werden kann allerdings nur, wenn alles korrekt eingegeben wurde. Ich habe dem zumindest optisch Rechnung getragen, indem die Abbrechen Schaltfläche deaktiviert wird. Ist aber nur halbherzig, da man den Dialog immer noch über das Kreuzchen rechts oben schliessen kann. Wieder für eine produktive Lösung undenkbar, aber das ist hier erst einmal irrelevant.

Eine zweite Anwendung von x:Static findet man in der Anwendung selbst. Hier wird eine Auflistung (TextFilterTypes[] FilterTypes) aller Alternativen einer .NET Enum angeboten, die in einer Auswahlliste angezeigt werden soll (ItemsSource=“{x:Static app:App.FilterTypes}“). Eine Erweiterung um eine Alternative erfordert erst einmal nur die Veränderung der Enum Definition und die Oberfläche folgt dieser sofort. Dass die Beispielanwendung diese Altenativen über WPF Ressourcen auf Texte abbildet ist für das Prinzp ohne Bedeutung – auch wenn das natürlich heißt, dass man doch zumindest einen weiteren Eintrag in den Ressourcen ergänzen muss.

Viel Spaß

Jochen

WPF Evaluation: Die Datenquelle

WPF ist sehr flexibel, wenn es darum geht, Daten an Oberflächenelemente zu binden. Als Datenquelle für mein kleines Beispiel dient der SOAP Web Service des VCR.NET Recording Service (ich denke aber, dass die folgenden Überlegungen für WCF sehr ähnlich sein werden) und dabei vor allem der Abruf der vollständigen Programmzeitschrift für ein Geräteprofil. Ich verwende hier bewusst die gesamte Programmzeitschrift, da es genau die Aufgabe des Beispielprogramms sein soll, diese geeignet zu filtern.

Wenn man eine Anwendung an eine Web Service bindet, so werden eine Reihe von Proxy Klassen im Projekt erzeugt (Unterverzeichnis Web References). Im Beispiel liefert die entscheidende Methode (GetEPGData) ein Feld mit EPGEvent Instanzen. Die Einträge der Programmzeitschrift sind allerdings zur direkten Darstellung wenig geeignet, enthalten sie doch den Startzeitpunkt in GMT / UTC Notation und statt des Endzeitpunktes die Dauer in Sekunden.

Glücklicherweise werden alle Proxy Klassen als partiellen Klassen (partial class) angelegt, so dass es ein Einfaches ist, diese mit geeigneten Eigenschaften und Methoden zu ergänzen. Im Beispiel wird dies hier gezeigt. Die Klasse EPGEvent bietet nun einen einfachen Zugriff auf Start und Ende in der lokalen Zeitzone und einige Hilfseigenschaften, die zur Variation der Darstellung benötigt werden (etwa meldet IsCurrent ob die Sendung jetzt gerade läuft). Zusätzlich wurden die Klassen zur Programmierung von Aufzeichnungen erweitert. Zu einem Auftrag (VCRJob) können nun einfach neue Aufzeichnungen hinzugefügt werden und eine Aufzeichnung (VCRSchedule) erlaubt eine Pflege der Aufzeichnungsparameter (FirstStart als GMT / UTC und Duration in Minuten) elegant in der lokalen Zeitzone über Start- und Endzeitpunkt.

Interessanter Weise macht der Code in dieser Datei auch einen erheblichen Anteil am Gesamtcode und der Kommunikation mit VCR.NET aus. Für das Laden der Programmzeitschrift wurde ein asynchroner Mechanismus gewählt, der ideal zu den asynchronen dynamischen Werten von WPF (Binding.IsAsync) passt – dazu in einem anderen Artikel. Beim Erzeugen der ProgramGuide Hilfsklasse wird VCR.NET asynchron kontaktiert, ein Abruf der Werte ist erst möglich, wenn eine Antwort bereit steht. Da die Instanz der Hilfsklasse als WPF Ressource angelegt wird (auch dazu an anderer Stelle mehr) wird dieser Vorgang sehr früh beim Starten der Anwendung angeworfen und kann parallel zum Aufbau des Hauptfensters abgearbeitet werden.

Auf technische Details der Implementierung will ich hier nicht eingehen – Thema ist ja WPF. Erwähnt werden sollte allerdings eine wesentliche Unschönheit in dem vorhandenen Code: wenn die SOAP URL des Servers nicht stimmt, so passiert erst einmal gar nichts. Hier muss der Anwender über die Konfiguration eine korrekte Adresse eintragen und die Anwendung neu starten. Für eine produktive Anwendung ein Unding, aber für das Ziel dieser Evaluation völlig ausreichend.

So long

Jochen

Hineintapsen in WPF…

Schon seit lange schiebe ich es vor mir her, mich einmal etwas mit dem .NET Windows Presentation Framework (WPF) auseinander zu setzen. Um zumindestens einmal ein Gefühl dafür zu bekommen, wie dieses tickt, habe ich mir als Beispiel einfach einmal die Programmzeitschrift des VCR.NET Recording Service herausgegriffen und ein wenig gebastelt. Das Ergebis ist weder eine fertige Anwendung noch ein Muster für WPF Programmierung – beides ganz sicher nicht! Ich habe allerdings versucht, die wichtigsten Aspekte des WPF wenigstens einmal anzufassen – wobei alles rund um ein echtes Design nicht betrachtet wurde. Im Zentrum geht es dabei um die dynamische Darstellung von Daten mit dem Versuch, echte (C# et al) Programmlogik so wenig wie möglich zu nutzen und so viel wie möglich auf die Dynamik von WPF und XAML zurückzugreifen.

Wie dem auch sein: ich habe mal eine Version hochgeladen – dito der aktuelle Quelltext. In den nächsten Tagen wird es hier im BLog einige Kommentare dazu geben, wobei ich auch für mich nochmal durch das Gelernte gehen werden (kann also sein, dass sich Quellen und Anwendung dabei noch verändern). Ich werde versuchen, mich jeweils auf einzelne Themen zu beschränken und dabei auch erläutern, was wie und warum geht und was nicht (ja, irgendwie habe ich nicht wirklich alles ans Laufen bekommen). Vielleicht hilft das dem einen oder anderen WPF Einsteiger, auf die richtigen Ideen zu kommen – immer vor dem Hintergrund, dass ich selbst natürlich noch blutiger Laie bin.

Viel Spaß

Jochen

pic.jpg