WPF Evaluation: Ressourcen

Ich stelle mir XAML Ressourcen als eine Art statische Felder vor, die beim Starten der Anwendung automatisch initialisiert werden. Natürlich ist dieses Bild etwas vereinfacht, da es Konzepte wie die hierarchische Sichtbarkeit nicht wirklich wiedergeben kann, aber für einen Einstieg in die Thematik sollte es reichen.

Eine der wohl üblichsten Verwendungsarten von in XAML definierten Ressourcen sind sicherlich Abbildungsvorschriften von Daten auf Oberflächenelementen (e.g. <DataTemplate x:Key=“ShortDescription“>) und Styles (e.g. <Style x:Key=“DescriptionHeader“>), ich fand es im Bereich der Oberflächengestaltung auch recht praktisch, beliebige Strukturen vordefinieren zu können (e.g. <StackPanel x:Key=“LongDescription“>).

Genauso nützlich ist es, globale Instanzen einmalig definieren zu können (e.g. <app:ProgramGuide x:Key=“TheGuide“ />), wobei diese sich oft (wenn die Reihenfolge der Definition es erlaubt) auch gegenseitige referenzieren können (e.g. <app:Filter x:Key=“GuideFilter“ /> und <app:ProgramGuideView x:Key=“GuideView“ GuideFilter=“{StaticResource GuideFilter}“ Source=“{Binding Source={StaticResource TheGuide}, Path=Events, IsAsync=true, Mode=OneTime}“ />).

In der Beispielanwendung werden Ressourcen auch für Zeichenketten wie Formate (e.g <system:String x:Key=“DateTimeFormat“>dd.MM.yyyy HH:mm</system:String>) verwendet, wobei allerdings meiner persönlichen Ansicht nach hier die Grauzone zum Aspekt der Lokalisierung auf die gute alte WinForms Art (.resx) berührt wird. Auch wenn WPF anderen Ideen zur allgemeinen Lokalisierung mit sich bringt, könnte es für diese Fälle (Zeichenketten, die lokalisierbar sein sollten, aber nicht direkt in der Oberfläche erscheinen) eleganter sein, auf RESX und die StaticResource Markup Erweiterung zu setzen.

Als letztes finde ich im Rahmen der Beispielanwendung erwähnenswert, dass auch Felder als XAML Ressourcen angelegt werden können (e.g. <x:Array Type=“system:DateTime“ x:Key=“TimeSelections“> und darin <system:DateTime>00:00</system:DateTime> et al).

Happy Coding

Jochen

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

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

Vista UAC vs. Full Trust

Da ich nun Vista habe, habe ich auch die UAC aktiviert. Mit einem sehr eigenartigen Effekt betreffs der Nutzung von DVB.NET 3.9: ich wollte mal eben so ein kleines Testprogramm machen und starte Visual Studio 2008 als Normalanwender (nicht als Administrator). C# Projekt angelegt und JMS.DVB.Common aus dem GAC als Referenz hinzugefügt. Keine Fehlermeldung, aber die Klassen der Bibliothek stehen mir nicht zur Verfügung. Nachdem ich das Projekt mit einem Strong Name versehen habe, geht es. Was ist passiert? Ich vermute, dass hier AllowPartiallyTrustedCallers(Attribute) zuschlägt, das DVB.NET bewusst nicht verwendet: mein C# Projekt ist ohne Strong Name nicht berechtigt, die Bibliothek zu verwenden. Ich weiss nicht, ob es gegangen wäre, wenn ich VS als Administrator gestartet habe, aber in jedem Fall ein erst mal (zumindest für mich) überraschender Effekt – ohne irgendwelche Fehlermeldungen im VS.

Happy Coding

Jochen