Exemplarische Vorgehensweise: Erstellen einer einfachen Komponente in C# oder Visual Basic und Aufrufen dieser Komponente über JavaScript

 

In dieser exemplarischen Vorgehensweise wird erläutert, wie mit .NET Framework in Kombination mit Visual Basic oder C# eigene Windows-Runtime-Typen erstellt werden können, die in einer Komponente für Windows-Runtime gepackt sind, und wie die Komponente von der mit JavaScript für Windows erstellten Windows 8.x Store-App aufgerufen wird.

Visual Studio erleichtert das Hinzufügen von mit C# oder Visual Basic geschriebenen Windows-Runtime-Komponenten zu Ihrer Anwendung sowie die Erstellung von Windows-Runtime-Typen, die aus JavaScript aufgerufen werden können. Intern können die Windows-Runtime-Typen sämtliche für Windows 8.x Store-Apps zulässigen .NET Framework-Funktionen verwenden. (Weitere Informationen finden Sie unter Erstellen von Windows-Runtime-Komponenten in C# und Visual Basic und .NET für Windows Store-Apps – Übersicht.) Extern können die Typmitglieder nur Windows-Runtime-Typen für ihre Parameter und Rückgabewerte verfügbar machen. Wenn Sie die Projektmappe erstellen, erstellt Visual Studio das Windows-Runtime-Komponentenprojekt von .NET Framework und führt dann einen Schritt zur Erstellung einer Datei mit Windows-Metadaten (.winmd) durch. Das ist die Windows-Runtime-Komponente, die Visual Studio in die App einbindet.

System_CAPS_noteHinweis

.NET Framework ordnet automatisch einige gängige .NET Framework-Typen den jeweiligen Windows-Runtime-Entsprechungen zu, darunter primitive Datentypen und Auflistungstypen. Diese .NET Framework-Typen können in der öffentlichen Schnittstelle einer Windows-Runtime-Komponente verwendet werden und werden den Benutzern der Komponente als die entsprechenden Windows-Runtime-Typen angezeigt. Weitere Informationen finden Sie unter Erstellen von Windows-Runtime-Komponenten in C# und Visual Basic

In dieser exemplarischen Vorgehensweise werden die folgenden Aufgaben veranschaulicht: Nachdem Sie den ersten Abschnitt durchgearbeitet haben, in dem die Windows 8.x Store-App mit JavaScript eingerichtet wird, können Sie die übrigen Abschnitte in beliebiger Reihenfolge bearbeiten.

Für diese exemplarische Vorgehensweise wird Folgendes benötigt:

  • Windows 8 (oder höher)

  • Microsoft Visual Studio 2012 oder Microsoft Visual Studio Express 2012 für Windows 8 (oder höher)

In diesem Abschnitt werden eine mit JavaScript für Windows erstellte Windows 8.x Store-App erstellt und ein Visual Basic- oder C#-Windows-Runtime-Komponentenprojekt hinzugefügt. Es wird gezeigt, wie Sie einen verwalteten Windows-Runtime-Typ definieren, aus JavaScript eine Instanz des Typs erstellen und statische Mitglieder und Instanzmitglieder aufrufen. Die grafische Darstellung der Beispielanwendung wurde bewusst relativ einfach gehalten, damit Sie sich auf die Komponente konzentrieren können. Sie dürfen Sie gerne verschönern.

  1. Erstellen Sie in Visual Studio ein neues JavaScript-Projekt: Wählen Sie in der Menüleiste Datei, Neu, Projekt aus (wählen Sie in Visual Studio Express Datei, Neues Projekt aus). Wählen Sie im Dialogfeld Neues Projekt im Abschnitt Installierte Vorlagen die Option JavaScript und dann Windows Store aus. (Ist Windows Store nicht vorhanden, überprüfen Sie, ob Sie Windows 8 oder höher verwenden.) Wählen Sie als Vorlage Leere Anwendung aus, und geben Sie als Projektname SampleApp ein.

  2. Erstellen Sie das Komponentenprojekt: Öffnen Sie in Projektmappen-Explorer das Kontextmenü der SampleApp-Projektmappe, und wählen Sie Hinzufügen aus. Fügen Sie der Projektmappe dann mit Neues Projekt ein neues C#- oder Visual Basic-Projekt hinzu. Wählen Sie im Dialogfeld Neues Projekt hinzufügen im Abschnitt Installierte Vorlagen die Option Visual Basic oder Visual C# aus, und wählen Sie dann Windows Store. Wählen Sie die Vorlage Windows-Runtime-Komponente aus, und geben Sie als Projektname SampleComponent ein.

  3. Ändern Sie den Klassennamen in Beispiel. Beachten Sie, dass die Klasse in Visual Basic standardmäßig mit public sealedPublic NotInheritable gekennzeichnet ist. Alle Windows-Runtime-Klassen, die von der Komponente zur Verfügung gestellt werden, müssen versiegelt sein.

  4. Ergänzen Sie die Klasse mit zwei einfachen Mitgliedern, einer static-Methode (in Visual Basic Shared-Methode) und einer Instanzeigenschaft:

    namespace SampleComponent { public sealed class Example { public static string GetAnswer() { return "The answer is 42."; } public int SampleProperty { get; set; } } }
    
  5. Optional: Um IntelliSense für die neuen Mitglieder zu aktivieren, öffnen Sie in Projektmappen-Explorer das Kontextmenü des Projekts SampleComponent, und wählen Sie die Option Erstellen aus.

  6. Öffnen Sie in Projektmappen-Explorer im JavaScript-Projekt das Kontextmenü von Verweise, und wählen Sie zum Öffnen des Verweis-Managers dann Verweis hinzufügen aus. Wählen Sie Projektmappe und anschließend Projekte aus. Aktivieren Sie das Kontrollkästchen für das Projekt SampleComponent, und fügen Sie mit OK einen Verweis hinzu. 

Um den Windows-Runtime-Typ aus JavaScript zu verwenden, fügen Sie am Ende der Datei "default.js" (im Ordner "js" des Projekts) nach der von der Visual Studio-Vorlage bereitgestellten anonymen Funktion folgenden Code hinzu:

var ex; function basics1() { document.getElementById('output').innerHTML = SampleComponent.Example.getAnswer(); ex = new SampleComponent.Example(); document.getElementById('output').innerHTML += "<br/>" + ex.sampleProperty; } function basics2() { ex.sampleProperty += 1; document.getElementById('output').innerHTML += "<br/>" + ex.sampleProperty; }

Der erste Buchstabe der Mitgliedernamen ändert sich von Groß- in Kleinbuchstaben. Diese Umwandlung ist Teil der Unterstützung von JavaScript, damit Windows-Runtime regulär verwendet werden kann. Namespaces und Klassennamen werden in Pascal-Schreibweise angegeben. Mitgliedernamen werden in Kamel-Schreibweise angegeben. Ausgenommen hiervon sind Ereignisnamen. Bei diesen wird Kleinschreibung verwendet. Weitere Informationen finden Sie unter Verwenden von Windows-Runtime in JavaScript. Die Regeln der Kamel-Schreibweise sind etwas gewöhnungsbedürftig. Eine Reihe am Anfang stehender Großbuchstaben wird normalerweise in Kleinbuchstaben geschrieben. Folgt auf drei Großbuchstaben aber ein Kleinbuchstabe, werden nur die ersten beiden Buchstaben in Kleinbuchstaben angezeigt. Beispiel: Der Name des Mitglieds "IDStringKind" wird "idStringKind" geschrieben. In Visual Studio können Sie das Windows-Runtime-Komponentenprojekt erstellen und dann im JavaScript-Projekt mit IntelliSense die richtige Groß-/Kleinschreibung anzeigen.

Auf ähnliche Weise unterstützt .NET Framework die normale Verwendung von Windows-Runtime in verwaltetem Code. Weitere Informationen dazu erhalten Sie an späterer Stelle in diesem Artikel sowie in den Artikeln Erstellen von Windows-Runtime-Komponenten in C# und Visual Basic und .NET Framework-Unterstützung für Windows Store-Apps und Windows-Runtime.

Öffnen Sie im JavaScript-Projekt die Datei "default.html", und aktualisieren Sie den Text wie im folgenden Code angegeben. Der Code enthält alle Steuerelemente für die Beispielanwendung sowie die Funktionsnamen für die Klickereignisse.

System_CAPS_cautionAchtung

Wenn Sie die App zum ersten Mal ausführen, werden nur die Schaltflächen Basics1 und Basics2 unterstützt.

<body> <div id="buttons"> <button onclick="basics1();">Basics 1</button> <button onclick="basics2();">Basics 2</button> <button onclick="runtime1();">Runtime 1</button> <button onclick="runtime2();">Runtime 2</button> <button onclick="returns1();">Returns 1</button> <button onclick="returns2();">Returns 2</button> <button onclick="events1();">Events 1</button> <button id="btnAsync" onclick="asyncRun();">Async</button> <button id="btnCancel" onclick="asyncCancel();" disabled="disabled">Cancel Async</button> <progress id="primeProg" value="25" max="100" style="color: yellow;"></progress> </div> <div id="output"> </div> </body>

Öffnen Sie im JavaScript-Projekt im CSS-Ordner die Datei default.css. Ändern Sie den Abschnitt body wie angegeben, und fügen Sie Stile hinzu, um das Schaltflächenlayout und die Platzierung des Ausgabetexts zu bestimmen.

body { -ms-grid-columns: 1fr; -ms-grid-rows: 1fr 14fr; display: -ms-grid; } #buttons { -ms-grid-rows: 1fr; -ms-grid-columns: auto; -ms-grid-row-align: start; } #output { -ms-grid-row: 2; -ms-grid-column: 1; }

Um die Projektmappe zu erstellen und auszuführen, drücken Sie F5. (Tritt ein Laufzeitfehler auf, der besagt, dass SampleComponent nicht definiert ist, fehlt der Verweis auf das Klassenbibliotheksprojekt. 

Visual Studio kompiliert zuerst die Klassenbibliothek und führt dann eine MSBuild-Aufgabe aus, die mithilfe von Winmdexp.exe (Windows Runtime Metadata Export Tool) die Windows-Runtime-Komponente erstellt. Die Komponente wird in eine WINMD-Datei eingefügt, die sowohl den verwalteten Code als auch die Windows-Metadaten enthält, mit denen der Code beschrieben wird. Wenn Sie Code schreiben, der in einer Windows-Runtime-Komponente ungültig ist, erzeugt WinMdExp.exe Buildfehlermeldungen. Diese werden in der Visual Studio-IDE angezeigt. Visual Studio fügt die Komponente dem Anwendungspaket (APPX-Datei) der Windows 8.x Store-App hinzu und erstellt das entsprechende Manifest.

Wählen Sie die Schaltfläche Basics 1 aus, um den Rückgabewert der statischen GetAnswer-Methode dem Ausgabebereich zuzuweisen, eine Instanz der Example-Klasse zu erstellen und den Wert der SampleProperty-Eigenschaft im Ausgabebereich anzuzeigen. Die Ausgabe sieht wie folgt aus:

"The answer is 42." 0

Wählen Sie die Schaltfläche Basics 2 aus, um den Wert der SampleProperty-Eigenschaft zu erhöhen und den neuen Wert im Ausgabebereich anzuzeigen. Primitive Typen wie Zeichenfolgen und Zahlen können als Parametertypen und Rückgabetypen verwendet werden und zwischen verwaltetem Code und JavaScript ausgetauscht werden. Da Zahlen in JavaScript im Gleitkommaformat mit doppelter Genauigkeit gespeichert werden, werden sie in numerische .NET Framework-Typen umgewandelt.

System_CAPS_noteHinweis

Standardmäßig können Haltepunkte nur im JavaScript-Code festgelegt werden. Informationen zum Debuggen von Visual Basic- oder C#-Code erhalten Sie unter Erstellen von Windows-Runtime-Komponenten in C# und Visual Basic.

Wenn Sie den Debugvorgang unterbrechen und die Anwendung schließen möchten, wechseln Sie aus der Anwendung zu Visual Studio, und drücken Sie UMSCHALT+F5.

Die Windows-Runtime kann aus JavaScript oder verwaltetem Code aufgerufen werden.Windows-Runtime-Objekte können zwischen beiden hin und her übergeben werden, und Ereignisse können von beiden Seiten verarbeitet werden. Allerdings werden die Windows-Runtime-Typen in den beiden Umgebungen teilweise unterschiedlich verwendet, da JavaScript und .NET Framework Windows-Runtime anders unterstützen. Das folgende Beispiel zeigt diese Unterschiede anhand der Windows.Foundation.Collections.PropertySet-Klasse. Hier erstellen Sie eine Instanz der PropertySet-Auflistung in verwaltetem Code und registrieren einen Ereignishandler, um Änderungen in der Auflistung zu erfassen. Anschließend fügen Sie JavaScript-Code hinzu, der die Auflistung abruft, einen eigenen Ereignishandler registriert und die Auflistung verwendet. Abschließend fügen Sie eine Methode hinzu, mit der Änderungen an der Auflistung aus verwaltetem Code vorgenommen werden und die zeigt, wie JavaScript eine verwaltete Ausnahme behandelt.

System_CAPS_importantWichtig

In diesem Beispiel wird das Ereignis im UI-Thread ausgelöst. Wenn Sie das Ereignis beispielsweise in einem asynchronen Aufruf aus einem Hintergrundthread auslösen, müssen Sie zusätzliche Aufgaben ausführen, damit JavaScript das Ereignis behandelt. Weitere Informationen finden Sie unter Auslösen von Ereignissen in Windows-Runtime-Komponenten.

Fügen Sie im Projekt SampleComponent eine neue public sealed-Klasse (in Visual Basic Public NotInheritable-Klasse) mit dem Namen PropertySetStats hinzu. Die Klasse bindet eine PropertySet-Auflistung ein und behandelt das zugehörige MapChanged-Ereignis. Der Ereignishandler erfasst die Anzahl aller auftretenden Änderungen, und die DisplayStats-Methode erstellt einen in HTML formatierten Bericht. Achten Sie auf die zusätzliche using-Anweisung (in Visual Basic Imports-Anweisung). Fügen Sie diese den vorhandenen using-Anweisungen hinzu, anstatt sie zu überschreiben.

using Windows.Foundation.Collections; namespace SampleComponent { public sealed class PropertySetStats { private PropertySet _ps; public PropertySetStats() { _ps = new PropertySet(); _ps.MapChanged += this.MapChangedHandler; } public PropertySet PropertySet { get { return _ps; } } int[] counts = { 0, 0, 0, 0 }; private void MapChangedHandler(IObservableMap<string, object> sender, IMapChangedEventArgs<string> args) { counts[(int)args.CollectionChange] += 1; } public string DisplayStats() { StringBuilder report = new StringBuilder("<br/>Number of changes:<ul>"); for (int i = 0; i < counts.Length; i++) { report.Append("<li>" + (CollectionChange)i + ": " + counts[i] + "</li>"); } return report.ToString() + "</ul>"; } } }

Der Ereignishandler folgt dem bekannten .NET Framework-Ereignismuster. Allerdings wird der Absender des Ereignisses (in diesem Fall das PropertySet-Objekt) in die IObservableMap<string, object>-Schnittstelle umgewandelt (in Visual Basic IObservableMap(Of String, Object)). Diese ist eine Instantiierung der Windows-Runtime-Schnittstelle IObservableMap<K, V>. (Falls erforderlich, können Sie den Absender in den entsprechenden Typ umwandeln.) Auch die Ereignisargumente werden als Schnittstelle und nicht als Objekt dargestellt.

Ergänzen Sie die Datei "default.js" wie angegeben mit der Runtime1-Funktion. Dieser Code erstellt ein PropertySetStats-Objekt, ruft seine PropertySet-Auflistung ab und fügt den eigenen Ereignishandler, d. h. die onMapChanged-Funktion, hinzu, um das MapChanged-Ereignis zu behandeln. Nach Änderung der Auflistung ruft runtime1 die DisplayStats-Methode auf, um eine Zusammenfassung der Änderungstypen anzuzeigen.

var propertysetstats; function runtime1() { document.getElementById('output').innerHTML = ""; propertysetstats = new SampleComponent.PropertySetStats(); var propertyset = propertysetstats.propertySet; propertyset.addEventListener("mapchanged", onMapChanged); propertyset.insert("FirstProperty", "First property value"); propertyset.insert("SuperfluousProperty", "Unnecessary property value"); propertyset.insert("AnotherProperty", "A property value"); propertyset.insert("SuperfluousProperty", "Altered property value") propertyset.remove("SuperfluousProperty"); document.getElementById('output').innerHTML += propertysetstats.displayStats(); } function onMapChanged(change) { var result switch (change.collectionChange) { case Windows.Foundation.Collections.CollectionChange.reset: result = "All properties cleared"; break; case Windows.Foundation.Collections.CollectionChange.itemInserted: result = "Inserted " + change.key + ": '" + change.target.lookup(change.key) + "'"; break; case Windows.Foundation.Collections.CollectionChange.itemRemoved: result = "Removed " + change.key; break; case Windows.Foundation.Collections.CollectionChange.itemChanged: result = "Changed " + change.key + " to '" + change.target.lookup(change.key) + "'"; break; } document.getElementById('output').innerHTML += "<br/>" + result; }

Die Art und Weise, wie Windows-Runtime-Ereignisse in JavaScript behandelt werden, unterscheidet sich stark von der, wie sie in .NET Framework-Code behandelt werden. Der JavaScript-Ereignishandler nimmt nur ein Argument entgegen. Bei der Anzeige dieses Objekts im Visual Studio-Debugger ist die erste Eigenschaft der Absender. Die Mitglieder der Ereignisargumentschnittstelle werden direkt auf diesem Objekt angezeigt.

Um die App auszuführen, drücken Sie F5. Ist die Klasse nicht versiegelt, erhalten Sie eine Fehlermeldung, dass der Export des nicht versiegelten Typs "SampleComponent.Example" derzeit nicht unterstützt wird. Sie werden aufgefordert, ihn als versiegelt zu kennzeichnen.

Klicken Sie auf die Schaltfläche Runtime 1. Der Ereignishandler zeigt Änderungen an, wenn Elemente hinzugefügt oder geändert werden. Am Ende wird die DisplayStats-Methode aufgerufen, um die Anzahl der Änderungen zu erfassen. Um den Debugvorgang zu unterbrechen und die App zu schließen, wechseln Sie wieder zu Visual Studio, und drücken Sie UMSCHALT+F5.

Wenn Sie die PropertySet-Auflistung mit zwei weiteren Elementen aus verwaltetem Code ergänzen möchten, fügen Sie der PropertySetStats-Klasse folgenden Code hinzu:

public void AddMore() { _ps.Add("NewProperty", "New property value"); _ps.Add("AnotherProperty", "A property value"); }

Anhand dieses Codebeispiels lässt sich ein weiterer Unterschied in der Verwendung der Windows-Runtime-Typen in den beiden Umgebungen aufzeigen. Wenn Sie den Code selbst eingeben, sehen Sie, dass IntelliSense nicht die insert-Methode anzeigt, die Sie im JavaScript-Code verwendet haben. Stattdessen wird die Add-Methode angegeben, die sich in Auflistungen in .NET Framework häufig findet. Dies liegt daran, dass einige gängige Auflistungsschnittstellen zwar verschiedene Namen haben, in Windows-Runtime und .NET Framework aber ähnlich funktionieren. Werden diese Schnittstellen nun in verwaltetem Code verwendet, werden sie in Form ihrer .NET Framework-Entsprechungen angegeben. Weitere Informationen dazu erhalten Sie unterErstellen von Windows-Runtime-Komponenten in C# und Visual Basic. Wenn Sie dieselben Schnittstellen in JavaScript verwenden, unterscheidet sich dies zu Windows-Runtime nur darin, dass Großbuchstaben am Anfang von Mitgliedernamen in Kleinschreibung angezeigt werden.

Um die AddMore-Methode mit Ausnahmebehandlung aufzurufen, ergänzen Sie die Datei "default.js" abschließend mit der runtime2-Funktion.

function runtime2() { try { propertysetstats.addMore(); } catch (ex) { document.getElementById('output').innerHTML += "<br/><b>" + ex + "</b>"; } document.getElementById('output').innerHTML += propertysetstats.displayStats(); }

Um die App auszuführen, drücken Sie F5. Wählen Sie Runtime 1 und dann Runtime 2 aus. Der JavaScript-Ereignishandler meldet die erste Änderung an der Auflistung. Die zweite Änderung hat allerdings einen doppelten Schlüssel. Benutzer von .NET Framework-Wörterbüchern erwarten, dass die Add-Methode eine Ausnahme auslöst, und genau das passiert. JavaScript behandelt die Ausnahmen von .NET Framework.

System_CAPS_noteHinweis

Sie können die Ausnahmemeldung nicht mit JavaScript-Code anzeigen. Der Meldungstext wird durch eine Stapelüberwachung ersetzt. Weitere Informationen hierzu finden Sie unter "Throwing exceptions" in Erstellen von Windows-Runtime-Komponenten in C# und Visual Basic.

Im Gegensatz dazu würde, wenn die insert-Methode mit einem doppelten Schlüssel von JavaScript aufgerufen würde, sich der Wert des Elements ändern. Dieser Unterschied ist dadurch bedingt, dass JavaScript und .NET Framework Windows-Runtime verschieden unterstützen. Dies wurde unter Erstellen von Windows-Runtime-Komponenten in C# und Visual Basic entsprechend erläutert.

Wie bereits gesagt, können systemeigene Windows-Runtime-Typen ungehindert zwischen dem JavaScript-Code und dem C#- oder Visual Basic-Code hin- und hergereicht werden. In den meisten Fällen sind die Typ- und Mitgliedernamen in beiden Fällen gleich (außer, dass Mitgliedernamen in JavaScript mit einem Kleinbuchstaben beginnen). Im vorhergehenden Abschnitt aber schien die PropertySet-Klasse im verwalteten Code andere Member zu haben. (So haben Sie beispielsweise in JavaScript die insert-Methode und im .NET Framework-Code die Add-Methode aufgerufen.) In diesem Abschnitt wird erklärt, wie sich diese Unterschiede auf .NET Framework-Typen auswirken, die an JavaScript übergeben werden.

Neben der Fähigkeit, Windows-Runtime-Typen zurückzugeben, die Sie in Ihrer Komponente erstellt oder aus JavaScript an die Komponente übergeben haben, können Sie einen in verwaltetem Code erstellten verwalteten Typ so an JavaScript zurückgeben, als wäre es der entsprechende Windows-Runtime-Typ. Selbst im ersten einfachen Beispiel einer Runtime-Klasse waren die Parameter und Rückgabetypen der Mitglieder primitive Visual Basic- oder C#-Typen, bei denen es sich um .NET Framework-Typen handelt. Um dies bei Auflistungen zu demonstrieren, fügen Sie der Example-Klasse folgenden Code hinzu. Damit wird eine Methode erstellt, die ein generisches Wörterbuch an Zeichenfolgen zurückgibt, die mit einer ganzen Zahl indexiert sind:

public static IDictionary<int, string> GetMapOfNames() { Dictionary<int, string> retval = new Dictionary<int, string>(); retval.Add(1, "one"); retval.Add(2, "two"); retval.Add(3, "three"); retval.Add(42, "forty-two"); retval.Add(100, "one hundred"); return retval; }

Das Wörterbuch muss als Schnittstelle zurückgegeben werden, die von Dictionary<TKey, TValue> implementiert und einer Windows-Runtime-Schnittstelle zugeordnet wird. In diesem Fall ist die Schnittstelle IDictionary<int, string> (in Visual Basic IDictionary(Of Integer, String)). Wird der Windows-Runtime-Typ IMap<int, string> an verwalteten Code übergeben, wird er als IDictionary<int, string> angezeigt. Wird der verwaltete Typ an JavaScript übergeben, gilt das Gegenteil.

System_CAPS_importantWichtig

Implementiert ein verwalteter Typ mehrere Schnittstellen, nutzt JavaScript die Schnittstelle, die in der Liste an erster Stelle steht. Wenn Sie beispielsweise Dictionary<int, string> an JavaScript-Code zurückgeben, wird es stets als IDictionary<int, string> angezeigt, unabhängig vom angegebenen Rückgabetyp. Dies bedeutet, dass die erste Schnittstelle Member enthalten muss, die auch auf den neueren Schnittstellen erscheinen. Ansonsten ist ein Member nicht für JavaScript sichtbar.

Um die neue Methode zu testen und das Wörterbuch zu verwenden, ergänzen Sie die Datei "default.js" mit den Funktionen returns1 und returns2:

var names; function returns1() { names = SampleComponent.Example.getMapOfNames(); document.getElementById('output').innerHTML = showMap(names); } ct = 7 function returns2() { if (!names.hasKey(17)) { names.insert(43, "forty-three"); names.insert(17, "seventeen"); } else { var err = names.insert("7", ct++); names.insert("forty", "forty"); } document.getElementById('output').innerHTML = showMap(names); } function showMap(map) { var item = map.first(); var retval = "<ul>"; for (var i = 0, len = map.size; i < len; i++) { retval += "<li>" + item.current.key + ": " + item.current.value + "</li>"; item.moveNext(); } return retval + "</ul>"; }

Bei diesem JavaScript-Code gibt es einige interessante Punkte zu bemerken. Erstens enthält er eine showMap-Funktion zur Anzeige der Inhalte des Wörterbuchs in HTML. Achten Sie im Code für showMap auf das Iterationsmuster. .NET Framework enthält keine First-Methode in der generischen IDictionary-Schnittstelle, und die Größe wird von einer Count-Eigenschaft und nicht einer Size-Methode zurückgegeben. Für JavaScript ist IDictionary<int, string> scheinbar der Windows-Runtime-Typ IMap<int, string>. (Weitere Informationen finden Sie unter der IMap<K,V>-Schnittstelle.)

In der returns2-Funktion wie auch in vorherigen Beispielen ruft JavaScript die Insert-Methode (in JavaScript insert) auf, um Elemente zum Wörterbuch hinzuzufügen.

Um die App auszuführen, drücken Sie F5. Um die anfänglichen Inhalte des Wörterbuchs zu erstellen und anzuzeigen, klicken Sie auf Returns 1. Um das Wörterbuch mit weiteren Inhalten zu ergänzen, klicken Sie auf Returns 2. Die Einträge werden in der Reihenfolge angezeigt, in der sie ergänzt wurden, so wie bei Dictionary<TKey, TValue> zu erwarten ist. Wenn Sie die sortieren möchten, können Sie sich ein SortedDictionary<int, string> von GetMapOfNames zurückgeben lassen. (Die in vorherigen Beispielen verwendete PropertySet-Klasse ist intern anders aufgebaut als Dictionary<TKey, TValue>.)

Da JavaScript keine stark typisierte Sprache ist, ist die Verwendung stark typisierter generischer Auflistungen eventuell mit einigen überraschenden Ergebnissen verbunden. Klicken Sie erneut auf Returns 2. JavaScript zwingt die "7" in die Form einer numerischen 7, und die in ct gespeicherte numerische 7 in die einer Zeichenfolge. Zudem wird die Zeichenfolge "forty" auf null gebracht. Aber das ist nur der Anfang. Klicken Sie erneut mehrere Male auf Returns 2. In verwaltetem Code würde die Add-Methode identische Schlüsselausnahmen erzeugen, auch wenn die Werte zu den richtigen Typen hinzugefügt würden. Stattdessen aktualisiert die Insert-Methode den zugeordneten Wert durch einen vorhandenen Schlüssel und gibt einen Boolean-Wert zurück, der angibt, ob dem Wörterbuch ein neuer Schlüssel hinzugefügt wurde. Daher verändert sich der dem Schlüssel "7" zugeordnete Wert ständig.

Ein anderes unerwartetes Ergebnis ist, dass wenn Sie eine nicht zugewiesene JavaScript-Variable als Zeichenfolgenargument übergeben, Sie die Zeichenfolge "undefined" erhalten. Gehen Sie deshalb mit Bedacht vor, wenn Sie .NET Framework-Auflistungstypen an Ihren JavaScript-Code übergeben.

System_CAPS_noteHinweis

Bei vielen zu verkettenden Texten empfiehlt es sich, den Code in eine .NET Framework-Methode einzufügen und die StringBuilder-Klasse zu verwenden, wie in der showMap-Funktion gezeigt.

Obwohl Sie keine eigenen generischen Typen von einer Windows-Runtime-Komponente zur Verfügung stellen lassen können, können Sie generische .NET Framework-Auflistungen für Windows-Runtime-Klassen zurückgeben lassen, indem Sie folgenden Code verwenden:

public static object GetListOfThis(object obj) { Type target = obj.GetType(); return Activator.CreateInstance(typeof(List<>).MakeGenericType(target)); }

List<T> implementiert IList<T>, was in JavaScript als der Windows-Runtime-Typ IVector<T> angezeigt wird.

Ereignisse können Sie mithilfe des standardmäßigen .NET Framework-Ereignismusters oder anderer in Windows-Runtime verwendeter Muster deklarieren. In .NET Framework sind die Delegaten System.EventHandler<TEventArgs> und Windows-Runtime EventHandler<T> gleichwertig. Mit EventHandler<TEventArgs> lässt sich das standardmäßige .NET Framework-Muster somit gut implementieren. Um zu sehen, wie das funktioniert, fügen Sie dem SampleComponent-Projekt folgendes Klassenpaar hinzu:

namespace SampleComponent { public sealed class Eventful { public event EventHandler<TestEventArgs> Test; public void OnTest(string msg, long number) { EventHandler<TestEventArgs> temp = Test; if (temp != null) { temp(this, new TestEventArgs() { Value1 = msg, Value2 = number }); } } } public sealed class TestEventArgs { public string Value1 { get; set; } public long Value2 { get; set; } } }

Wenn Sie ein Ereignis in Windows-Runtime zur Verfügung stellen, erbt die Ereignisargumentklasse von System.Object. Sie erbt nicht von System.EventArgs wie im .NET Framework, weil EventArgs kein Windows-Runtime-Typ ist.

System_CAPS_noteHinweis

Zur Deklaration benutzerdefinierter Ereignisaccessoren für Ihr Ereignis (in Visual Basic Custom-Schlüsselwort), verwenden Sie das Windows-Runtime-Ereignismuster. Weitere Informationen finden Sie unter Benutzerdefinierte Ereignisse und Ereignisaccessoren der Windows-Runtime-Komponenten.

Um das Test-Ereignis zu behandeln, ergänzen Sie die Datei "default.js" mit der events1-Funktion. Die events1-Funktion erstellt eine Ereignishandlerfunktion für das Test-Ereignis und ruft zum Auslösen des Ereignisses sofort die OnTest-Methode auf. Wenn Sie einen Haltepunkt im Text des Ereignishandlers einbinden, sehen Sie, dass das Objekt, das dem einzelnen Parameter übergeben wird, das Quellobjekt und beide Mitglieder von TestEventArgs enthält.

var ev; function events1() { ev = new SampleComponent.Eventful(); ev.addEventListener("test", function (e) { document.getElementById('output').innerHTML = e.value1; document.getElementById('output').innerHTML += "<br/>" + e.value2; }); ev.onTest("Number of feet in a mile:", 5280); }

.NET Framework hat viele auf der Task-Klasse sowie auf generischen Task<TResult>-Klassen beruhende Tools für die asynchrone Verarbeitung und die Parallelverarbeitung. Um eine aufgabenbasierte asynchrone Verarbeitung in einer Komponente für Windows-Runtime zu ermöglichen, verwenden Sie die Windows-Runtime-Schnittstellen IAsyncAction, IAsyncActionWithProgress<TProgress>, IAsyncOperation<TResult> und IAsyncOperationWithProgress<TResult, TProgress>. (In Windows-Runtime geben Vorgänge Ergebnisse zurück, nicht aber Aktionen.)

Dieser Abschnitt zeigt einen abbrechbaren asynchronen Vorgang, mit dem der Status erfasst und Ergebnisse zurückgegeben werden. Die GetPrimesInRangeAsync-Methode verwendet die AsyncInfo-Klasse, um eine Aufgabe zu generieren und ihre Funktionen für Abbruch und Statuserfassung an ein WinJS.Promise-Objekt zu binden. Fügen Sie zuerst der using-Klasse die folgenden Imports-Anweisungen (in Visual Basic Example) hinzu:

using System.Runtime.InteropServices.WindowsRuntime; using Windows.Foundation;

Fügen Sie dann der GetPrimesInRangeAsync-Klasse die Example-Methode hinzu:

public static IAsyncOperationWithProgress<IList<long>, double> GetPrimesInRangeAsync(long start, long count) { if (start < 2 || count < 1) throw new ArgumentException(); return AsyncInfo.Run<IList<long>, double>((token, progress) => Task.Run<IList<long>>(() => { List<long> primes = new List<long>(); double onePercent = count / 100; long ctProgress = 0; double nextProgress = onePercent; for (long candidate = start; candidate < start + count; candidate++) { ctProgress += 1; if (ctProgress >= nextProgress) { progress.Report(ctProgress / onePercent); nextProgress += onePercent; } bool isPrime = true; for (long i = 2, limit = (long)Math.Sqrt(candidate); i <= limit; i++) { if (candidate % i == 0) { isPrime = false; break; } } if (isPrime) primes.Add(candidate); token.ThrowIfCancellationRequested(); } progress.Report(100.0); return primes; }, token) ); }

GetPrimesInRangeAsync ist als Programm zur Primzahlensuche bewusst sehr einfach gehalten. Da es hier vor allem um das Implementieren eines asynchronen Vorgangs geht, ist Einfachheit wichtig, und eine langsame Implementierung ist bei der Veranschaulichung eines Abbruchs von Vorteil.GetPrimesInRangeAsync ermittelt Primzahlen auf die primitive Art: Ein Kandidat wird durch alle ganzen Zahlen dividiert, die kleiner oder gleich der entsprechenden Quadratwurzel sind, statt dass nur die Primzahlen verwendet werden. Erklärung zu diesem Code:

  • Bevor Sie einen asynchronen Vorgang starten, überprüfen Sie die Parameter, und lösen Sie Ausnahmen für ungültige Eingaben aus.

  • Das Wichtigste an dieser Implementierung ist die AsyncInfo.Run<TResult, TProgress>(Func<CancellationToken, IProgress<TProgress>, Task<TResult>>)-Methode und der Delegat, d. h. der einzige Parameter der Methode. Der Delegat muss ein Abbruchtoken und eine Schnittstelle zur Statusmeldung akzeptieren sowie eine begonnene Aufgabe zurückgeben, die diese Parameter verwendet. Wenn die GetPrimesInRangeAsync-Methode in JavaScript aufgerufen wird, treten die folgenden Schritte auf (allerdings nicht unbedingt in der hier angegebenen Reihenfolge):

    • Das WinJS.Promise-Objekt stellt Funktionen bereit, um die zurückgegebenen Ergebnisse zu verarbeiten, auf den Abbruch zu reagieren und Statusberichte zu behandeln.

    • Die AsyncInfo.Run-Methode erstellt eine Abbruchquelle und ein Objekt, das die IProgress<T>-Schnittstelle implementiert. An den Delegaten übergibt sie sowohl ein CancellationToken-Token von der Abbruchquelle als auch eines von der IProgress<T>-Schnittstelle.

      System_CAPS_noteHinweis

      Wenn das Promise-Objekt keine Funktion bereitstellt, um auf den Abbruch zu reagieren, übergibt AsyncInfo.Run trotzdem ein abbrechbares Token, und der Abbruch kann nach wie vor erfolgen. Stellt das Promise-Objekt keine Funktion bereit, um Statusaktualisierungen zu behandeln, liefert AsyncInfo.Run trotzdem ein Objekt, das IProgress<T> implementiert. Die zugehörigen Berichte aber werden ignoriert.

    • Der Delegat erstellt mit der Task.Run<TResult>(Func<TResult>, CancellationToken)-Methode eine begonnene Aufgabe, in der Token und Statusschnittstelle verwendet werden. Der Delegat für die begonnene Aufgabe wird von einer Lambda-Funktion bereitgestellt, die das gewünschte Ergebnis berechnet. Weitere Informationen folgen im Anschluss.

    • Die AsyncInfo.Run-Methode erstellt ein Objekt, das die IAsyncOperationWithProgress<TResult, TProgress>-Schnittstelle implementiert, den Windows-Runtime-Abbruchmechanismus mit der Tokenquelle und die Funktion zur Statuserfassung des Promise-Objekts mit der IProgress<T>-Schnittstelle verbindet.

    • Die IAsyncOperationWithProgress<TResult, TProgress>-Schnittstelle wird an JavaScript zurückgegeben.

  • Die von der begonnenen Aufgabe dargestellte Lambda-Funktion akzeptiert keine Argumente. Da es sich um eine Lambda-Funktion handelt, hat sie Zugriff auf das Token und die IProgress-Schnittstelle. Immer, wenn eine Kandidatenzahl ausgewertet wird, wird mithilfe der Lambda-Funktion Folgendes durchgeführt:

    • Es wird geprüft, ob der nächste Prozentpunkt des Status erreicht wurde. Falls ja, ruft die Lambda-Funktion die IProgress<T>.Report-Methode auf, und der Prozentsatz wird an die Funktion übergeben, die im Promise-Objekt zur Statusmeldung angegeben wurde.

    • Wurde der Vorgang abgebrochen, wird mithilfe des Abbruchtokens eine Ausnahme ausgelöst. Bei Aufrufen der IAsyncInfo.Cancel-Methode (welche die IAsyncOperationWithProgress<TResult, TProgress>-Schnittstelle erbt) wird mittels der von der AsyncInfo.Run-Methode hergestellten Verbindung sichergestellt, dass das Abbruchtoken benachrichtigt wird.

  • Nach Rückgabe der Liste mit den Primzahlen durch die Lambda-Funktion wird die Liste an die Funktion übergeben, die im WinJS.Promise-Objekt zur Verarbeitung der Ergebnisse angegeben ist.

Um die JavaScript-Zusicherung zu erstellen und den Abbruchmechanismus festzulegen, fügen Sie der Datei "default.js" die Funktionen asyncRun und asyncCancel hinzu.

var resultAsync; function asyncRun() { document.getElementById('output').innerHTML = "Retrieving prime numbers."; btnAsync.disabled = "disabled"; btnCancel.disabled = ""; resultAsync = SampleComponent.Example.getPrimesInRangeAsync(10000000000001, 2500).then( function (primes) { for (i = 0; i < primes.length; i++) document.getElementById('output').innerHTML += " " + primes[i]; btnCancel.disabled = "disabled"; btnAsync.disabled = ""; }, function () { document.getElementById('output').innerHTML += " -- getPrimesInRangeAsync was canceled. -- "; btnCancel.disabled = "disabled"; btnAsync.disabled = ""; }, function (prog) { document.getElementById('primeProg').value = prog; } ); } function asyncCancel() { resultAsync.cancel(); }


Durch Aufruf der asynchronen GetPrimesInRangeAsync-Methode erstellt die asyncRun-Funktion ein WinJS.Promise-Objekt. Die then-Methode des Objekts hat drei Funktionen, um die zurückgegebenen Ergebnisse zu verarbeiten, auf Fehler (einschließlich Abbruch) zu reagieren und Statusberichte zu behandeln. Bei diesem Beispiel werden die zurückgegebenen Ergebnisse im Ausgabebereich gedruckt. Bei Abbruch oder Fertigstellung werden die Schaltflächen zum Starten und Abbrechen des Vorgangs zurückgesetzt. Im Zuge der Berichterstellung zum Status wird auch das Statussteuerelement aktualisiert.

Die asyncCancel-Funktion ruft nur die cancel-Methode des WinJS.Promise-Objekts auf.

Um die App auszuführen, drücken Sie F5. Um den asynchronen Vorgang zu starten, klicken Sie auf Async. Das, was daraufhin geschieht, hängt von der Geschwindigkeit des Computers ab. Wenn die Statusleiste in Sekundenschnelle am rechten Ende ankommt, vergrößern Sie die Anfangszahl, die an GetPrimesInRangeAsync übergeben wird, mindestens einmal um den Faktor zehn. Sie können die Dauer des Vorgangs optimieren, indem Sie die Anzahl der Zahlen testweise erhöhen oder verringern. Am wirksamsten ist es, die Anfangszahl in der Mitte mit Nullen zu ergänzen. Um den Vorgang abzubrechen, klicken Sie auf Cancel Async.

Anzeigen: