Intranet Trends 2016

Intranet Design & Feature Trends 2016

Anfang jeden Jahres werden die “10 Best Intranets” durch die Norman Nielsen Group innerhalb eines Reports ausgezeichnet. Und jedes Jahr ist dieses eine tolle Quelle für Inspirationen und Trends, welche ich Ihnen in einer kurzen Vorstellung zusammenfassen möchte.

How to Deploy Mobile Channels on SharePoint 2013

SharePoint 2013 bietet neu Mobile Channels an, welche die Device-gerechte Darstellung der Seiten auf verschiedene Mobile-Plattformen gewährleisten. Die Funktionalität sowie die Anwendung der Device Channels kann im Internet beispielsweise unter (http://blogs.msdn.com/b/sharepointdev/archive/2013/05/07/optimizing-sharepoint-2013-websites-for-mobile-devices.aspx) nachgelesen werden.

Dieser Artikel beschreibt, wie Device Channels automatisiert in einem Feature deployed werden können.

Der Ansatz von Microsoft

Als Ausgangslage habe ich das Deployment der Device Channels im SmallBusinessWebsite Feature von Microsoft verwendet. Dieses bietet Deployment für Mobile-Device Channels:

Das Feature.xml im Ordner %ProgramFiles%\Common Files\microsoft shared\Web Server Extensions\15\TEMPLATE\FEATURES\SmallBusinessWebsite verlinkt fürs Deployment der mobilen Master-Pages aufs MobileChannel.xml File.

Das MobileChannel.xml File verlinkt dabei die Mobile-Device Typen (UserAgentSubstrings; z.B. iOS oder $FallbackMobileUserAgents;) mit eigens definierten Namen (ChannelAlias) und füllt diese Informationen in die Device Channels List:

In den SharePoint Settings finden sich diese Informationen unter Device Channels wieder (abweichend zur Xml-Definition oben wird im folgenden Bild der Namics Mobile Channel dargestellt):

Master Pages für Device Channels

Anhand des ChannelAlias werden die hier definierten User Agent Gruppierungen zu Master Pages verknüpft. Die Benutzerschnittstelle dafür sieht wie folgt aus
(abweichend zur Xml-Definition oben wird im folgenden Bild der Namics Mobile Channel dargestellt):

Die Masterpage selbst wird im SmallBusinessWebsite Feature über das gewohnte Module-Deployment in die Masterpage-Gallery eingespielt:

Offen bleibt in der Feature-Definition, wie die Zuweisung der Masterpage auf einen Device Channel vonstattengeht.

Zuweisung der Masterpage auf einen Device Channel

Im SharePoint 2013 geschieht diese Zuweisung der Masterpage auf einen Device Channel über das Mappings-File __DeviceChannelMappings.aspx. Dieses liegt im MasterPage Katalog unter _catalogs/masterpage/ und enthält die Mappings im XML Format:

Beim SmallBusinessWebsite Feature kommt gemäss Feature-Definition der Microsoft.SharePoint.Publishing.SmallBusinessWebsiteFeatureHandler Receiver von Microsoft zum Einsatz, welcher das Mappings-File für die Site anlegt und konfiguriert.

Aufgrund der Komplexität des SmallBusinessWebsiteFeatureHandlers, habe ich hier das PublishingMobile Feature analysiert. Dieses deployed ebenfalls Mobile Channels:

Microsoft verwendet beim PublishingMobile Feature den Microsoft.SharePoint.Publishing.Mobile.FeatureHandler Receiver. Nach einigen Nachforschungen stellte ich fest, dass für die Zuweisung der Masterpage auf einen Device Channel Microsoft-interne (d.h. internal) Klassen wie die Microsoft.SharePoint.Publishing.Mobile.MasterPageMappingsFile zum Einsatz kommen:

Dies erschwert das automatische Deployment der Mobile Channels, da die entsprechenden Features nachgebaut werden müssen. Wesentliche Punkte dabei sind in folgenden Methoden abgedeckt:

  • Microsoft.SharePoint.Publishing.Mobile.MappingsFile<T>.UpdateFile()
    Channel Mappings File in die Master Page Gallery eintragen.
  • Microsoft.SharePoint.Publishing.Mobile.MappingsFile.SaveNoMobileMappingsInfoToRootWeb()
    Root Web Property für die Mobile Channels Deaktivierung (__NoMobileMapping) entfernen.

Putting it all together

Der folgende Code enthält den Receiver als Code-Schnippsel:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
/// <summary>
/// Initializes a new instance of the Microsoft.SharePoint.SPFeatureReceiver class.
/// </summary>
public class MobileChannelReceiver : SPFeatureReceiver
{
    private static readonly SPContentTypeId DeviceChannelMappingsContentTypeId = new SPContentTypeId("0x010100FDA260FD09A244B183A666F2AE2475A6");

    private const string mDeviceChannelFile = "__DeviceChannelMappings.aspx";
    private const string mMasterPageLibPath = "_catalogs/masterpage/";
    private const string mSiteCollectionUrlStart = @"href=""";
    private const string mSiteCollectionUrlReplacement = mSiteCollectionUrlStart + "~sitecollection";

    /// <summary>
    /// Deploys the mobile channel file into the Master Page Gallery. The existing
    /// file will be replaced with the file from the current feature.
    /// </summary>
    /// <param name="properties">An SPFeatureReceiverProperties object that represents the properties of the event.</param>
    public override void FeatureActivated(SPFeatureReceiverProperties properties)
    {
        using (SPWeb web = ((SPSite) properties.Feature.Parent).OpenWeb())
        {
            RegisterChannelFile(
                web,
                properties.Definition.RootDirectory + @"\" + mDeviceChannelFile,
                mDeviceChannelFile);
        }
    }


    private void RegisterChannelFile(SPWeb rootWeb, string fullFilePathOfNewFile, string masterPageFileNameToReplace)
    {
        SPFile fileMasterpage = rootWeb.GetFile(mMasterPageLibPath + masterPageFileNameToReplace);

        if (fileMasterpage != null && fileMasterpage.Exists)
        {
            TryCheckedIn(fileMasterpage);
            TryCheckedOut(fileMasterpage);

            // patch file content with the root web url
            string mobileDeploymentFileRaw = File.ReadAllText(fullFilePathOfNewFile);
            string mobileDeploymentFile = mobileDeploymentFileRaw.Replace(
                mSiteCollectionUrlReplacement,
                mSiteCollectionUrlStart + rootWeb.ServerRelativeUrl);

            // Source: Microsoft.SharePoint.Publishing.Mobile.MappingsFile<T>::UpdateFile()
            // save patched content into server-side file library
            fileMasterpage.SaveBinary(Encoding.Unicode.GetBytes(mobileDeploymentFile));

            // update content type if has been set to a wrong one
            SPListItem item = fileMasterpage.Item;
               
            if (!DeviceChannelMappingsContentTypeId.IsParentOf((SPContentTypeId)item[SPBuiltInFieldId.ContentTypeId]))
            {
                item[SPBuiltInFieldId.ContentTypeId] = DeviceChannelMappingsContentTypeId;
                item.Update();
            }
               
            // accept changes and checkin
            fileMasterpage.CheckIn(string.Empty, SPCheckinType.MajorCheckIn);

            // approve if required
            SPModerationInformation moderationInformation = fileMasterpage.Item.ModerationInformation;

            if (moderationInformation != null && moderationInformation.Status == SPModerationStatusType.Pending)
            {
                fileMasterpage.Approve(string.Empty);
            }

            // activate custom master page channel mappings on web level
            // Source: Microsoft.SharePoint.Publishing.Mobile.MappingsFile<T>::SaveNoMobileMappingsInfoToRootWeb()
            string noMobileMappingKey = GetNoMobileMappingKey(rootWeb);
            if (rootWeb.AllProperties.ContainsKey(noMobileMappingKey))
            {
                rootWeb.AllProperties.Remove(noMobileMappingKey);
                rootWeb.Update();
            }
        }
    }

    private static string GetNoMobileMappingKey(SPWeb web)
    {
        return string.Format(CultureInfo.InvariantCulture, "__NoMobileMapping{0}", new object[]
        {
            web.ID.ToString("N")
        });
    }

    private bool TryCheckedOut(SPFile toCheckOut)
    {
        try
        {
            if (toCheckOut.Level != SPFileLevel.Checkout)
            {
                toCheckOut.CheckOut();
            }
        }
        catch (Exception ex)
        {
            LOG.Current.TraceUnexpected(
                CommonLibErrorCodes.EnsureApplicationFeatureReceiver,
                () => ex.Message);
            return false;
        }
        return true;
    }

    private bool TryCheckedIn(SPFile toCheckIn)
    {
        try
        {
            if (toCheckIn.CheckOutType != SPFile.SPCheckOutType.None)
            {
                toCheckIn.CheckIn("checked in by feature activation");
            }
            return false;
        }
        catch (Exception ex)
        {
            LOG.Current.TraceUnexpected(
                CommonLibErrorCodes.EnsureApplicationFeatureReceiver,
                () => ex.Message);
        }
        return true;
    }
}

Dafür wird das __DeviceChannelMappings.aspx Mappings File wie folgt deployed:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<%@ Reference VirtualPath="~CustomMasterUrlForMapping0" %><%@ Reference VirtualPath="~CustomMasterUrlForMapping1" %><%@ Page Language="C#" Inherits="Microsoft.SharePoint.Publishing.Internal.WebControls.MappingsFileBasePage" %><html xmlns:mso="urn:schemas-microsoft-com:office:office" xmlns:msdt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882"><%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<head>
<!--[if gte mso 9]><SharePoint:CTFieldRefs runat=server Prefix="mso:" FieldList="FileLeafRef"><xml>
<mso:CustomDocumentProperties>
<mso:ContentTypeId msdt:dt="string">0x010100FDA260FD09A244B183A666F2AE2475A6</mso:ContentTypeId>
</mso:CustomDocumentProperties>
</xml></SharePoint:CTFieldRefs><![endif]-->
</head><body><mappings>
  <mapping>
    <channelAlias>NamicsMobileChannel</channelAlias>
    <masterUrl href="~sitecollection/_catalogs/masterpage/SP2013Mobile.Master" token="~sitecollection/_catalogs/masterpage/SP2013Mobile.master" />
  </mapping>
  <defaultChannelMapping>
    <siteMasterUrl token="~sitecollection/_catalogs/masterpage/SP2013.Master" href="~sitecollection/_catalogs/masterpage/SP2013.Master" />
    <systemMasterUrl token="~sitecollection/_catalogs/masterpage/seattle.master" href="~sitecollection/_catalogs/masterpage/seattle.master" />
    <alternateCssUrl token="" href="" />
    <themedCssFolderUrl token="" href="" isthemeshared="false" />
  </defaultChannelMapping>
</mappings></body></html>

Fazit

SharePoint 2013 bietet mit den neuen Mobile Channels einen effizienten Weg, um die Seite Mobile-tauglich zu gestalten. Das automatisierte Deployment via Features ist im Moment noch nicht durchgehend gelöst, da die API fast ausschliesslich als internal markiert ist.  Hier muss Hand angelegt und mit einem entsprechenden Feature Receiver die Deployment-Automatisierung nachgerüstet werden.

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.

Seite 1 von 212