Asynchrone Programmierung in WinRT

Meine neue WebSite finden Du jetzt unter https://AttilaKrick.com.

Beispiele zu Asynchrone API

MSDN Asynchrone Programmierung

Ein Benutzer erwartet das auf seine Aktion unmittelbare Reaktion von der App erfolgt. Um so verwirrter reagiert der Benutzer wenn diese Reaktion auf sich warten lässt weil die App noch synchron mit dem laden einer Datei aus dem Internet blockiert ist. Eine flüssige App würde hier den Download asynchron durchführen, so das der Benutzer weiter mit seiner App interagieren kann.

Wer mit dem asynchronen Mustern nicht vertraut ist kann sich die Asynchronität wie eine Bestellung beim Pizza-Dienst vorstellen. Sie rufen an und geben Ihre Bestellung durch und können anschließend Ihre bisherige Tätigkeit fortsetzen. Sobald der Pizza-Man oder -Frau mit der fertig Pizza klingelt können Sie sich nun um das leckere Esswerk kümmern. Niemand würde auf die Idee kommen nach der Bestellung apathisch in Inaktivität zu verfallen bis es klingelt.

Async-Namenskonvention – Üblicherweise enden asynchrone Methoden auf ...Async. Diese Methoden finden Sie dort wo Dauer eine Rolle spielt, z.B.

ContactPicker.PickSingleContactAsync
XmlDocument.SaveToFileAsync
DataReader.LoadAsync
RandomAccessStream.CopyAsync
SyndicationClient.RetrieveFeedAsync

await & async – Wenn Sie den await-Operator in einer Methode verwenden muss diese Methode das async-Schlüsselwort enthalten. Im Konstruktor oder in einer Eigenschaft können Sie await nicht verwenden. TIPP Erstellen Sie eine Methode, lagern den await-Code dorthin aus und ruf anschließend die erstellte Methode im Konstruktor bzw. in der Eigenschaft auf.

Threads, Kontextwechsel und Verteiler spielen für Sie keine Rolle mehr. Der Aufruf einer asynchronen API mittels await erfolgt im selben Kontext wie der ursprüngliche Aufruf. Das bedeutet dass Sie die Benutzeroberfläche mit den Ergebnissen aktualisieren können ohne sich um die Rückkehr zum Benutzeroberflächen-Thread zu kümmern.

Task – Sie können aus dem .NET Framework Task und Task<TResult>
verwenden um eigene asynchrone Methode zu implementieren. Hierzu gibt es die Erweiterungsmethoden (WindowsRuntimeSystemExtensions) AsAsyncAction oder AsAsyncOperation<TResult>. Diese liefern Async-WinRT-Konforme Ergebnisse die sich in WinRT entsprechend weiter verwerten lassen. Hierzu später mehr.

Asynchrone Methoden verwenden

Angenommen wir wollen Nachrichten-Beiträge direkt aus dem Internet herunterladen. Hierbei ist es wichtig dass die App reaktionsfähig bleibt. Um diese Reaktionsfähigkeit sicherzustellen, stellt WinRT eine asynchrone Methode SyndicationClient.RetrieveFeedAsync zum Herunterladen von Newsfeeds bereit deren asynchrone Verwendung wie folgt aussieht:

async void Button_Click(object sender, RoutedEventArgs e)
{
    var feedUri = new Uri("http://rss.golem.de/rss.php?feed=RSS2.0");
    var client = new SyndicationClient();
    try
    {
        var feed = await client.RetrieveFeedAsync(feedUri);
        StatusTextBox.Text = feed.Title.Text;
    }
    catch (Exception ex)
    {
        StatusTextBox.Text = ex.Message;
    }
}

Anmerkungen

  1. Der await-Operator teilt dem Compiler mit dass Sie eine asynchrone Methode aufrufen und er zusätzliche Arbeiten für Sie erliegen muss.
  2. Das Schlüsselwort asyncmuss in der Methodendeklaration aller Methoden angegeben werden, in denen Sie den await-Operator verwenden.
  3. Durch den Aufruf von ... await client.RetrieveFeedAsync initiiert die Methode den Abruf asynchron und beendet den Click-Ereignishandler vorübergehend. Ihre App kann dadurch andere Ereignisse verarbeiten und bleibt für den Benutzer reaktionsfähig.
  4. Wenn RetrieveFeedAsync abgeschlossen und das Ergebnis SyndicationFeed verfügbar ist macht Ihre App im Click-Ereignishandler an der Stelle weiter wo sie aufgehört hat var feeds ... um den Rest des Handlers abzuarbeiten.
  5. Während des asynchronen Vorgangs könnte z.B. die Netzwerkverbindung zum RSS-Feed abbrechen. Daher können Sie wie gewohnt die Fehlertoleranz erhöhen, indem Sie den asynchronen Code in einen try-catch-Block ausführen.

Ergebnisse asynchroner Methoden

Der Rückgabetyp von RetrieveFeedAsync ist kein SyndicationFeed sondern IAsyncOperationWithProgress<SyndicationFeed, RetrievalProgress>. Es ist wichtig zu Verstehen das der await-Operator tatsächlich auf Basis des Rückgabewerts und nicht auf der Methode agiert. Wenn Sie await anwenden erhalten Sie das Ergebnis der asynchronen Methoden SyndicationFeed, wenn Sie await nicht anwenden erhalten Sie IAsyncOperationWithProgress<SyndicationFeed, RetrievalProgress>.

Wenn Sie eine asynchrone Methode verwenden reflektieren Sie üben den Rückgabetyp um Infos zum Ergebnis zu erhalten. Alle asynchronen Methoden in WinRT geben einen der folgenden Typen zurück:

IAsyncAction                                    = Einfache Aktion
IAsyncActionWithProgress<TProgress>             = inkl Fortschritt 
IAsyncOperation<TResult>                        = inkl Ergebnis
IAsyncOperationWithProgress<TResult, TProgress> = inkl Ergebnis + Forts.

Diese Rückgabetypen eröffnen Ihnen weitere Möglichkeiten beim Umgang mit asynchronen Vorgängen die im weiteren Verlauf besprochen werden.

Ablaufsteuerung mithilfe asynchroner Rückgabetypen

Status

Ein asynchroner Vorgang beginnt im .Status==Started und wird mit Canceled, Completed oder Error fortgesetzt.

var feedUri = new Uri("http://rss.golem.de/rss.php?feed=RSS2.0");
var client = new SyndicationClient();

var asyncInfo = client.RetrieveFeedAsync(feedUri);
var id = asyncInfo.Id;
var status = asyncInfo.Status;

Abbrechen

In unterschiedlichen Situation kann es nötig sein einen asynchronen Vorgang abzubrechen. Rufe Sie dazu die Cancel-Methode auf:

IAsyncOperationWithProgress<SyndicationFeed, 
    RetrievalProgress> _FeedAsyncInfo;

void Starten_Click(object sender, RoutedEventArgs e)
{
    var feedUri = new Uri("http://rss.golem.de/rss.php?feed=RSS2.0");
    var client = new SyndicationClient();

    _FeedAsyncInfo = client.RetrieveFeedAsync(feedUri);
    var status = _FeedAsyncInfo.Status; // = Started
}

void Abbrechen_Click(object sender, RoutedEventArgs e)
{
    _FeedAsyncInfo**.Cancel()**;
    var status = _FeedAsyncInfo.Status; // = Canceled
}

Abschluss

Bei einem asynchronen Vorgang wird der Completed-Handler immer benötigt da dieser u. a. das tatsächliche Ergebnis liefert aber auch ausgeführt wird wenn abgebrochen wurde oder einen Fehler auftrat. Überprüfen Sie den Status und rufen nur im Erfolgsfall über GetResults() das Ergebnis ab:

var feedUri = new Uri("http://rss.golem.de/rss.php?feed=RSS2.0");
var client = new SyndicationClient();

var asyncInfo = client.RetrieveFeedAsync(feedUri);
Meldung.Text = String.Format("Start {0:ss.fff}: ID {1} STATUS {2}\n",
    DateTime.Now,
    asyncInfo.Id,
    asyncInfo.Status);

asyncInfo.Completed = async (ai, status) =>
{
    await Dispatcher.RunIdleAsync((f) =>
    {
        if (status == AsyncStatus.Completed)
            Meldung.Text = String.Format("{0:ss.fff}: ID {1} STATUS {2} ERGEBNIS {3}\n",
                DateTime.Now,
                ai.Id,
                ai.Status,
                ai.GetResults().Title.Text);

        else if (status == AsyncStatus.Canceled)
            Meldung.Text = String.Format("Abbruch {0:ss.fff}: ID {1}\n",
                DateTime.Now,
                ai.Id);

        else if (status == AsyncStatus.Error)
            Meldung.Text = String.Format("Fehler {0:ss.fff}: ID {1} "
                + "ERROR {2}\n",
                DateTime.Now,
                ai.Id, 
                ai.ErrorCode);
    });
};

Der Completed-Handler wird nicht im UI-Thread ausgeführt und kann so auch nicht direkt auf UI-Elemente zugreifen. Über await Dispatcher.RunIdleAsync(...) wäre ein Zugriff auf UI-Elemente wieder möglich da diese im UI-Thread ausgeführt werden.

Fortschritt

Einige asynchrone Methoden unterstützen Fortschrittsbenachrichtigungen während sie ausgeführt werden. Sie können diese Benachrichtigungen für aktuelle Fortschrittsberichte zum asynchronen Vorgang in der UI verwenden:

var feedUri = new Uri(FeedUri.Text);
var client = new SyndicationClient();
var asyncInfo = client.RetrieveFeedAsync(feedUri);

progressBar.Value = 0;
asyncInfo.Progress = async (ai, args) =>
{
    await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
    {
        progressBar.Value = args.BytesRetrieved 
            / args.TotalBytesToRetrieve * 100;
    });
};

Eigene Aufgaben asynchron ausführen

Neben den asynchronen WinRT-Methoden kann es hilfreich sein eigene Vorgänge mit großer Dauer asynchron in einem anderen Thread als den der Oberfläche auszuführen. Fügen Sie diese Aufgabe der Warteschlange des Threadpool hinzu. Implementieren wie oben einen Handler für Completed und werten darin im Erfolgsfall das Ergebnis aus.

Wenn nun der asynchrone Vorgang in der Warteschlange des ThreadPools abgeschlossen ist, wird der Completed-Handler aufgerufen. In diesem können Sie jedoch keine Änderung in der UI vornehmen da wir uns in einem anderen Thread befinden und würde sogar eine Wrong-Thread-Excaption kassieren. Um dies zu umgehen, kann der UI zugeordnete CoreDispatcher verwendet werden um die UI im richtigen Thread zu ändern.

Und so könnte z.B. das Click-Ereignis aussehen:

void Button_Click(object sender, RoutedEventArgs e)
{
    var asyncInfo = ThreadPool.RunAsync((a) =>
    {
        // Fake rechenintensiver Code
        new ManualResetEvent(false).WaitOne(2000);
    });
    asyncInfo.Completed = async (ai, status) =>
    {
        await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
        {
            switch (status)
            {
                case AsyncStatus.Completed:
                    LogThreadPoolRunAsync.Text += String.Format("Fertig {0:ss.fff} ID {1} STATUS {2}",
                        DateTime.Now,
                        asyncInfo.Id,
                        asyncInfo.Status);
                    break;

                case AsyncStatus.Error:
                    LogThreadPoolRunAsync.Text += String.Format("Fehler {0:ss.fff} ID {1} STATUS {2}",
                        DateTime.Now,
                        asyncInfo.Id,
                        asyncInfo.Status);
                    break;

                case AsyncStatus.Canceled:
                    LogThreadPoolRunAsync.Text += String.Format("Abbruch {0:ss.fff} ID {1} STATUS {2} EX {3}",
                        DateTime.Now,
                        asyncInfo.Id,
                        asyncInfo.Status,
                        ai.ErrorCode);
                    break;
            }
        });
    };
    LogThreadPoolRunAsync.Text = String.Format("Start {0:ss.fff} "
        + "ID {1} STATUS {2} >>> ",
        DateTime.Now,
        asyncInfo.Id,
        asyncInfo.Status);
}

Das gleiche Ergebnis mit dem await-Operator würde so aussehen:

async void Button_Click(object sender, RoutedEventArgs e)
{
    LogThreadPoolRunAsync.Text = String.Format("Start {0:ss.fff} >>> ", DateTime.Now);
    try
    {
        await ThreadPool.RunAsync((a) =>
        {
            // Fake rechenintensiver Code
            new ManualResetEvent(false).WaitOne(2000); 
        });
        LogThreadPoolRunAsync.Text += String.Format("Fertig {0:ss.fff}", DateTime.Now);
    }
    catch (Exception ex)
    {
        LogThreadPoolRunAsync.Text +=String.Format("Fehler {0:ss.fff} {1}", DateTime.Now, ex);
    }
}

Task nach WinRT-Async umwandeln

Um Ihre vorhandenen Task-Objekte im WinRT-Async-Model nutzen zu können oder über den Umweg der Task-Klasse an weitere Threading-Techniken wie z.B. den Cancel-Token zu gelangen, können Sie Task weiter verwenden und diese in die oben beschriebenen Async-Rückgabetypen umwandeln.

Hierfür enthält das System.Runtime.WindowsRuntime-Assembly Erweiterungsmethoden AsAsyncAction und AsAsyncOperation die Sie bei Task und Task<TResult>-Objekten nutzen können:

async void Button_Click(object sender, RoutedEventArgs e)
{
    LogTaskToAsyncOperationTest.Text = "Läuft ...";
    LogTaskToAsyncOperationTest.Text = await TaskToAsyncOperationAsync();
}

IAsyncOperation<string> TaskToAsyncOperationAsync()
{
    var t = Task<string>.Run(() =>
        {
            // Fake rechenintensiver Code
            new ManualResetEvent(false).WaitOne(3000);
            return "Nach 3s 43 asynchron heruntergeladen.";
        });
    return t.AsAsyncOperation();
}

Eigene erweiterte asynchrone Aufgaben ausführen

Das Umwandeln von Task mittels AsAsyncAction und AsAsyncOperation bietet grundlegende Unterstützung für Task’s in WinRT-Async. Darüber hinaus erhalten Sie erweiterter Unterstützung, wenn Sie AsyncInfo.Run und dessen Überladungen verwenden. Dazu gehören:

Abbruch

Mit AsyncInfo.Run werden Abbrüche mit asynchronen WinRT-Methoden unterstützt:

void Start_Click(object sender, RoutedEventArgs e)
{
    _JetztArbeitenAsyncInfo = JetztArbeitenAsync();
    _JetztArbeitenAsyncInfo.Completed = (asyncInfo, asyncStatus) => 
    {
        if (asyncStatus== AsyncStatus.Completed)
            LogAsyncInfoRunCancelTest.Text += String.Format("STATUS {0} "
                + "RESULT {1}",
                asyncStatus, asyncInfo.GetResults());

        else if (asyncStatus == AsyncStatus.Canceled)
            LogAsyncInfoRunCancelTest.Text += String.Format("STATUS {0}",
                asyncStatus);
    };
    LogAsyncInfoRunCancelTest.Text = String.Format("START {0} "
        + "STATUS {1} >>> ",
        _JetztArbeitenAsyncInfo.Id,
        _JetztArbeitenAsyncInfo.Status);
}

void Abbrechen_Click(object sender, RoutedEventArgs e)
{
    if (_JetztArbeitenAsyncInfo!=null)
    {
        _JetztArbeitenAsyncInfo.Cancel();
    }
}

private IAsyncOperation<string> _JetztArbeitenAsyncInfo;

public static IAsyncOperation<string> JetztArbeitenAsync()
{
    return AsyncInfo.Run(cancellationToken =>
    {
        return Task<string>.Run(() =>
        {
            for (int i = 0; i < 100; i++)
            {
                // Fake rechenintensiver Code
                new ManualResetEvent(false).WaitOne(25);

                if (cancellationToken.IsCancellationRequested)
                {
                    // Operation wurde abgebrochen
                    // Code der Aufräumen
                    cancellationToken.ThrowIfCancellationRequested();
                }
            }
            return "Fertig mit Async-Operation";
        });
    });
}

Fortschritt

AsyncInfo.Run stellt Unterstützung für Fortschrittsberichte durch asynchrone WinRT-Methoden bereit:

void Button_Click(object sender, RoutedEventArgs e)
{
    var ai = JetztArbeitenMitFortschrittAsync();
    ai.Progress = (asyncInfo, progressInfo) =>
    {
        LogAsyncInfoRunFortschrittTest.Text += String.Format("{0}% ",
            progressInfo);
    };
    LogAsyncInfoRunFortschrittTest.Text = String.Format("START {0} "
        + "STATUS {1} >>> Fortschritt ",
        ai.Id,
        ai.Status);
}

public static IAsyncOperationWithProgress<string, int> JetztArbeitenMitFortschrittAsync()
{
  return AsyncInfo.Run((CancellationToken cancellationToken, IProgress<int> progress) =>
    {
        return Task<string>.Run(() =>
        {
            for (int i = 0; i <= 10; i++)
            {
                progress.Report(i*10);

                // Fake rechenintensiver Code
                new ManualResetEvent(false).WaitOne(200);
            }
            return "Fertig mit Async-Operation";
        });
    });
}

Windows Store App-Gestaltung

Meine neue WebSite finden Du jetzt unter https://AttilaKrick.com.

Windows Store Apps müssen als Vollbild-Anwendungen konzipiert werden da eine Fenster-Darstellung nicht mehr möglich ist. Im Grund ist das neue Windows 8 UI-Layout simpel. Nehmen Sie eine klassische Windows-Fenster-Anwendung und entfernen Sie davon jedes Pixel was kein Inhalt ist, Fertig!

Geben Sie Ihrem Inhalt den primären Fokus, nutzen Sie Ihren Inhalt zur Interaktion mit dem Benutzer und platzieren alles anderen in sekundäre Bereiche.

Ihre App sollte sich schnell und flüssig, sanft und nicht hart anfühlen.

Apps können mit allen Windows 8-Bildschirmgrößen ausgeführt werden. In der Regel haben größere Bildschirme auch höhere Auflösungen. Daher bieten diese einen größeren Anzeigebereich denn es gilt zu nutzen und zu kleineren Auflösungen abzugrenzen.

Sie sollten das Layout früh in die Planung Ihrer App einbeziehen. Unterschätzen Sie auch nicht den Aufwand für Anpassungen diverser Bildschirmauflösungen, Ansichten-Gestaltung und Animation die einen erheblichen Einfluss auf das Layout haben.

Eine Seite muss für verschiedene Ansichten mehrfach gestallt werden. Eine Ansicht ist die Art, in der sich der Inhalt an der UI anpasst in Abhängigkeit wie der Benutzer auf Ihre App zugreift. Zum Beispiel im Portrait-Format oder Ihre App ist an einer Seite angedockt. Eine App die mehrere Ansichten unterstützt, kann auf Geräten verschiedener Größen und Ausrichtungen verwendet werden, und der Benutzer kann den Inhalt entsprechend seinen Anforderungen und Vorlieben bearbeiten.

Viele dieser Faktoren werden beim Zertifizierungsverfahren für den Microsoft Store berücksichtigt.

Bildschirmskalierung

Für die Unterstützung aller Features, wie z. B. das Andocken ist eine Mindestauflösung von 1366x768px erforderlich.

Der Benutzer erwartet bei größeren Zoll-Auflösungen mehr Inhalt und Funktionalität.

Entwerfen Sie Ihre App so, dass alle UI-Elemente wie Navigation, Steuerelemente und Inhalte auf den Bildschirm passen, d. h. also für die Mindestauflösung von 1024x768px.

Entwerfen Sie Ihre App so, dass alle UI-Elemente ohne leere Flecke auf den Bildschirm passen, d. h. also für die Idealauflösung von 1366x768px.

Berücksichtigen Sie auf einem größeren Bildschirme Layout, Ästhetik, Proportionen und die Anordnung Ihrer Steuerelemente.

Statisches Layout Eine Möglichkeit ist das statische Layout. Dieses treffen Sie oft bei Spielen an die hauptsächlich aus Bitmap-Bildern bestehen. Hier mehr Inhalt anzuzeigen ist nicht möglich oder bringt vermutlich keinen Mehrwert. Windows 8 skaliert feste Layouts mit einem integrierten Verfahren automatisch bis zu maximal 200%.

Über die Eigenschaften .Canvas.Left und .Canvas.Top können Sie ihre UI Elemente in einem statischen Layout über X- und Y-Koordinaten genau positioniert.

Dynamisches Layout Diese zweite Möglichkeit sehen Sie häufig in Inhalt-Apps. Diese Layouts bestehen i.d.R. aus definierten proportionalen Elementen wie Kopfzeile, Fußzeile und in der Mitte einen Inhalts-Bereich. Dieses Layout ändert sich dynamisch und passt sich an verschiedenen Bildschirmgrößen an. Die Größe der Benutzeroberfläche wird automatisch an unterschiedliche Bildschirmauflösungen angepasst.

Zu den wichtigsten dynamischen Positions-Eigenschaften gehören .Width, .Height,.MinWidth,.MinHeight,.Margin,.Padding,.VerticalAlignment,.HorizontalAlignment` u.v.m.

Statisches Layout erstellen

1. Beginnen Sie mit den Basisauflösungen 1024×768 und 1366×768.

2. Platzieren Sie den festen Inhalt in eine ViewBox, um ein festes Layout auf den Bildschirm zu skaliert.

3. Legen Sie die Größen der ViewBox z.B. auf 1366x768px fest.

4. Platzieren Sie keine dynamischen Steuerelemente in die ViewBox, da sich diese Elemente automatisch an verschiedene Bildschirmgrößen anpassen.

5. Definieren Sie Stil & Farbe für das Portrait-Format.

6. Stellen Sie Vektor-Ressourcen oder Raster-Ressourcen mit hoher Auflösung bereit.

<Canvas Height="125" Margin="20">
    <Rectangle Canvas.Left="40" Canvas.Top="20" Fill="Yellow" Width="75" Height="75" />
</Canvas>

Dynamisches Layout erstellen

1. Bestimmen Sie für jede Zelle, die Sie als in horizontaler oder vertikaler Richtung dynamisch identifiziert haben, wie das App-Layout diese Fläche auf einem größeren Bildschirm nutzen soll.

2. Bestimmen Sie das Layout-Drahtmodell, hier sollten die Positionen der Bereiche für Kopfzeile, Navigation und Inhalt hervorgehen.

3. Bestimmen Sie, welche Teile des Layout statisch bzw. dynamisch sind.

4. Ein ListView oder GridView füllt automatisch die verfügbare Fläche mit weiteren Elementen.

5. Verwenden Sie für Text gegebenenfalls ein mehrspaltiges Layout.

6. Verwenden Sie gegebenenfalls für Bilder ein Canvas, da dieses automatisch erweitert wird, um die verfügbare Fläche auszufüllen.

7. Zeigen Sie mehr Leerraum oder zeigen Sie mehr von der App.

Für ein dynamisches Layout nutzen Sie u.a. die Steuerelemente StackPanel, Grid, GridView und ListView, zum Beispiel:

<StackPanel Orientation="Horizontal">
    <Rectangle Fill="Fuchsia" Width="50" Height="50" Margin="10" />
    <Rectangle Fill="Honeydew" Width="50" Height="50" Margin="10" />
    <Rectangle Fill="Orange" Width="50" Height="50" Margin="10" />
</StackPanel>

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition />
        <RowDefinition Height="40" />
        <RowDefinition />
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="65" />
        <ColumnDefinition Width="Auto" />
        <ColumnDefinition Width="*" />
    </Grid.ColumnDefinitions>
    <TextBox Grid.Column="0" Grid.Row="0" Text="Z1 | S1"/>
    <TextBox Grid.Column="1" Grid.Row="0" Text="Z1 | S2"/>
    <TextBox Grid.Column="0" Grid.Row="1" Text="Z2 | S1"/>
    <TextBox Grid.Column="1" Grid.Row="1" Text="Z2 | S2"/>
    <TextBox Grid.Row="2" Grid.ColumnSpan="2" Text="Z3 | S1-2"/>
    <TextBox Grid.Column="3" Grid.RowSpan="3" Text="Z1-3 | S3"/>
</Grid>

Ansichten

Angedockte Ansichten (Snapped) und gefüllte Ansichten (Filled) sind nur für Displays mit einer horizontalen Mindestauflösung von 1366px verfügbar.

Der Benutzer kann mit Ihrer App im Querformat(Lanscape), Hochformat (Portrait) oder mit zwei Apps gleichzeitig arbeiten (Filled und Snapped).
Um diese vier Zustände (ViewStates) bedienen zu können, kann Ihre App verschieden Layouts bevorraten oder eins entsprechend modifizieren.

Weitere Informationen finden Sie auch im Kapitel ApplicationViewState.

Lanscape Querformat und i. d. R. die meistbenutzte Ansicht.

Portrait Hochformat

Filled Die App füllt den verbleibenden Bildschirmbereich aus, der nicht von der angedockten App belegt wird. Kann nur im Querformat genutzt werden. Diesem Layout stehen min. 1024px Breite zur Verfügung.

Snapped Die App wird am linken oder rechten Rand angedockt. Kann nur im Querformat genutzt werden. Diesem Layout stehen min. 320px Breite zur Verfügung.

TIP Bei der angedockten App handelt es sich um keine Gadget-Version Ihrer App, daher sollten Sie für den Benutzer Status, Kontext und die Interaktivität aufrechterhalten.

TIP Sorgen Sie für Feature-Parallelität in den verschiedenen Zuständen.

TIP Geben Sie den Benutzern die Kontrolle, d.h. docken Sie die App nicht programmgesteuert ab.

TIP Nutzen Sie das hohe Seitenverhältnis des angedockten Layouts für vertikale Verschiebungen Ihres Inhaltes.

TIP Angesichts der geringen Breite von 320 Pixel im angedockten Zustand sollten Sie vom mehrspaltigen Layout zum einspaltigen Layout übergehen.

Vektor- und Raster-Ressourcen

Vektor-Ressourcen (z.B. SVG, XAML) werden ohne Skalierungsartefakte oder Verschwimmen skaliert.

Raster-Ressourcen (z.B. BMP) werden mit Skalierungsartefakte oder Verschwimmen skaliert. Stellen Sie Bilder bereit, die doppelt so groß sind wie die Designgröße um beim automatischen vergrößern nicht verpixelt zu wirken oder geben mehrere Bilder mit unterschiedlichen Auflösungen an. Mehr darüber erfahren Sie im Kapitel Bild-Ressource.

Testen des App-Layouts

In Visual Studio über den Windows Simulator und im XAML Designer in der Geräte-Ansicht können Sie diverse Auflösungen und Ansichten testen.

In Blend über den XAML Designer und in der Geräte-Ansicht.

Code on WordPress Tipps

Meine neue WebSite finden Du jetzt unter https://AttilaKrick.com.


Posting Source Code

Das BBCode-Element heißt ‚code‘ und muss mit seinene Attributen in ‚[]‘ stehen, sowei am Ende mit ‚/‘ abgeschlossen werden.

code lang=“csharp“ title=“Mein Favorit“

[TestMethod]
public void PropertyChange_ModelBase()
{
    // siehe http://wp.me/p3i5NY-1R
    var myModel = new MyModelTest();
    ModelBase modelBase = myModel;
    string propertyName_SOLL = "Kurzname";
    myModel.PropertyChanged += (fired, args) =>
    {
        Assert.AreEqual(propertyName_SOLL, args.PropertyName);
    };
}

code light=“true“

[TestMethod]
public void PropertyChange_ModelBase()
{
    // siehe http://wp.me/p3i5NY-1R
    var myModel = new MyModelTest();
    ModelBase modelBase = myModel;
    string propertyName_SOLL = "Kurzname";
    myModel.PropertyChanged += (fired, args) =>
    {
        Assert.AreEqual(propertyName_SOLL, args.PropertyName);
    };
}

code Highlight=“5,10″

[TestMethod]
public void PropertyChange_ModelBase()
{
    // siehe http://wp.me/p3i5NY-1R
    var myModel = new MyModelTest();
    ModelBase modelBase = myModel;
    string propertyName_SOLL = "Kurzname";
    myModel.PropertyChanged += (fired, args) =>
    {
        Assert.AreEqual(propertyName_SOLL, args.PropertyName);
    };
}

collapse=“true“ title=“BBCode Beispiel zugeklappt“

[TestMethod]
public void PropertyChange_ModelBase()
{
    // siehe http://wp.me/p3i5NY-1R
    var myModel = new MyModelTest();
    ModelBase modelBase = myModel;
    string propertyName_SOLL = "Kurzname";
    myModel.PropertyChanged += (fired, args) =>
    {
        Assert.AreEqual(propertyName_SOLL, args.PropertyName);
    };
}

Weitere Details hier