Procédure pas à pas pour créer un composant C# ou Visual Basic et l’appeler à partir de JavaScript
Cette procédure pas à pas montre comment utiliser .NET avec Visual Basic ou C# pour créer vos propres types Windows Runtime, empaquetés dans un composant Windows Runtime, puis comment appeler ce composant à partir d’une application JavaScript plateforme Windows universelle (UWP).
Visual Studio facilite la création et le déploiement de vos propres types Windows Runtime personnalisés à l’intérieur d’un projet WRC (Windows Runtime Component) écrit avec C# ou Visual Basic, puis de référencer ce WRC à partir d’un projet d’application JavaScript et d’utiliser ces types personnalisés à partir de cette application.
En interne, vos types Windows Runtime peuvent utiliser toutes les fonctionnalités .NET qui sont autorisées dans une application Windows universelle (UWP).
Notes
Pour plus d’informations, consultez Composants Windows Runtime avec C# et Visual Basic et Présentation de .NET pour les applications UWP.
En externe, les membres de votre type peuvent exposer uniquement les types Windows Runtime pour leurs paramètres et valeurs de retour. Quand vous générez votre solution, Visual Studio génère votre projet .NET WRC, puis 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.
Remarque
.NET mappe automatiquement certains types .NET couramment utilisés, tels que les types de données primitifs et les types de collection, à leurs équivalents Windows Runtime. Ces types .NET 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 Composants Windows Runtime avec C# et Visual Basic.
Configuration requise :
- Windows 10
- Microsoft Visual Studio
Remarque
les projets plateforme Windows universelle (UWP) utilisant JavaScript ne sont pas pris en charge dans Visual Studio 2019. Consultez JavaScript et TypeScript dans Visual Studio 2019. Pour suivre cette rubrique, nous vous recommandons d’utiliser Visual Studio 2017. Consultez JavaScript dans Visual Studio 2017.
Création d’une classe Windows Runtime simple
Cette section crée une application UWP JavaScript et ajoute à la solution un projet de composant Windows Runtime Visual Basic ou C#. Il montre comment définir un type Windows Runtime, créer une instance du type à partir de JavaScript et appeler des membres statiques et d’instance. L’affichage visuel de l’exemple d’application est délibérément faible pour garder le focus sur le composant.
Dans Visual Studio, créez un projet JavaScript : dans la barre de menus, choisissez Fichier, Nouveau, Projet. Dans la section Modèles installés de la boîte de dialogue Nouveau projet , choisissez JavaScript, puis Windows, puis Universal. (Si Windows n’est pas disponible, vérifiez que vous utilisez Windows 8 ou version ultérieure.) Choisissez le modèle d’application vide et entrez SampleApp pour le nom du projet.
Créez le projet de composant : dans Explorateur de solutions, ouvrez le menu contextuel de la solution SampleApp, puis choisissez 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 Windows, puis Universal. Choisissez le modèle de composant Windows Runtime et entrez SampleComponent pour le nom du projet.
Remplacez le nom de la classe par Example. Notez que par défaut, la classe est marquée comme scellée publique (Public NotInheritable en Visual Basic). Toutes les classes Windows Runtime que vous exposez à partir de votre composant doivent être sealed.
Ajoutez deux membres simples à la classe, une méthode statique (méthode partagée 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
Facultatif : pour activer IntelliSense pour les membres nouvellement ajoutés, dans Explorateur de solutions, ouvrez le menu contextuel du projet SampleComponent, puis choisissez Générer.
Dans Explorateur de solutions, dans le projet JavaScript, ouvrez le menu contextuel des références, puis choisissez Ajouter une référence pour ouvrir le Gestionnaire de références. Sélectionnez Projets, puis Solution. Cochez la case pour le projet SampleComponent et choisissez OK pour ajouter une référence.
Appeler le composant à partir de JavaScript
Pour utiliser le type Windows Runtime à partir de JavaScript, ajoutez le code suivant dans la fonction anonyme dans le fichier default.js (dans le dossier js du projet) fourni par le modèle Visual Studio. Il doit suivre le gestionnaire d’événements app.oncheckpoint et avant l’appel à app.start.
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 de chaque nom de membre est remplacée par des majuscules en minuscules. Cette transformation fait partie de la prise en charge de JavaScript pour permettre l’utilisation naturelle de Windows Runtime. Les espaces de noms et les noms de classes sont cased Pascal. Les noms de membres sont en cas de chameau, à l’exception des noms d’événements, qui sont tous en minuscules. Consultez Utilisation de Windows Runtime en JavaScript. Les règles de casse de chameau peuvent être déroutantes. Une série de lettres majuscules initiales apparaît normalement en minuscules, mais si trois lettres majuscules sont suivies d’une lettre 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, .NET prend en charge l’utilisation naturelle de Windows Runtime dans le code managé. Ceci est abordé dans les sections suivantes de cet article, ainsi que dans les articles des composants Windows Runtime avec la prise en charge C# et Visual Basic et .NET pour les applications UWP et Windows Runtime.
Créer une interface utilisateur simple
Dans votre projet JavaScript, ouvrez le fichier default.html et mettez à jour le corps, comme indiqué dans le code suivant. Ce code inclut l’ensemble complet de contrôles pour l’exemple d’application et spécifie les noms de fonction pour les événements click.
Notez que lorsque vous exécutez l’application pour la première fois, seul le bouton De base1 et De base2 est pris en charge.
<body>
<div id="buttons">
<button id="button1" >Basics 1</button>
<button id="button2" >Basics 2</button>
<button id="runtimeButton1">Runtime 1</button>
<button id="runtimeButton2">Runtime 2</button>
<button id="returnsButton1">Returns 1</button>
<button id="returnsButton2">Returns 2</button>
<button id="events1Button">Events 1</button>
<button id="btnAsync">Async</button>
<button id="btnCancel" 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 du corps comme indiqué, puis ajoutez des styles pour contrôler la disposition des boutons et le placement 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;
}
Ajoutez maintenant le code d’inscription de l’écouteur d’événements en ajoutant une clause à l’appel processAll dans app.onactivated dans default.js. Remplacez la ligne de code existante qui appelle setPromise et remplacez-la par le code suivant :
args.setPromise(WinJS.UI.processAll().then(function () {
var button1 = document.getElementById("button1");
button1.addEventListener("click", basics1, false);
var button2 = document.getElementById("button2");
button2.addEventListener("click", basics2, false);
}));
Il s’agit d’un meilleur moyen d’ajouter des événements aux contrôles HTML que d’ajouter un gestionnaire d’événements click directement dans HTML. Consultez Créer une application « Hello, World » (JS).
Générer et exécuter l’application
Avant de générer, remplacez la plateforme cible pour tous les projets par Arm, x64 ou x86, selon les besoins de votre ordinateur.
Pour générer et exécuter la solution, choisissez la clé F5. (Si vous obtenez un message d’erreur d’exécution indiquant que SampleComponent n’est pas défini, la référence au projet de bibliothèque de classes est manquante.)
Visual Studio compile d’abord la bibliothèque de classes, puis exécute une tâche MSBuild qui s’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 à la fois le code managé et les métadonnées Windows qui décrivent le code. WinMdExp.exe génère des messages d’erreur de génération lorsque vous écrivez du code non valide dans un composant Windows Runtime et que les messages d’erreur sont affichés dans l’IDE Visual Studio. Visual Studio ajoute votre composant au package d’application (fichier .appx) pour votre application UWP et génère le manifeste approprié.
Choisissez le bouton De base 1 pour affecter la valeur de retour de la méthode GetAnswer statique à la zone de sortie, créez une instance de la classe Example et affichez la valeur de sa propriété SampleProperty dans la zone de sortie. La sortie est affichée ici :
"The answer is 42."
0
Choisissez le bouton De base 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 comme types de paramètres et types de retour, et peuvent être passés entre le code managé et JavaScript. Étant donné que les nombres en JavaScript sont stockés au format à virgule flottante double précision, ils sont convertis en types numériques .NET Framework.
Notez par défaut que vous pouvez définir des points d’arrêt uniquement 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, puis choisissez Maj+F5.
Utilisation de Windows Runtime à partir de JavaScript et du code managé
Windows Runtime peut être appelé à partir de JavaScript ou de code managé. Les objets Windows Runtime peuvent être passés entre les deux, et les événements peuvent être gérés de l’un ou l’autre côté. Toutefois, les méthodes d’utilisation des types Windows Runtime dans les deux environnements diffèrent dans certains détails, car JavaScript et .NET prennent en charge Windows Runtime différemment. L’exemple suivant illustre ces différences, à l’aide de la classe Windows.Foundation.Collections.PropertySet . Dans cet exemple, vous créez une instance de la collection PropertySet dans le code managé et inscrivez un gestionnaire d’événements pour suivre les modifications apportées à la collection. Ensuite, vous ajoutez du code JavaScript qui obtient la collection, inscrit son propre gestionnaire d’événements et utilise la collection. Enfin, vous ajoutez une méthode qui apporte des modifications à la collection à partir du code managé et affiche la gestion javaScript d’une exception managée.
Important dans cet exemple, l’événement est déclenché sur le thread d’interface utilisateur. Si vous déclenchez l’événement à partir d’un thread d’arrière-plan, par exemple dans un appel asynchrone, vous devez effectuer un travail supplémentaire pour que JavaScript gère l’événement. Pour plus d’informations, consultez Déclenchement d’événements dans les composants Windows Runtime.
Dans le projet SampleComponent, ajoutez une nouvelle classe sealed publique (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 suit le nombre de modifications de chaque type qui se produisent, et la méthode DisplayStats produit un rapport mis en forme en HTML. Notez l’instruction using supplémentaire (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 familier, sauf que l’expéditeur de l’événement (dans ce cas, l’objet PropertySet) est converti en chaîne IObservableMap<, interface objet> (IObservableMap(Of String, Object) en Visual Basic), qui est une instanciation de l’interface Windows Runtime IObservableMap<K, V>. (Vous pouvez convertir l’expéditeur en son type si nécessaire.) En outre, les arguments d’événement sont présentés en tant qu’interface plutôt qu’en tant qu’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;
default:
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 la façon dont vous les gérez dans le code .NET Framework. Le gestionnaire d’événements JavaScript ne prend qu’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, choisissez la touche F5. Si la classe n’est pas scellée, vous obtenez le message d’erreur « L’exportation du type non scellé « SampleComponent.Example » n’est actuellement pas prise en charge. Marquez-le comme scellé.
Choisissez le bouton Runtime 1 . Le gestionnaire d’événements affiche les modifications à mesure que des éléments sont ajoutés ou modifiés, et à la fin, 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 façon dont vous utilisez les types Windows Runtime dans les deux environnements. Si vous tapez ce code vous-même, vous remarquerez que IntelliSense n’affiche pas la méthode d’insertion que vous avez utilisée dans le code JavaScript. Au lieu de cela, elle affiche la méthode Add couramment vue sur les collections dans .NET. Cela est dû au fait que certaines interfaces de collection couramment utilisées ont des noms différents, mais des fonctionnalités similaires dans Windows Runtime et .NET. Lorsque vous utilisez ces interfaces dans le code managé, elles apparaissent comme leurs équivalents .NET Framework. Cela est abordé dans les composants Windows Runtime avec C# et Visual Basic. Lorsque vous utilisez les mêmes interfaces en JavaScript, la seule modification apportée à Windows Runtime est que les lettres majuscules au début des noms de membres deviennent minuscules.
Enfin, pour appeler la méthode AddMore avec gestion des exceptions, ajoutez la fonction runtime2 à default.js.
function runtime2() {
try {
propertysetstats.addMore();
}
catch(ex) {
document.getElementById('output').innerHTML +=
"<br/><b>" + ex + "<br/>";
}
document.getElementById('output').innerHTML +=
propertysetstats.displayStats();
}
Ajoutez le code d’inscription du gestionnaire d’événements de la même façon que vous l’avez fait précédemment.
var runtimeButton1 = document.getElementById("runtimeButton1");
runtimeButton1.addEventListener("click", runtime1, false);
var runtimeButton2 = document.getElementById("runtimeButton2");
runtimeButton2.addEventListener("click", runtime2, false);
Pour exécuter l’application, choisissez la touche F5. Choisissez Runtime 1 , puis Runtime 2. Le gestionnaire d’événements JavaScript signale la première modification apportée à la collection. Toutefois, la deuxième modification a une clé en double. Les utilisateurs des dictionnaires .NET Framework s’attendent à ce que la méthode Add lève une exception et c’est ce qui se passe. JavaScript gère l’exception .NET.
Notez que vous ne pouvez pas afficher le message de l’exception à partir du code JavaScript. Le texte du message est remplacé par une trace de pile. Pour plus d’informations, consultez « Levée d’exceptions » dans Création de composants Windows Runtime en C# et Visual Basic.
En revanche, lorsque JavaScript a appelé la méthode insert avec une clé en double, la valeur de l’élément a été modifiée. Cette différence de comportement est due aux différentes façons dont JavaScript et .NET prennent en charge Windows Runtime, comme expliqué dans les composants Windows Runtime avec C# et Visual Basic.
Retour de types managés à partir de votre composant
Comme indiqué précédemment, vous pouvez passer librement des types Windows Runtime natifs entre votre code JavaScript et votre code C# ou Visual Basic. La plupart du temps, les noms de type et les noms de membres seront les mêmes dans les deux cas (sauf que les noms de membres commencent par des lettres minuscules en JavaScript). Toutefois, dans la section précédente, la classe PropertySet semblait avoir différents membres dans le code managé. (Par exemple, en JavaScript, vous avez appelé la méthode insert et dans le code .NET que vous avez appelé la méthode Add.) Cette section explore la façon dont ces différences affectent les types .NET Framework passés à JavaScript.
Outre le renvoi de types Windows Runtime que vous avez créés dans votre composant ou transmis à votre composant à partir de JavaScript, vous pouvez renvoyer un type managé, créé dans le code managé, à JavaScript comme s’il s’agissait du type Windows Runtime correspondant. Même dans le premier exemple simple d’une classe 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é en tant qu’interface implémentée par Dictionary<TKey, TValue> et qui est mappée à une interface Windows Runtime. Dans ce cas, l’interface est IDictionary<int, string> (IDictionary(Of Integer, String) en Visual Basic). Lorsque l’int de type Windows Runtime IMap<est passé au code managé,> il apparaît en tant qu’int IDictionary<, string> et l’inverse est 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 dictionnaire<int, chaîne> au code JavaScript, il apparaît en tant qu’int IDictionary<, chaîne> quelle que soit l’interface que vous spécifiez comme type de retour. Cela signifie que si la première interface n’inclut pas de membre qui apparaît sur les interfaces ultérieures, ce membre n’est pas visible par 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);
}
var 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>";
}
Ajoutez le code d’inscription d’événement au même bloc que l’autre code d’inscription d’événement :
var returnsButton1 = document.getElementById("returnsButton1");
returnsButton1.addEventListener("click", returns1, false);
var returnsButton2 = document.getElementById("returnsButton2");
returnsButton2.addEventListener("click", returns2, false);
Il existe quelques points intéressants à observer sur ce code JavaScript. Tout d’abord, il inclut une fonction showMap pour afficher le contenu du dictionnaire en HTML. Dans le code de showMap, notez le modèle d’itération. Dans .NET, il n’existe aucune méthode First sur l’interface IDictionary générique, et la taille est retournée par une propriété Count plutôt que par une méthode Size. Pour JavaScript, IDictionary<int, la chaîne> semble être le type IMap<int windows Runtime, chaîne>. (Voir le IMap<K,interface V> .)
Dans la fonction returns2, comme dans les exemples précédents, JavaScript appelle la méthode Insert (insérer en JavaScript) pour ajouter des éléments au dictionnaire.
Pour exécuter l’application, choisissez la touche F5. Pour créer et afficher le contenu initial du dictionnaire, choisissez le bouton Renvoyer 1 . Pour ajouter deux entrées supplémentaires au dictionnaire, choisissez le bouton Retour 2 . Notez que les entrées sont affichées dans l’ordre d’insertion, comme vous l’attendiez à partir de Dictionary<TKey, TValue>. Si vous souhaitez les trier, vous pouvez retourner un int TridDictionary<, chaîne> de GetMapOfNames. (La classe PropertySet utilisée dans les exemples précédents a une organisation interne différente de Dictionary<TKey, TValue>.)
Bien sûr, JavaScript n’est pas un langage fortement typé. L’utilisation de collections génériques fortement typées peut entraîner des résultats surprenants. Choisissez à nouveau le bouton Renvoyer 2 . JavaScript obligingly coerce le « 7 » sur un nombre 7, et le nombre 7 stocké dans ct dans une chaîne. Et il force la chaîne « quarante » à zéro. Mais c’est seulement le début. Choisissez le bouton Retour 2 plusieurs fois. Dans le code managé, la méthode Add génère des exceptions de clé en double, même si les valeurs ont été converties en types corrects. En revanche, la méthode Insert met à jour la valeur associée à une clé existante et retourne une valeur booléenne qui indique si une nouvelle clé a été ajoutée au dictionnaire. C’est pourquoi la valeur associée à la clé 7 continue de changer.
Autre comportement inattendu : si vous passez une variable JavaScript non attribuée en tant qu’argument de chaîne, ce que vous obtenez est la chaîne « undefined ». En bref, faites attention lorsque vous passez des types de collection .NET Framework à votre code JavaScript.
Notez que si vous avez de grandes quantités de texte à concaténer, vous pouvez le faire plus efficacement en déplaçant le code dans une méthode .NET Framework et en utilisant la classe StringBuilder, comme illustré dans la fonction showMap.
Bien que vous ne puissiez pas exposer vos propres types génériques à partir d’un composant Windows Runtime, vous pouvez retourner des collections génériques .NET Framework pour les classes Windows Runtime à l’aide de code comme suit :
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
La liste<T> implémente IList<T>, qui apparaît en tant que type IVector<T> windows Runtime 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é T> De Gestionnaire d’événements Windows Runtime. L’utilisation de EventHandler<<TEventArgs> est donc un bon moyen d’implémenter le modèle .NET Framework standard. Pour voir comment cela fonctionne, ajoutez la paire de classes suivante 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’arguments d’événement hérite de System.Object. Il n’hérite pas de System.EventArgs, comme dans .NET, car EventArgs n’est pas un type Windows Runtime.
Si vous déclarez des accesseurs d’événements personnalisés pour votre événement (mot clé personnalisé en Visual Basic), vous devez utiliser le modèle d’événement Windows Runtime. Consultez les événements personnalisés et les accesseurs d’événements 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);
}
Ajoutez le code d’inscription d’événement au même bloc que l’autre code d’inscription d’événement :
var events1Button = document.getElementById("events1Button");
events1Button.addEventListener("click", events1, false);
Exposition d’opérations asynchrones
Le .NET Framework dispose d’un ensemble complet d’outils pour le traitement asynchrone et le traitement parallèle, en fonction des classes TResult> des<tâches et des tâches génériques. Pour exposer le traitement asynchrone basé sur des tâches dans un composant Windows Runtime, utilisez les interfaces Windows Runtime IAsyncAction, IAsyncActionWithProgress<TProgress>, IAsyncOperation<TResult> et IAsyncOperationWithProgress<TResult, TProgress>. (Dans Windows Runtime, les opérations retournent les résultats, mais les actions ne le font pas.)
Cette section illustre une opération asynchrone annulable qui signale la progression et retourne les résultats. La méthode GetPrimesInRangeAsync utilise la classe AsyncInfo pour générer une tâche et connecter ses fonctionnalités d’annulation et de création de rapports de progression à un objet WinJS.Promise. Commencez par ajouter la méthode GetPrimesInRangeAsync à l’exemple de classe :
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
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)
);
}
Imports System.Runtime.InteropServices.WindowsRuntime
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 est un finder de nombres premiers très simple, et c’est par conception. Le focus est ici sur l’implémentation d’une opération asynchrone, donc la simplicité est importante, et une implémentation lente est un avantage lorsque nous montrons l’annulation. GetPrimesInRangeAsync recherche les primes par force brute : il divise un candidat par tous les entiers inférieurs ou égaux à sa racine carrée, plutôt que d’utiliser uniquement les nombres premiers. Pas à pas dans ce code :
Avant de démarrer une opération asynchrone, effectuez des activités de nettoyage telles que la validation des paramètres et la levée d’exceptions pour une entrée non valide.
La clé de cette implémentation est la méthode AsyncInfo.Run<TResult, TProgress>(Func<CancellationToken, IProgress<TProgress>, Task<TResult>>) et 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 retourner une tâche démarrée qui utilise ces paramètres. Lorsque JavaScript appelle la méthode GetPrimesInRangeAsync, les étapes suivantes se produisent (pas nécessairement dans l’ordre donné ici) :
L’objet WinJS.Promise fournit des fonctions pour traiter les résultats retournés, réagir à l’annulation et gérer les rapports de progression.
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é, il transmet à la fois un jeton CancellationToken à partir de la source d’annulation et l’interface IProgress<T> .
Remarque Si l’objet Promise ne fournit pas de fonction pour réagir à l’annulation, AsyncInfo.Run transmet toujours un jeton annulable et l’annulation peut toujours 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 rapports 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é de la tâche démarrée est fourni par une fonction lambda qui calcule le résultat souhaité. Vous en saurez plus à ce sujet dans un instant.
La méthode AsyncInfo.Run crée un objet qui implémente l’interface TResult IAsyncOperationWithProgress<, TProgress>, connecte le mécanisme d’annulation Windows Runtime avec la source du jeton et connecte la fonction de création de rapports de progression de l’objet Promise à l’interface IProgress<T>.
L’interface IAsyncOperationWithProgress<TResult, TProgress> est retournée à JavaScript.
La fonction lambda représentée par la tâche démarrée ne prend aucun argument. Étant donné qu’il s’agit d’une fonction lambda, il a accès au jeton et à l’interface IProgress. Chaque fois qu’un nombre candidat est évalué, la fonction lambda :
- Vérifie si le prochain point de progression a été atteint. Si c’est le cas, la fonction lambda appelle IProgress<T>. Méthode de rapport, et le pourcentage est transmis à la fonction spécifiée par l’objet Promise pour la progression de la création de rapports.
- Utilise le jeton d’annulation pour lever une exception si l’opération a été annulée. Si la méthode IAsyncInfo.Cancel (que la méthode IAsyncOperationWithProgress<TResult, l’interface TProgress> hérite) a été appelée, la connexion configurée par la méthode AsyncInfo.Run garantit que le jeton d’annulation est averti.
Lorsque la fonction lambda retourne la liste des nombres premiers, la liste est passée à la fonction spécifiée par l’objet WinJS.Promise pour le traitement des résultats.
Pour créer la promesse JavaScript et configurer 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();
}
N’oubliez pas le code d’inscription d’événement identique à celui que vous avez fait précédemment.
var btnAsync = document.getElementById("btnAsync");
btnAsync.addEventListener("click", asyncRun, false);
var btnCancel = document.getElementById("btnCancel");
btnCancel.addEventListener("click", asyncCancel, false);
En appelant la méthode GetPrimesInRangeAsync asynchrone, la fonction asyncRun crée un objet WinJS.Promise. La méthode de l’objet prend ensuite trois fonctions qui traitent les résultats retournés, réagissent aux erreurs (y compris l’annulation) et gèrent les rapports de progression. Dans cet exemple, les résultats retournés sont imprimés dans la zone de sortie. L’annulation ou la saisie semi-automatique réinitialise les boutons qui lancent et annulent l’opération. 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, choisissez la touche F5. Pour démarrer l’opération asynchrone, choisissez le bouton Asynchrone . Ce qui se passe ensuite dépend de la vitesse de votre ordinateur. Si la barre de progression se termine avant que vous ayez le temps de clignoter, augmentez la taille du numéro de départ passé à GetPrimesInRangeAsync par un ou plusieurs facteurs de dix. Vous pouvez affiner la durée de l’opération en augmentant ou en réduisant le nombre de nombres à tester, mais l’ajout de zéros au milieu du numéro de départ aura un impact plus important. Pour annuler l’opération, choisissez le bouton Annuler asynchrone .