Log-In Dialog mit WinRT

Die neue Windows 8 Oberfläche bietet eine klare Übersicht über Ihre Apps und den darin verwalteten Daten. Heute kommen grösstenteils End-User Applikation in den Genuss dieser Features, welche als „Active Tiles“ bekannt sind. Allerdings ist die übersichtliche Ansichten nicht nur für Home-Kunden interessant, sondern auch fürs Business bietet die Windows 8 Style UI Oberfläche, beispielsweise als Ergänzung zum Intranet, reichen Mehrwert.

Besonders solche Intranet-Portale sind häufig mit einem Zugang gesichert. Für solche Applikationen ist ein Log-In Dialog zwingend, falls nicht direkt mit den Windows Credentials (siehe http://msdn.microsoft.com/en-us/library/windows/apps/windows.system.userprofile.userinformation.aspx) gearbeitet werden kann.

Benutzerinformationen für die App

Um Log-In Informationen einzugeben, hat Microsoft im WinRT Framework den CredentialsPicker-Dialog (http://msdn.microsoft.com/en-us/library/windows/apps/Hh701247) entworfen. Über diesen standardisierten Dialog kann der Benutzer seine Credentials eingeben. Der Dialog sieht wie folgt aus:

Der folgende Code zeigt den Dialog an:

private async Task<bool> OpenLoginDialogAsync()
{
CredentialPickerOptions credPickerOptions = new CredentialPickerOptions();
credPickerOptions.Message = „Bitte geben Sie Ihren Namics LDAP login ein.„;
credPickerOptions.Caption = „Namics Login„;
credPickerOptions.TargetName = „https://know.namics.com„;
credPickerOptions.CredentialSaveOption = CredentialSaveOption.Unselected;
credPickerOptions.AuthenticationProtocol = AuthenticationProtocol.Basic;

var credPickerResults = await CredentialPicker.PickAsync(credPickerOptions);

if (credPickerResults.Credential != null)
{
// TODO: Store retrieved data into variables / business layer / …
return true;
}
return false;
}

Die Überschrift sowie die Log-In-Mitteilung an den Client lassen sich anpassen. Gegenwärtig besteht keine Möglichkeit, weitere Texte wie Labels oder Buttons zu konfigurieren.
Der Dialog wird vom Framework modal dargestellt. Falls ein anderes modales Fenster geöffnet wird (beispielsweise der Konfiguration-Dialog), schliesst sich der CredentialPicker automatisch. Es können ungewollte Side-Effects auftreten, welche auf die modale Darstellung zurück zu führen sind – dagegen hilft meist nur eine Nach-Implementation des Dialoges.

CredentialPicker einbinden

Der CredentialPicker soll beim Start der App eingebunden werden. Damit dies realisiert werden kann, bietet WinRT die Möglichkeit eines Splash-Screens (im Beispiel LogInPage genannt). Diese Page wird direkt im App.xaml.cs registriert:

protected override void OnLaunched(LaunchActivatedEventArgs args)
{
if (args.PreviousExecutionState != ApplicationExecutionState.Running)
{
LogInPage extendedSplash = new LogInPage(
args.SplashScreen,
(args.PreviousExecutionState == ApplicationExecutionState.Terminated));
Window.Current.Content = extendedSplash;
}
// Ensure the current window is active
Window.Current.Activate();
}

Zu beachten ist, dass der SuspensionManager mit dem Frame-Management bewusst weggelassen wurde. Andernfalls wäre es möglich, mit dem Back-Button zurück zur LogInPage zu navigieren. Ebenfalls ist so beim Öffnen der App eine unautorisierte Navigation zur letzten geöffneten Ansicht unterbunden.
Die LogInPage selbst ist von LayoutAwarePage abgeleitet. Die Implementation könnte wie folgt aussehen:

/// <summary>
/// The Splash Screen page that is displayed as first page of the app.
/// </summary>
public sealed partial class LogInPage : LayoutAwarePage
{
private bool _appWasTerminated;
public LogInPage(SplashScreen splash, bool appWasTerminated)
{
InitializeComponent();
Loaded  += OnLogInPageLoaded;
}
private async void OnLogInPageLoaded(object sender, RoutedEventArgs e)
{
if (await OpenLoginDialogAsync() /*  see code above */)
{
// TODO: perform validation of provided login data  here;
//       otherwise force user to re-enter  credentials.
}
else
{
Application.Current.Exit();
}
// TODO: load data here… (e.g.  call await DataContext::LoadDataAsync())
await NavigateToInitialPageAsync();
}
private async Task NavigateToInitialPageAsync()
{
// Create a Frame to act as the  navigation ctx and navigate to the first page
Frame rootFrame = new Frame();
Window.Current.Content = rootFrame;
// Associate the frame with a  SuspensionManager key
SuspensionManager.RegisterFrame(rootFrame, „AppFrame„);
if (_appWasTerminated)
{
// Restore the saved session state  only when appropriate
try
{
await SuspensionManager.RestoreAsync();
}
catch (SuspensionManagerException sme)
{
// Something went wrong restoring  state.
// Assume there is no state and  continue
// TODO: Log error here…
}
}
if (rootFrame.Content == null)
{
   // When the navigation stack isn’t  restored navigate to the first page,
            // configuring the new page by  passing required information as a
            // navigation parameter
if (!rootFrame.Navigate(typeof(ItemsPage), „AllGroups„))
{
throw new NavigationException(typeof(ItemsPage), „AllGroups„);
}
}
    // Ensure the current window is  active
Window.Current.Activate();
}
}
Sobald die LogInPage initialisiert und geladen wurde, stellt WinRT den CredentialsPicker dar. Anschliessend navigiert die Prozedur NavigateToInitialPageAsync zur ItemsPage, welche dann mit den Inhalten aus dem Intranet arbeitet.

Fazit

Das richtige Einbinden des CredentialsPickers gestaltete sich als Herausforderung. Es muss darauf geachtet werden, dass während des Log-Ins keine weiteren modalen Dialoge geöffnet werden, da ansonsten mit ungeahnten Side-Effects zu rechnen ist. Ebenfalls muss der CredentialsPicker auf einer Splash-Screen Page aufgerufen werden, um den Log-In zu erzwingen.
WinRT stellt mit dem CredentialsPicker ein einfaches Werkzeug für die Benutzerdaten-Eingabe zur Verfügung. Aufgrund dessen Einfachheit lässt sich der Dialog nicht allen Wünschen gerecht konfigurieren – vor allem sämtliche Texte sollten sich anpassen lassen.

From DataTrigger to VisualStateManager

Viele WPF-Entwickler kennen die Vorzüge der DataTrigger-Komponente aus der Desktop-Entwicklung. Die DataTrigger-Komponenten werden typischerweise verwendet um auf Änderungen im ViewModel zu reagieren und das User Interface dementsprechend anzupassen.
Das folgende Code-Stück zeigt eine typische Verwendung von DataTriggers in Desktop Applikationen: Das ListItem soll nicht/anders dargestellt werden, falls kein Text vorhanden ist.

In WinRT besteht diese Möglichkeit nicht mehr. Die Trigger-Funktionalitäten, bis auf den EventTrigger, wurden aus dem Framework entfernt.

VisualStateManager als Ersatz für den DataTrigger

VisualStateManger gibt dem Entwickler die Möglichkeit, das Aussehen des Controls auf State-Basis aufzuteilen.
Eine mögliche Implementierung für das gleiche Szenario wie oben könnte wie folgt aussehen (Nachfolgend zum Code werden die einzelnen Komponenten erklärt):

Durch den VisualStateManager gestaltet sich der Code bereits für simple Beispiele um einiges komplexer. Bei grösseren Controls kann durch das Konzept vom VisualStateManager allerdings Komplexität eingespart werden. Weiterführende Informationen: http://msdn.microsoft.com/en-us/library/windows/apps/windows.ui.xaml.visualstatemanager.aspx

VisualManager.VisualStateProperty=“{Binding HasText}“

Das VisualStateManager-Konzept sieht standardmässig keine Anbindung ans ViewModel vor. Üblicherweise wird durch die Status im Code behind navigiert. Z.B:

Das Binding auf das ViewModel ist möglich mithilfe von Attached Properties. Das Kernstück der Implementation ist der ValueChangeHandler, welcher den VisualState des Controles bei jeder Änderungen des gebunden Wertes mitverändert.

VisualStateManager.CustomVisualStateManager

Der ExtendedVisualStateManager wird benötigt, falls auf der State von einem FrameworkElement (z.B. Grid) verändert werden muss. Der standardmässige VisualStateManager funktioniert nur mit Controls.
Der folgende Code ist ein Subset vom ExtendedVisualStateManager von Silverlight:

Fazit

Mit dem VisualStateManager stellt Microsoft ein mächtiges Werkzeug zur Verfügung. Mithilfe von zusätzlichen Klassen ist es möglich den User Interface-Code vom Domainen-Code zu trennen. So finden sich geübte WPF-Programmierer schnell mit dem VisualStateManager zurecht und profitieren bald von den effizienteren und anpassbareren Möglichkeiten von WinRT.

Resource Management mit WinRT

Resource Management ist ein Werkzeug zur Übersetzung von Texten in unterschiedliche Sprachen. Vielfach werden nicht nur GUI-Texte übersetzt, sondern auch Fehler- und Statusmeldungen aus dem Programmcode. Dieser Artikel beschreibt, wie grundsätzlich das Resource-Management für das GUI funktioniert und wie eine Lokalisierung für Code-Komponenten implementiert werden kann.

GUI Lokalisierung

WinRT bietet in Bereich GUI-Lokalisierung bereits heute zahlreiche Features an. So können die Texte im Ordner [WinRT GUI Projekt]\strings\[Sprach-Code] als resm-Files abgelegt werden. Die resm-Files sind im selben Format wie die aus .NET bekannten resx-Files aufgebaut.

Gui Lokalisierung

Eine Referenz über die Sprach-Codes und deren Bedeutung findet sich unter http://msdn.microsoft.com/en-US/library/windows/apps/hh965324.aspx.

Im WPF XAML-Code werden die Texte anschliessend mit x:Uid-Attribut eines beliebigen Controls referenziert. Der Compiler verknüpft anschliessend die Attribute des Controls mit den Texten aus dem referenzierten Sprach-File:

Die Sprachumschaltung zwischen den verschiedenen im \strings\ Ordner abgelegten Sprachen kann über die Systemsteuerung -> Sprache getestet werden.

Die WPF Lokalisierung ist in der MSDN unter http://msdn.microsoft.com/library/windows/apps/Hh965329(v=win.10).aspx detailliert beschreiben.

Der eigene Weg für Code-Lokalisierung

Weniger zahlreich sind die gebotenen Features im Bereich Code-Lokalisierung. Hier muss für eine wartbare Lösung selbst Hand angelegt werden, da, die aus dem .NET bekannten resx-Files, mit dem Compile Time Code Generator den Weg ins WinRT noch nicht gefunden haben. Daher ist hier ein möglichst .NET ähnlicher Weg anzustreben, da anzunehmen ist, dass entsprechende Features zu einem späteren Zeitpunkt noch nachgeliefert werden.

Dabei stellt sich die Frage, warum Code-Fragmente wie beispielsweise Fehlermeldungen überhaupt lokalisiert werden, da die englische Version der Meldung zumeist weit aussagekräftiger ist. Die Antwort darauf ist ähnlich wie bei der Benutzerschnittstelle: Auch Fehlermeldungen bergen das Risiko, bis zum Benutzer vorzudringen. Gerade bei wiederverwendeten Komponenten sollten die Texte also in separaten Files abgelegt werden, da Apps für verschiedene Sprachen und Regionen erstellt werden können.

Der einfachste Weg, die Code-Resourcen zu implementieren ist das Anlegen eines resm-Files und anschliessendes referenzieren über den Resource-Manager. Dies geschieht beispielsweise wie folgt:

Dies hat allerdings zum Nachteil, dass die Resources über Strings lose an die Eigenschaft im  resm-File gebunden werden. Bei einem Rename der Resource von resourceToFind auf resourceToRetrieve muss an allen Stellen im Programm-Code per Search & Replace der String „Resources/resourceToFind“ ersetzt werden. Falls nur eine Stelle vergessen wird, kann das Programm zur Laufzeit abstürzen.

Aus diesem Grunde ist es empfehlenswert, die Resourcen möglichst typisiert und automatisiert aus dem resm-File auszulesen und in ein Code-File als Property zu übertragen. Dies wird mittels Visual Studio .tt (T4) Templates erreicht. Sie finden eine Auflistung der T4-Template-Möglichkeiten unter http://msdn.microsoft.com/en-us/library/vstudio/dd820620.aspx . Das folgende C# Code-Schnipsel des Templates stellt dies sicher:

Die Resourcen werden aus dem angegebenen ReswFile geladen und damit anschliessend die C# Properties generiert. Die Variable resources muss im Vorfeld, wie im Resource-Manager Beispiel oben beschrieben, belegt werden:

Der einzige Nachteil dieser Lösung ist, dass der Entwickler den Template-Run manuell ausführen muss:

Mehr Informationen über das Resource-Management in WinRT können Sie unter http://msdn.microsoft.com/en-us/library/windows/apps/jj552947.aspx nachlesen.

Fazit

WinRT  bietet eine gute Lokalisierung im UI Bereich. Für die Lokalisierung des Programm-Codes muss sich der Entwickler selbst eine Lösung suchen, da die gebotenen Features noch nicht vollständig dem heutigen .NET Standard entsprechen.

Das Beispiel oben hat aufgezeigt, dass das Nachrüsten der Code-Lokalisierung keine grossen Aufwände mit sich bringt. Das Template-File einmalig mit minimalem Aufwand zu erstellen ist sicherlich eine lohnenswerte Investition – in der Hoffnung, dass beim nächsten WinRT Release eine ausgereifte Code-Lokalisierung mitgeliefert wird.

Logging für Windows Store Apps mit WinRT

Logging ist in der Programm-Entwicklung eine der wesentlichsten Methoden zur Fehlerverfolgung. So nimmt die Bedeutung von Logging mit den neuen .NET 4.5 Features (speziell async/await) zu, da im asynchronen Programmcontext bei unsachgemässer Implementation nicht deterministische Fehler „geschluckt“ werden können. Die Nachverfolgung solcher Fehler gestaltet sich, ohne Protokollierung, als äusserst schwierig.

Was WinRT bietet

Als rudimentäres Logging-Feature hat Microsoft zu diesem Zweck in WinRT die Event Tracing for Windows (ETW) Library für das Mobile- und Desktop-App Framework integriert. Dieses entfaltet vor allem für kleinere Entwicklungen seine Stärken, ist allerdings für den Einsatz von grösseren und komplexeren Lösungen nur bedingt geeignet. Es fehlen markante Features wie das Uploaden der Log-Files auf einen zentralen Server. Zusätzlich enthält das von Microsoft gebotene Beispiel Multithreading-Probleme, welche beim Save des Log-Files zum Tragen kommen können.

MetroLog kann Abhilfe schaffen

Das MetroLog Framework als OpenSource Projekt nimmt sich dieser Problematik an. Dieses basiert auf NLog und bietet neben der klassischen Logging-API auch den besagten Upload zum Server an. Weiterhin bietet MetroLog die Features zum Loggen von Daten ins ETW oder in eine lokale SQL-Lite Datenbank. Ein Beispiel zu MetroLog findet sich unter: https://github.com/mbrit/MetroLog/blob/master/Samples/ConsoleSample/Program.cs

Wunsch-Framework: log4net und noch mehr…

Persönliche habe ich eine Vorliebe für das log4net Framework. Dieses wurde bereits bei diversen .NET Projekten eingesetzt und ist entsprechend in der .NET Community etabliert. Ebenfalls setzen bereits bestehende interne Basis-Funktionalitäten log4net ein. Diese Funktionalitäten sollen nach und nach zu WinRT migriert werden, ohne hierbei ein grosses Rewrite zu generieren.

Leider exisitiert im Moment noch keine Implementierung des log4net Frameworks für WinRT. Aus diesem Grunde heisst die Lösung im Moment: Facade-Pattern der GoF. Mit Hilfe dieses Patterns, kann die effektive Logging-Implementierung im Nachhinein ohne Probleme ausgetauscht und so beispielsweise durch log4net Komponenten ersetzt werden.

Die Anwendung der Facade (Klasse Logger<T>) sieht wie folgt aus:

Durch die Facade kann ebenfalls die Logging-API minimiert werden. Im Beispiel werden direkt die Logger<ExampleClass>-Methoden aufgerufen. Diese loggen anschliessend die Mitteilung im Namen der Klasse ExampleClass. Der Compiler sorgt implizit dafür, dass genau eine Instanz des Templates Logger<ExampleClass> existiert, daher muss der Logger nicht explizit in jeder Anwender-Klasse instanziiert werden. Dank der Verwendung des Compiler-Features müssen auch keine Performance-Einbussen hingenommen werden.

Die Implementation der Logger<T> sieht im Prinzip sehr einfach aus:

Fazit

WinRT bietet im Moment erst rudimentäre Logging-Funktionalitäten. Dafür stehen Frameworks wie MetroLog zur Verfügung. Wer sich allerdings an ein zukunftssicheres API binden möchte, ist gut beraten, eine eigene Entwicklung anzustreben.

Im Beispiel oben wurde auf einfache Art und Weise gezeigt, wie das Logging mit WinRT mit den heutigen Mitteln zukunftssicher implementiert werden kann. Sicherlich ist die gebotene Implementierung aus aktueller Sicht noch ausbaufähig, doch bietet sie einen Grundstein für den späteren Einsatz einer mächtigeren Library.