Partager via


Procédure pas à pas : création d'un composant simple en C# ou Visual Basic et appel de ce composant depuis JavaScript

Cette procédure pas à pas montre comment utiliser le .NET Framework 4.5 avec Visual Basic ou C# pour créer vos propres types Windows Runtime, empaquetés dans un composant Windows Runtime, et comment appeler le composant à partir de votre application Windows Store générée pour Windows à l'aide de JavaScript.

Visual Studio facilite l'ajout à votre application d'un composant Windows Runtime écrit avec C# ou Visual Basic, et la création de types Windows Runtime que vous pouvez appeler à partir de JavaScript. En interne, vos types Windows Runtime peuvent utiliser toutes les fonctionnalités du .NET Framework autorisées dans une application Windows Store. (Pour plus d'informations, consultez Création de composants Windows Runtime en C# et Visual Basic et Vue d'ensemble de .NET pour les applications Windows Store.) En externe, les membres de votre type peuvent exposer uniquement les types Windows Runtime pour leurs paramètres et valeurs de retour. Lorsque vous générez votre solution, Visual Studio génère votre projet de composant Windows Runtime .NET Framework, puis d'exécute une étape de génération qui crée un fichier de métadonnées Windows (.winmd). Il s'agit de votre composant Windows Runtime, que Visual Studio inclut dans votre application.

Notes

Le .NET Framework mappe automatiquement certains types .NET Framework couramment utilisés, tels que les types de données primitives et les types de collection, à leurs équivalents Windows Runtime. Ces types .NET Framework peuvent être utilisés dans l'interface publique d'un composant Windows Runtime et sont présentés aux utilisateurs du composant comme les types Windows Runtime correspondants. Consultez Création de composants Windows Runtime en C# et Visual Basic.

Cette procédure pas à pas décrit les tâches suivantes. Après avoir terminé la première section, dans laquelle l'application Windows Store est installée avec JavaScript, vous pouvez aborder les sections restantes dans n'importe quel ordre.

  • Création d'une classe Windows Runtime simple

  • Utilisation de Windows Runtime à partir de JavaScript et du code managé

  • Retour des types managés à partir de votre composant

  • Déclaration d'événements

  • Exposition d'opérations asynchrones

Composants requis :

Les éléments suivants sont nécessaires pour effectuer cette procédure pas à pas :

  • Windows 8

  • Microsoft Visual Studio 2012 ou Microsoft Visual Studio Express 2012 pour Windows 8

Création d'une classe Windows Runtime simple

Cette section crée une application Windows Store générée pour Windows à l'aide de JavaScript et ajoute un projet de composant Windows Runtime Visual Basic ou C#. Elle montre comment définir un type Windows Runtime managé, créer une instance du type à partir de JavaScript, puis appeler les membres statiques et les membres d'instance. L'affichage de l'exemple d'application est volontairement simple, pour que le composant soit le centre d'attention. N'hésitez pas à l'améliorer.

  1. Dans Visual Studio, créez un nouveau projet JavaScript : dans la barre de menus, choisissez Fichier, Nouveau, Projet (dans Visual Studio Express 2012 pour Windows 8, choisissez Fichier, Nouveau projet). Dans la section Modèles installés de la boîte de dialogue Nouveau projet, choisissez JavaScript, puis Windows Store. (Si Windows Store n'est pas disponible, vérifiez que vous utilisez Windows 8.) Choisissez le modèle Application vide et entrez SampleApp comme nom de projet.

  2. Créez le projet de composant : dans l'Explorateur de solutions, ouvrez le menu contextuel pour la solution SampleApp et choisissez Ajouter, puis Nouveau projet pour ajouter un nouveau projet C# ou Visual Basic à la solution. Dans la section Modèles installés de la boîte de dialogue Ajouter un nouveau projet, choisissez Visual Basic ou Visual C#, puis choisissez Windows Store. Choisissez le modèle Composant Windows Runtime et entrez SampleComponent comme nom de projet.

  3. Remplacez le nom de la classe par Exemple. Notez que la classe est marquée public sealed par défaut (Public NotInheritable en Visual Basic). Toutes les classes Windows Runtime que vous exposez à partir de votre composant doivent être sealed.

  4. Ajoutez deux membres simples à la classe, une méthode static (méthode Shared en Visual Basic) et une propriété d'instance :

    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. Facultatif : pour activer IntelliSense pour les membres récemment ajoutés, dans l'Explorateur de solutions, ouvrez le menu contextuel du projet SampleComponent, puis choisissez Générer.

  6. Dans l'Explorateur de solutions, dans le projet JavaScript, ouvrez le menu contextuel pour Références, puis choisissez Ajouter une référence pour ouvrir le Gestionnaire de références. Choisissez Solution, puis Projets. Activez la case à cocher pour le projet SampleComponent et choisissez OK pour ajouter une référence.

Hh779077.collapse_all(fr-fr,VS.110).gifAppelez le composant à partir de JavaScript

Pour utiliser le type Windows Runtime à partir de JavaScript, ajoutez le code suivant à la fin du fichier default.js (situé dans le dossier js du projet), après la fonction existante fournie par le modèle Visual Studio :

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

Notez que la première lettre majuscule de chaque nom de membre est remplacée par une minuscule. Cette transformation fait partie du support que JavaScript fournit pour activer l'utilisation naturelle de Windows Runtime. Les espaces de noms et les noms de classe utilisent la casse Pascal. Les noms de membres sont de casse mixte, à l'exception des noms d'événements, qui sont en minuscules. Consultez Utilisation de Windows Runtime en JavaScript. Les règles de casse mixte peuvent être ambigües. Une série de majuscules initiales apparaît normalement en minuscules, mais si trois lettres majuscules sont suivies par une minuscule, seules les deux premières lettres apparaissent en minuscules : par exemple, un membre nommé IDStringKind apparaît comme idStringKind. Dans Visual Studio, vous pouvez générer votre projet de composant Windows Runtime, puis utiliser IntelliSense dans votre projet JavaScript pour voir la casse correcte.

De la même façon, le .NET Framework offre une prise en charge pour activer l'utilisation naturelle de Windows Runtime en code managé. Ce sujet est abordé dans les sections suivantes de cet article, ainsi que dans les articles Création de composants Windows Runtime en C# et Visual Basic et Prise en charge .NET Framework pour les applications Windows Store et Windows Runtime.

Hh779077.collapse_all(fr-fr,VS.110).gifCréer une interface utilisateur simple

Dans votre projet JavaScript, ouvrez le fichier default.html et mettez à jour le corps, comme illustré dans le code suivant. Ce code inclut le jeu complet de contrôles pour l'exemple d'application et spécifie les noms de fonctions pour les événements Click.

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

Dans votre projet JavaScript, dans le dossier css, ouvrez default.css. Modifiez la section body comme illustré et ajoutez des styles pour contrôler la disposition des boutons et le positionnement du texte de sortie.

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(fr-fr,VS.110).gifGénérer et exécuter l'application

Pour générer et exécuter la solution, appuyez sur la touche F5. (Si vous recevez un message d'erreur d'exécution indiquant que SampleComponent n'est pas défini, la référence au projet Bibliothèque de classes est manquante.)

Visual Studio commence par compiler la bibliothèque de classes, puis exécute une tâche MSBuild qui exécute Winmdexp.exe (outil d'exportation de métadonnées Windows Runtime) pour créer votre composant Windows Runtime. Le composant est inclus dans un fichier .winmd qui contient le code managé et les métadonnées Windows qui décrivent le code. WinMdExp.exe génère des messages d'erreur de build lorsque vous écrivez du code non valide dans un composant Windows Runtime et les messages d'erreur sont affichés dans l'IDE de Visual Studio. Visual Studio ajoute votre composant au package d'application (fichier .appx) pour votre application Windows Store et génère le manifeste approprié.

Choisissez le bouton Basics 1 pour affecter la valeur de retour de la méthode statique GetAnswer à la zone de sortie, pour créer une instance de la classe Example et pour afficher la valeur de sa propriété SampleProperty dans la zone de sortie.

Choisissez le bouton Basics 2 pour incrémenter la valeur de la propriété SampleProperty et afficher la nouvelle valeur dans la zone de sortie. Les types primitifs tels que les chaînes et les nombres peuvent être utilisés en tant que types de paramètre et types de retour. Ils ne peuvent être passés entre le code managé et JavaScript. Étant donné que les nombres JavaScript sont enregistrés au format à virgule flottante double précision, ils sont convertis en types numériques .NET Framework.

Notes

Par défaut, vous pouvez uniquement définir des points d'arrêt dans votre code JavaScript. Pour déboguer votre code Visual Basic ou C#, consultez Création de composants Windows Runtime en C# et Visual Basic.

Pour arrêter le débogage et fermer votre application, passez de l'application à Visual Studio et choisissez Maj + F5.

Utilisation de Windows Runtime à partir de JavaScript et du code managé

Windows Runtime peut être appelé à partir d'un code JavaScript ou managé. Les objets Windows Runtime peuvent être passés dans les deux sens entre les deux, et les événements peuvent être gérés de chaque côté. Toutefois, l'utilisation des types Windows Runtime dans les deux environnements diffère, étant donné que JavaScript et le .NET Framework ne prennent pas en charge Windows Runtime de la même manière. L'exemple suivant illustre ces différences, à l'aide la classe Windows.Foundation.Collections.PropertySet. Dans cet exemple, vous créez une instance de la collection PropertySet en code managé et enregistrez un gestionnaire d'événements pour suivre les modifications apportées à la collection. Vous ajoutez ensuite du code JavaScript pour obtenir la collection, enregistrer son propre gestionnaire d'événements et utiliser la collection. Vous ajoutez enfin une méthode qui apporte des modifications à la collection à partir du code managé et présente la gestion d'une exception managée par JavaScript.

Dans le projet SampleComponent, ajoutez une nouvelle classe public sealed (classe Public NotInheritable en Visual Basic) nommée PropertySetStats. La classe encapsule une collection PropertySet et gère son événement MapChanged. Le gestionnaire d'événements assure le suivi du nombre de modifications de chaque type qui se produisent et la méthode DisplayStats produit un état au format HTML. Notez l'instruction supplémentaire using (instruction Imports en Visual Basic). Veillez à l'ajouter aux instructions using existantes plutôt que de les remplacer.

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

Le gestionnaire d'événements suit le modèle d'événement .NET Framework courant, mais l'expéditeur de l'événement (dans ce cas, l'objet PropertySet) est casté en interface IObservableMap<string, object> (IObservableMap(Of String, Object) en Visual Basic), qui est une instanciation de l'interface Windows Runtime IObservableMap<K, V>. (Vous pouvez caster l'expéditeur en son type si nécessaire.) En outre, les arguments d'événement se présentent sous la forme d'une interface plutôt que d'un objet.

Dans le fichier default.js, ajoutez la fonction Runtime1 comme indiqué. Ce code crée un objet PropertySetStats, obtient sa collection PropertySet et ajoute son propre gestionnaire d'événements, la fonction onMapChanged pour gérer l'événement MapChanged. Après avoir apporté des modifications à la collection, runtime1 appelle la méthode DisplayStats pour afficher un résumé des types de modifications.

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

La façon dont vous gérez les événements Windows Runtime en JavaScript est très différente de celle dont vous les gérez dans le code .NET Framework. Le gestionnaire d'événements JavaScript prend un seul argument. Lorsque vous affichez cet objet dans le débogueur Visual Studio, la première propriété est l'expéditeur. Les membres de l'interface d'argument d'événement apparaissent également directement sur cet objet.

Pour exécuter l'application, appuyez sur la touche F5. Si la classe n'est pas sealed, vous obtenez le message d'erreur, « L'exportation de type unsealed 'SampleComponent.Example' n'est pas prise en charge. Marquez le type 'SampleComponent.Example' comme sealed. »

Cliquez sur le bouton Runtime 1. Le gestionnaire d'événements affiche des modifications lorsque des éléments sont ajoutés ou modifiés. Au final, la méthode DisplayStats est appelée pour produire un résumé des nombres. Pour arrêter le débogage et fermer l'application, revenez à Visual Studio et choisissez Maj + F5.

Pour ajouter deux éléments supplémentaires à la collection PropertySet à partir du code managé, ajoutez le code suivant à la classe PropertySetStats :

        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

Ce code met en évidence une autre différence dans la manière dont vous utilisez les types Windows Runtime dans les deux environnements. Si vous tapez ce code vous-même, vous remarquerez qu'IntelliSense n'affiche pas la méthode insert que vous avez utilisée dans le code JavaScript. Au lieu de cela, il affiche la méthode Add qui apparaît en général sur des collections dans le .NET Framework. En effet, certaines interfaces de collection souvent utilisées ont des noms différents mais des fonctionnalités semblables dans Windows Runtime et le .NET Framework. Lorsque vous utilisez ces interfaces en code managé, elles s'affichent sous la forme de leurs équivalents .NET Framework. Ce sujet est abordé dans Création de composants Windows Runtime en C# et Visual Basic. Lorsque vous utilisez les mêmes interfaces en JavaScript, la seule différence par rapport à Windows Runtime est que les lettres majuscules au début des noms de membres sont remplacées par des minuscules.

Enfin, pour appeler la méthode AddMore avec la gestion des exceptions, ajoutez la fonction runtime2 à default.js.

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

    document.getElementById('output').innerHTML +=
        propertysetstats.displayStats();
}

Pour exécuter l'application, appuyez sur la touche F5. Choisissez Runtime 1 puis Runtime 2. Le gestionnaire d'événements JavaScript signale la première modification à la collection. La seconde modification, toutefois, comporte une clé dupliquée. Les utilisateurs des dictionnaires .NET Framework s'attendent à ce que la méthode Add lève une exception et c'est ce qui se produit. JavaScript gère l'exception .NET Framework.

Notes

Vous ne pouvez pas afficher le message de l'exception depuis le code JavaScript. Le texte du message est remplacé par une trace de la pile. Pour plus d'informations, consultez « Levée des exceptions » dans Création de composants Windows Runtime en C# et Visual Basic.

En revanche, lorsque JavaScript appelle la méthode insert avec une clé dupliquée, la valeur de l'élément est modifiée. Cette différence de comportement est due à la différence de prise en charge de Windows Runtime par JavaScript et le .NET Framework, comme expliqué dans Création de composants Windows Runtime en C# et Visual Basic.

Retour des types managés à partir de votre composant

Comme indiqué précédemment, vous pouvez passer librement des types Windows Runtime natifs dans les deux sens entre votre code JavaScript et votre code C# ou Visual Basic. Le plus souvent, les noms de types et les noms de membres sont identiques dans les deux cas (à ceci près que les noms de membres commencent par une lettre minuscule en JavaScript). Toutefois, dans la section précédente, la classe PropertySet semblait comporter des membres différents en code managé. (Par exemple vous avez appelé la méthode insert en JavaScript et la méthode Add dans le code .NET Framework.) Cette section traite de l'impact de ces différences sur les types .NET Framework passés à JavaScript.

En plus de retourner les types Windows Runtime que vous avez créés dans votre composant ou passés à votre composant à partir de JavaScript, vous pouvez retourner un type managé, créé en code managé, à JavaScript comme s'il s'agissait du type Windows Runtime correspondant. Même dans le premier exemple basique d'une classe Windows Runtime, les paramètres et les types de retour des membres étaient des types primitifs Visual Basic ou C#, qui sont des types .NET Framework. Pour illustrer cela pour les collections, ajoutez le code suivant à la classe Example, pour créer une méthode qui retourne un dictionnaire générique de chaînes, indexé par des entiers :

        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

Notez que le dictionnaire doit être retourné comme une interface implémentée par Dictionary<TKey, TValue>, et qui mappe vers une interface Windows Runtime. Dans ce cas, l'interface est IDictionary<int, string> (IDictionary(Of Integer, String) en Visual Basic). Lorsque le type Windows Runtime IMap<int, string> est passé au code managé, il apparaît sous la forme IDictionary<int, string>. L'inverse est également vrai lorsque le type managé est passé à JavaScript.

Important

Lorsqu'un type managé implémente plusieurs interfaces, JavaScript utilise l'interface qui apparaît en premier dans la liste. Par exemple, si vous retournez Dictionary<int, string> au code JavaScript, il apparaît comme IDictionary<int, string> quelle que soit l'interface que vous spécifiez comme type de retour. Cela signifie que si la première interface n'inclut pas un membre qui apparaît sur les interfaces ultérieures, ce membre n'est pas visible dans JavaScript.

Pour tester la nouvelle méthode et utiliser le dictionnaire, ajoutez les fonctions returns1 et returns2 à default.js :

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

Plusieurs éléments intéressants peuvent être observés à propos de ce code JavaScript. Tout d'abord, il contient une fonction showMap pour afficher le contenu du dictionnaire au format HTML. Dans le code de showMap, notez le modèle d'itération. Dans le .NET Framework, il n'existe aucune méthode First sur l'interface générique IDictionary et la taille est retournée par une propriété Count plutôt que par une méthode Size. Pour JavaScript, IDictionary<int, string> apparaît être le type Windows Runtime IMap<int, string>. (Consultez l'interface IMAP<K,V> l'interface.)

Dans la fonction returns2, comme dans les exemples précédents, JavaScript appelle la méthode Insert (insert en JavaScript) pour ajouter des éléments au dictionnaire.

Pour exécuter l'application, appuyez sur la touche F5. Pour créer et afficher le contenu initial du dictionnaire, choisissez le bouton Returns 1. Pour ajouter deux lignes supplémentaires au dictionnaire, choisissez le bouton Returns 2. Notez que les entrées sont affichées par ordre d'insertion, comme pour tout Dictionary<TKey, TValue>. Pour les trier, vous pouvez retourner une SortedDictionary<int, string> de GetMapOfNames. (La classe PropertySet utilisée dans les exemples précédents dispose d'une organisation interne différente de Dictionary<TKey, TValue>.)

Naturellement, JavaScript n'est pas un langage fortement typé. L'utilisation de collections génériques fortement typées peut entraîner des résultats étonnants. Choisissez à nouveau le bouton Returns 2. JavaScript convertit obligeamment le « 7 » en 7 numérique, et le 7 numérique stocké dans ct en chaîne. Il force la conversion de la chaîne « quarante » à zéro. Mais il ne s'agit que du début. Choisissez le bouton Returns 2 à plusieurs reprises. En code managé, la méthode Add génère des exceptions de clé dupliquée, même si les valeurs ont été castées en types corrects. En revanche, la méthode Insert met à jour la valeur associée à une clé existante et retourne une valeur Boolean qui indique si une nouvelle clé a été ajoutée au dictionnaire. C'est la raison pour laquelle la valeur associée à la clé 7 change constamment.

Voici un autre comportement inattendu : si vous passez une variable JavaScript non assignée en tant qu'argument de chaîne, vous obtenez la chaîne « non définie ». En résumé, soyez prudent lorsque vous passez des types de collection .NET Framework à votre code JavaScript.

Notes

Si vous devez concaténer de grandes quantités de texte, vous pouvez le faire de façon plus efficace en déplaçant le code dans une méthode .NET Framework et en utilisant la classe StringBuilder, comme indiqué dans la fonction showMap.

Même si vous ne pouvez pas exposer vos propres types génériques à partir d'un composant Windows Runtime, vous pouvez retourner les collections génériques .NET Framework pour les classes Windows Runtime à l'aide, par exemple, du code suivant :

        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> implémente IList<T>, qui apparaît comme le type Windows Runtime IVector<T> en JavaScript.

Déclaration d'événements

Vous pouvez déclarer des événements à l'aide du modèle d'événement .NET Framework standard ou d'autres modèles utilisés par Windows Runtime. Le .NET Framework prend en charge l'équivalence entre le délégué System.EventHandler<TEventArgs> et le délégué Windows Runtime EventHandler<T>. Utiliser EventHandler<TEventArgs> est donc un bon moyen d'implémenter le modèle .NET Framework standard. Pour en comprendre le fonctionnement, ajoutez les paires de classes suivantes au projet SampleComponent :

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

Lorsque vous exposez un événement dans Windows Runtime, la classe d'argument d'événement hérite de System.Object. Elle n'hérite pas de System.EventArgs, comme c'est le cas dans le .NET Framework, car EventArgs n'est pas un type Windows Runtime.

Notes

Si vous déclarez des accesseurs d'événement personnalisés pour votre événement (mot clé Custom en Visual Basic), vous devez utiliser le modèle d'événement Windows Runtime. Consultez Événements personnalisés et accesseurs d'événement dans les composants Windows Runtime.

Pour gérer l'événement Test, ajoutez la fonction events1 à default.js. La fonction events1 crée une fonction de gestionnaire d'événements pour l'événement Test et appelle immédiatement la méthode OnTest pour déclencher l'événement. Si vous placez un point d'arrêt dans le corps du gestionnaire d'événements, vous pouvez voir que l'objet passé au paramètre unique inclut l'objet source et les deux membres de TestEventArgs.

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

Exposition d'opérations asynchrones

Le .NET Framework possède un jeu complet d'outils pour le traitement asynchrone et le traitement en parallèle, en fonction des classes Task et des classes Task<TResult> génériques. Pour exposer un traitement asynchrone basé sur les tâches dans un composant Windows Runtime, utilisez les interfaces IAsyncAction, Windows Runtime IAsyncActionWithProgress<TProgress>, IAsyncOperation<TResult> et IAsyncOperationWithProgress<TResult, TProgress>. (Dans Windows Runtime, les opérations retournent des résultats, mais pas les actions.)

Cette section illustre une opération asynchrone annulable qui signale la progression et retourne des résultats. La méthode GetPrimesInRangeAsync utilise la classe AsyncInfo pour générer une tâche et pour connecter ses fonctionnalités d'annulation et de rapport de progression à un objet WinJS.Promise. Commencez par ajouter les instructions using suivantes (Imports en Visual Basic) à la classe Example :

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

Ajoutez ensuite la méthode GetPrimesInRangeAsync suivante à la classe Example :

        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

De par sa conception, GetPrimesInRangeAsync est un outil de recherche de nombres premiers simple. L'objectif principal est d'implémenter une opération asynchrone. La simplicité est donc importante et une implémentation lente constitue un avantage lorsque nous expliquons l'annulation. GetPrimesInRangeAsync trouve des nombres premiers en force brute : en divisant un candidat par tous les entiers qui sont inférieurs ou égaux à sa racine carrée, plutôt qu'en utilisant uniquement les nombres premiers. Exécution pas à pas du code :

  • Avant de commencer une opération asynchrone, effectuez des opérations de nettoyage, telles que la validation des paramètres et la levée d'exceptions pour les entrées non valides.

  • L'élément essentiel de cette implémentation est la méthode AsyncInfo.Run<TResult, TProgress>(Func<CancellationToken, IProgress<TProgress>, Task<TResult>>), ainsi que le délégué qui est le seul paramètre de la méthode. Le délégué doit accepter un jeton d'annulation et une interface pour signaler la progression et doit retourner une tâche démarrée qui utilise ces paramètres. Lorsque JavaScript appelle la méthode GetPrimesInRangeAsync, les étapes suivantes se produisent (l'ordre peut être différent de celui donné ici) :

    • L'objet WinJS.Promise fournit des fonctions pour traiter les résultats retournés, réagir à l'annulation et gérer des rapports d'avancement.

    • La méthode AsyncInfo.Run crée une source d'annulation et un objet qui implémente l'interface IProgress<T>. Pour le délégué, elle passe un jeton CancellationToken de la source d'annulation et l'interface IProgress<T>.

      Notes

      Si l'objet Promise ne fournit pas de fonction pour réagir à l'annulation, AsyncInfo.Run passe toujours un jeton annulable et l'annulation peut encore se produire. Si l'objet Promise ne fournit pas de fonction pour gérer les mises à jour de progression, AsyncInfo.Run fournit toujours un objet qui implémente IProgress<T>, mais ses états sont ignorés.

    • Le délégué utilise la méthode Task.Run<TResult>(Func<TResult>, CancellationToken) pour créer une tâche démarrée qui utilise le jeton et l'interface de progression. Le délégué pour la tâche démarrée est fourni par une fonction lambda qui calcule le résultat souhaité. Vous obtiendrez plus d'informations à ce sujet ultérieurement.

    • La méthode AsyncInfo.Run crée un objet qui implémente l'interface IAsyncOperationWithProgress<TResult, TProgress>, connecte le mécanisme d'annulation Windows Runtime à la source du jeton et connecte la fonction de rapport de progression Promise de l'objet à l'interface IProgress<T>.

    • L'interface IAsyncOperationWithProgress<TResult, TProgress> est retournée à JavaScript.

  • La fonction lambda qui est représentée par la tâche démarrée n'accepte aucun argument. Comme il s'agit d'une fonction lambda, elle a accès au jeton et à l'interface IProgress. Chaque fois qu'un numéro candidat est évalué, la fonction lambda :

    • Vérifie si le point de progression suivant a été atteint. Si c'est le cas, la fonction lambda appelle la méthode IProgress<T>.Report et le pourcentage est passé à la fonction spécifiée par l'objet Promise pour signaler la progression.

    • Utilise le jeton d'annulation pour lever une exception si l'opération a été annulée. Si la méthode IAsyncInfo.Cancel (dont l'interface IAsyncOperationWithProgress<TResult, TProgress> hérite) a été appelée, la connexion installée par la méthode AsyncInfo.Run garantit que le jeton d'annulation est informé.

  • Lorsque la fonction lambda retourne la liste de nombres premiers, cette dernière est passée à la fonction spécifiée par l'objet WinJS.Promise pour traiter les résultats.

Pour créer la méthode Promise JavaScript et installer le mécanisme d'annulation, ajoutez les fonctions asyncRun et asyncCancel à default.js.

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

En appelant la méthode asynchrone GetPrimesInRangeAsync, la fonction asyncRun crée un objet WinJS.Promise. La méthode then de l'objet utilise trois fonctions qui traitent les résultats retournés, réagissent aux erreurs (y compris à l'annulation) et gèrent des rapports d'avancement. Dans cet exemple, les résultats retournés sont imprimés dans la zone de sortie. L'annulation ou l'achèvement réinitialise les boutons qui activent et annulent l'exécution. Le rapport de progression met à jour le contrôle de progression.

La fonction asyncCancel appelle simplement la méthode cancel de l'objet WinJS.Promise.

Pour exécuter l'application, appuyez sur la touche F5. Pour démarrer l'opération asynchrone, choisissez le bouton Async. Les étapes suivantes dépendent de la rapidité de votre ordinateur. Si la barre de progression indique l'achèvement avant que vous ayez pu faire quoi que ce soit, augmentez la taille du numéro de départ passé à GetPrimesInRangeAsync d'un ou plusieurs facteurs sur dix. Vous pouvez affiner la durée de l'opération en augmentant ou en diminuant le nombre de nombres à tester. Cependant, ajouter des zéros au milieu du nombre de départ aura plus d'impact. Pour annuler l'opération, cliquez sur le bouton de Cancel Async.

Voir aussi

Concepts

Vue d'ensemble de .NET pour les applications Windows Store

.NET pour les applications Windows Store - API prises en charge

Création de composants Windows Runtime en C# et Visual Basic

Programmation asynchrone avec Async et Await (C# et Visual Basic)

Création de composants Windows Runtime

Événements personnalisés et accesseurs d'événement dans les composants Windows Runtime

Autres ressources

Prise en charge .NET Framework pour les applications Windows Store et Windows Runtime