Freigeben über


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 4.5 in Kombination mit Visual Basic oder C# eigene Windows-Runtime-Typen erstellt werden können, die in einer Windows-Runtime-Komponente gepackt sind, und wie die Komponente von der mit JavaScript für Windows erstellten Windows 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 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.

Hinweis

.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 Store-App mit JavaScript eingerichtet wird, können Sie die übrigen Abschnitte in beliebiger Reihenfolge bearbeiten.

  • Erstellen einer einfachen Windows-Runtime-Klasse

  • Verwenden von Windows-Runtime aus JavaScript und verwaltetem Code

  • Zurückgeben verwalteter Typen aus der Komponente

  • Deklarieren von Ereignissen

  • Bereitstellen asynchroner Vorgänge

Erforderliche Komponenten:

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

  • Windows 8

  • Microsoft Visual Studio 2012 oder Microsoft Visual Studio Express 2012 für Windows 8

Erstellen einer einfachen Windows-Runtime-Klasse

In diesem Abschnitt werden eine mit JavaScript für Windows erstellte Windows 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: Klicken Sie in der Menüleiste auf Datei, Neu, Projekt (in Visual Studio Express 2012 für Windows 8 klicken Sie auf Datei, Neues Projekt). 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 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; }
        }
    }
    
    Public NotInheritable Class Example
        Public Shared Function GetAnswer() As String
            Return "The answer is 42."
        End Function
    
        Public Property SampleProperty As Integer
    End Class
    
  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.

Hh779077.collapse_all(de-de,VS.110).gifKomponente aus JavaScript aufrufen

Um den Windows-Runtime-Typ aus JavaScript zu verwenden, fügen Sie am Ende der Datei "default.js" (im JS-Ordner des Projekts) nach der von der Visual Studio-Vorlage bereitgestellten vorhandenen 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 dazu erhalten Sie unter Verwenden der 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.

Hh779077.collapse_all(de-de,VS.110).gifEinfache Benutzeroberfläche erstellen

Ö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.

<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;
}

Hh779077.collapse_all(de-de,VS.110).gifApp erstellen und ausführen

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-Metadaten-Exporttool) 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 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.

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.

Hinweis

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.

Verwenden von Windows-Runtime aus JavaScript und verwaltetem Code

Windows-Runtime kann entweder von JavaScript oder von verwaltetem Code aufgerufen werden. Windows-Runtime-Objekte können zwischen den beiden hin und her übergeben werden, und Ereignisse können von jeder Seite bearbeitet 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.

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>";
        }
    }
}
Imports System.Text

Public NotInheritable Class PropertySetStats
    Private _ps As PropertySet
    Public Sub New()
        _ps = New PropertySet()
        AddHandler _ps.MapChanged, AddressOf Me.MapChangedHandler
    End Sub

    Public ReadOnly Property PropertySet As PropertySet
        Get
            Return _ps
        End Get
    End Property

    Dim counts() As Integer = {0, 0, 0, 0}
    Private Sub MapChangedHandler(ByVal sender As IObservableMap(Of String, Object),
        ByVal args As IMapChangedEventArgs(Of String))

        counts(CInt(args.CollectionChange)) += 1
    End Sub

    Public Function DisplayStats() As String
        Dim report As New StringBuilder("<br/>Number of changes:<ul>")
        For i As Integer = 0 To counts.Length - 1
            report.Append("<li>" & CType(i, CollectionChange).ToString() &
                          ": " & counts(i) & "</li>")
        Next
        Return report.ToString() & "</ul>"
    End Function
End Class

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");
        }
    Public Sub AddMore()
        _ps.Add("NewProperty", "New property value")
        _ps.Add("AnotherProperty", "A property value")
    End Sub

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.

Hinweis

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.

Zurückgeben verwalteter Typen aus der Komponente

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 Mitglieder 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;
        }
    Public Shared Function GetMapOfNames() As IDictionary(Of Integer, String)
        Dim retval As New Dictionary(Of Integer, 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
    End Function

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.

Wichtig

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>. (Sehen Sie sich die Schnittstelle IMap<K,V> an.)

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.

Hinweis

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));
        }
    Public Shared Function GetListOfThis(obj As Object) As Object
        Dim target As Type = obj.GetType()
        Return Activator.CreateInstance(GetType(List(Of )).MakeGenericType(target))
    End Function

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

Deklarieren von Ereignissen

Ereignisse können Sie mithilfe des standardmäßigen .NET Framework-Ereignismusters oder anderer in Windows-Runtime verwendeter Muster deklarieren. In .NET Framework sind die DelegatenSystem.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; }
    }
}
Public NotInheritable Class Eventful
    Public Event Test As EventHandler(Of TestEventArgs)
    Public Sub OnTest(ByVal msg As String, ByVal number As Long)
        RaiseEvent Test(Me, New TestEventArgs() With {
                            .Value1 = msg,
                            .Value2 = number
                            })
    End Sub
End Class

Public NotInheritable Class TestEventArgs
    Public Property Value1 As String
    Public Property Value2 As Long
End Class

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.

Hinweis

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 Komponenten für Windows-Runtime.

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);
}

Bereitstellen asynchroner Vorgänge

.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 Windows-Runtime-Komponente zu ermöglichen, verwenden Sie die Schnittstellen Windows-Runtime IAsyncActionIAsyncActionWithProgress<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 Example-Klasse die folgenden using-Anweisungen (in Visual Basic Imports) hinzu:

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

Fügen Sie dann der Example-Klasse die GetPrimesInRangeAsync-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)
            );
        }
    Public Shared Function GetPrimesInRangeAsync(ByVal start As Long, ByVal count As Long) As IAsyncOperationWithProgress(Of IList(Of Long), Double)

        If (start < 2 Or count < 1) Then Throw New ArgumentException()

        Return AsyncInfo.Run(Of IList(Of Long), Double)( _
            Function(token, prog)
                Return Task.Run(Of IList(Of Long))( _
                    Function()
                        Dim primes As New List(Of Long)
                        Dim onePercent As Long = count / 100
                        Dim ctProgress As Long = 0
                        Dim nextProgress As Long = onePercent

                        For candidate As Long = start To start + count - 1
                            ctProgress += 1

                            If ctProgress >= nextProgress Then
                                prog.Report(ctProgress / onePercent)
                                nextProgress += onePercent
                            End If

                            Dim isPrime As Boolean = True
                            For i As Long = 2 To CLng(Math.Sqrt(candidate))
                                If (candidate Mod i) = 0 Then
                                    isPrime = False
                                    Exit For
                                End If
                            Next

                            If isPrime Then primes.Add(candidate)

                            token.ThrowIfCancellationRequested()
                        Next
                        prog.Report(100.0)
                        Return primes
                    End Function, token)
            End Function)
    End Function

GetPrimesInRangeAsync ist als Programm zur Primzahlensuche bewusst sehr einfach gehalten. Da sich der Fokus hier auf dem Implementieren eines asynchronen Vorgangs befindet, ist Einfachheit wichtig, und eine langsame Implementierung ist bei der Veranschaulichung eines Abbruchs von Vorteil. GetPrimesInRangeAsync ermittelt Primzahlen wie folgt: 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.

      Hinweis

      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. Wenn die IAsyncInfo.Cancel-Methode (welche die IAsyncOperationWithProgress<TResult, TProgress>-Schnittstelle erbt), aufgerufen wurde, 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 Löschen-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.

Siehe auch

Konzepte

.NET für Windows Store-Apps – Übersicht

.NET für Windows Store-Apps – unterstützte APIs

Erstellen von Windows-Runtime-Komponenten in C# und Visual Basic

Asynchrone Programmierung mit Async und Await (C# und Visual Basic)

Erstellen von Windows-Runtime-Komponenten

Benutzerdefinierte Ereignisse und Ereignisaccessoren der Komponenten für Windows-Runtime

Weitere Ressourcen

.NET Framework-Unterstützung für Windows Store-Apps und Windows-Runtime