Condividi tramite


Procedura dettagliata: creazione di un componente semplice in C# o Visual Basic e chiamata da JavaScript

In questa procedura dettagliata viene illustrato come utilizzare .NET Framework 4.5 con Visual Basic o C# per creare tipi di Windows Runtime personalizzati, inclusi in un pacchetto con un componente di Windows Runtime, e come chiamare il componente dall'app di Windows Store compilata per Windows utilizzando JavaScript.

In Visual Studio è facile aggiungere un componente di Windows Runtime scritto con C# o Visual Basic all'app e creare tipi di Windows Runtime che è possibile chiamare da JavaScript. Internamente, i tipi Windows Runtime possono utilizzare qualsiasi funzionalità di .NET Framework consentita in un'app di Windows Store. Per ulteriori informazioni, vedi Creazione di componenti Windows Runtime in C# e Visual Basic e Panoramica di .NET per le applicazioni Windows Store. Esternamente, i membri del tipo possono esporre solo tipi di Windows Runtime per i relativi parametri e valori restituiti. Quando compili la soluzione, in Visual Studio viene compilato il progetto .NET Framework Componente di Windows Runtime, quindi viene eseguita un'istruzione di compilazione che crea un file di metadati di Windows (con estensione winmd). Si tratta del componente di Windows Runtime incluso nell'app da Visual Studio.

Nota

.NET Framework esegue automaticamente il mapping di alcuni tipi .NET Framework di uso comune, come i tipi di dati e di raccolta primitivi, agli equivalenti di Windows Runtime. Questi tipi .NET Framework possono essere utilizzati nell'interfaccia pubblica di un componente di Windows Runtime e vengono visualizzati agli utenti del componente come tipi di Windows Runtime corrispondenti. Vedi Creazione di componenti Windows Runtime in C# e Visual Basic.

In questa procedura dettagliata vengono illustrate le attività seguenti. Dopo aver completato la prima sezione, in cui si imposta l'app Windows Store con JavaScript, puoi completare le sezioni rimanenti nell'ordine desiderato.

  • Creazione di una classe semplice di Windows Runtime

  • Utilizzo di Windows Runtime da JavaScript e codice gestito

  • Restituzione di tipi gestiti dal componente

  • Dichiarazione di eventi

  • Esposizione di operazioni asincrone

Prerequisiti:

Per completare questa procedura dettagliata, sono necessari i seguenti elementi:

  • Windows 8

  • Microsoft Visual Studio 2012 o Microsoft Visual Studio Express 2012 per Windows 8

Creazione di un classe semplice di Windows Runtime

In questa sezione viene creata un'app Windows Store compilata per Windows utilizzando JavaScript e viene aggiunto un progetto Visual Basic o C# di Componente di Windows Runtime. Viene illustrato come definire un tipo Windows Runtime gestito, creare un'istanza del tipo da JavaScript e chiamare membri statici e di istanza. L'aspetto visivo dell'app di esempio è deliberatamente monotono per focalizzare l'attenzione sul componente. Se lo desideri, puoi renderlo più accattivante.

  1. Crea un nuovo progetto JavaScript in Visual Studio: sulla barra dei menu scegli File, Nuovo, Progetto (in Visual Studio Express 2012 per Windows 8 scegli File, Nuovo progetto). Nella sezione Modelli installati della finestra di dialogo Nuovo progetto scegli JavaScript, quindi Windows Store. Se Windows Store non è disponibile, verifica di utilizzare Windows 8. Scegli il modello Applicazione vuota e immetti SampleApp come nome del progetto.

  2. Crea il progetto di componente: in Esplora soluzioni apri il menu di scelta rapida per la soluzione SampleApp e scegli Aggiungi, quindi scegli Nuovo progetto per aggiungere un nuovo progetto C# o Visual Basic alla soluzione. Nella sezione Modelli installati della finestra di dialogo Aggiungi nuovo progetto scegli Visual Basic o Visual C#, quindi Windows Store. Scegli il modello Componente di Windows Runtime e immetti SampleComponent come nome del progetto.

  3. Modifica il nome della classe in Example. Nota che per impostazione predefinita la classe è contrassegnata public sealed (Public NotInheritable in Visual Basic). Tutte le classi di Windows Runtime esposte dal componente devono essere sealed.

  4. Aggiungi due membri semplici alla classe, un metodo static (metodo Shared in Visual Basic) e una proprietà dell'istanza:

    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. Facoltativo: per abilitare IntelliSense per i membri appena aggiunti, in Esplora soluzioni apri il menu di scelta rapida del progetto SampleComponent e scegli Compila.

  6. In Esplora soluzioni, nel progetto JavaScript apri il menu di scelta rapida per Riferimenti, quindi scegli Aggiungi riferimento per aprire Gestione riferimenti. Scegli Soluzione, quindi Progetti. Seleziona la casella di controllo per il progetto SampleComponent e scegli OK per aggiungere un riferimento.

Hh779077.collapse_all(it-it,VS.110).gifChiamare il componente da JavaScript

Per utilizzare il tipo di Windows Runtime da JavaScript, aggiungi il codice seguente alla fine del file default.js (nella cartella js del progetto), dopo la funzione esistente fornita dal modello di 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;
}

Nota che la prima lettera del nome di ciascun membro viene modificata da maiuscola in minuscola. Questa trasformazione fa parte del supporto fornito da JavaScript per abilitare l'utilizzo naturale di Windows Runtime. I nomi degli spazi dei nomi e delle classi sono definiti in base alla convezione Pascal. I nomi dei membri sono definiti in base alla convenzione Camel, tranne per i nomi degli eventi che sono tutti in lettere minuscole. Vedi la pagina relativa all'utilizzo di Windows Runtime in JavaScript. Le regole per la convenzione Camel possono essere poco chiare. Una serie di lettere maiuscole iniziali viene in genere visualizzata in minuscolo, ma se tre lettere maiuscole sono seguite da una minuscola, solo le prime due lettere sono visualizzate in minuscolo: ad esempio un membro denominato IDStringKind viene visualizzato come idStringKind. In Visual Studio puoi compilare il progetto del componente di Windows Runtime, quindi utilizzare IntelliSense nel progetto JavaScript per visualizzare le maiuscole e minuscole corrette.

In modo analogo .NET Framework fornisce il supporto per abilitare l'utilizzo naturale di Windows Runtime nel codice gestito. Questo aspetto viene illustrato nelle sezioni successive di questo articolo e negli articoli Creazione di componenti Windows Runtime in C# e Visual Basic e Supporto .NET Framework per applicazioni Windows Store e Windows Runtime.

Hh779077.collapse_all(it-it,VS.110).gifCreare un'interfaccia utente semplice

Nel progetto JavaScript apri il file default.html e aggiorna il corpo come illustrato nel codice seguente. Questo codice include il set completo di controlli per l'app di esempio e specifica i nomi di funzione per gli eventi 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>

Nella cartella css del progetto JavaScript apri default.css. Modifica la sezione body come illustrato e aggiungi gli stili per controllare il layout dei pulsanti e la posizione del testo di output.

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(it-it,VS.110).gifCompilare ed eseguire l'app

Per compilare ed eseguire la soluzione, premi F5. Se viene visualizzato un messaggio di errore di runtime indicante che SampleComponent non è definito, il riferimento al progetto Libreria di classi è mancante.

In Visual Studio viene innanzitutto compilata la libreria di classi, quindi viene eseguita un'attività MSBuild che esegue Winmdexp.exe (Windows Runtime Metadata Export Tool) per creare il componente di Windows Runtime. Il componente è incluso in un file con estensione winmd che contiene sia il codice gestito sia i metadati di Windows che descrivono il codice. WinMdExp.exe genera messaggi di errore di compilazione quando scrivi codice non valido in un componente di Windows Runtime e tali messaggi vengono visualizzati nell'IDE di Visual Studio. In Visual Studio il componente viene aggiunto al pacchetto dell'app (file con estensione appx) per l'app Windows Store e viene generato il manifesto appropriato.

Scegli il pulsante Generale 1 per assegnare il valore restituito dal metodo statico GetAnswer all'area di output, crea un'istanza della classe Example e visualizza il valore della relativa proprietà SampleProperty nell'area di output.

Scegli il pulsante Generale 2 per incrementare il valore della proprietà SampleProperty e visualizza il nuovo valore nell'area di output. I tipi primitivi, ad esempio stringhe e numeri, possono essere utilizzati come tipi di parametri e tipi restituiti, e possono essere passati tra codice gestito e JavaScript. Poiché i numeri in JavaScript sono archiviati nel formato a virgola mobile a precisione doppia, vengono convertiti in tipi numerici .NET Framework.

Nota

Per impostazione predefinita, puoi impostare i punti di interruzione solo nel codice JavaScript. Per eseguire il debug del codice Visual Basic o C#, vedi Creazione di componenti Windows Runtime in C# e Visual Basic.

Per arrestare il debug e chiudere l'app, passa dall'app a Visual Studio e premi MAIUSC+F5.

Utilizzo di Windows Runtime da JavaScript e codice gestito

Windows Runtime può essere chiamato da JavaScript o dal codice gestito. Gli oggetti Windows Runtime possono essere passati avanti e indietro tra i due e gli eventi possono essere gestiti da qualsiasi lato. Tuttavia, le modalità in cui si utilizzano i tipi di Windows Runtime nei due ambienti si differenziano in alcuni aspetti, perché JavaScript e .NET Framework supportano Windows Runtime in modo diverso. Nell'esempio seguente vengono illustrate queste differenze, utilizzando la classe Windows.Foundation.Collections.PropertySet. In questo esempio, creerai un'istanza della raccolta PropertySet in codice gestito e registrerai un gestore eventi per rilevare le modifiche nella raccolta. Aggiungi quindi il codice JavaScript che ottiene la raccolta, registra il proprio gestore eventi e utilizza la raccolta. Infine, aggiungi un metodo con cui apportare le modifiche alla raccolta da codice gestito e mostrare la gestione di un'eccezione gestita in JavaScript.

Nel progetto SampleComponent aggiungi una nuova classe public sealed (classe Public NotInheritable in Visual Basic) denominata PropertySetStats. La classe esegue il wrapping di una raccolta PropertySet e ne gestisce l'evento MapChanged. Il gestore eventi tiene traccia del numero di modifiche di ogni tipo che si verificano e il metodo DisplayStats genera un report formattato in HTML. Nota l'istruzione using aggiuntiva (istruzione Imports in Visual Basic). Presta attenzione ad aggiungere questa istruzione alle istruzioni using esistenti, anziché sovrascriverle.

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

Il gestore eventi è basato sullo schema di eventi noto di .NET Framework, ad eccezione del fatto che viene eseguito il cast del mittente dell'evento (in questo caso l'oggetto PropertySet) nell'interfaccia IObservableMap<string, object> (IObservableMap(Of String, Object) in Visual Basic), che è la creazione di un'istanza dell'interfaccia IObservableMap<K, V> di Windows Runtime. Puoi eseguire il cast del mittente al relativo tipo se necessario. Inoltre, gli argomenti dell'evento sono presentati come interfaccia, anziché come oggetto.

Nel file default.js aggiungi la funzione Runtime1 come illustrato. Questo codice crea un oggetto PropertySetStats, ottiene la raccolta PropertySet e aggiunge il proprio gestore eventi, la funzione onMapChanged, per gestire l'evento MapChanged. Dopo avere apportato modifiche alla raccolta, runtime1 chiama il metodo DisplayStats per visualizzare un riepilogo dei tipi di modifiche.

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 modalità di gestione degli eventi Windows Runtime in JavaScript è molto diversa da quella utilizzata nel codice .NET Framework. Il gestore eventi JavaScript accetta un solo argomento. Quando visualizzi questo oggetto nel debugger di Visual Studio, la prima proprietà è il mittente. I membri dell'interfaccia di argomenti dell'evento vengono inoltre visualizzati direttamente in questo oggetto.

Per eseguire l'app, premi F5. Se la classe non è sealed, viene visualizzato un messaggio di errore "L'esportazione di tipi non sealed non è supportata. Contrassegna il tipo 'SampleComponent.Example' come sealed".

Scegli il pulsante Runtime 1. Il gestore eventi visualizza le modifiche quando gli elementi vengono aggiunti o modificati e alla fine viene chiamato il metodo DisplayStats per creare un riepilogo dei conteggi. Per arrestare il debug e chiudere l'app, torna a Visual Studio e premi MAIUSC+F5.

Per aggiungere altri due elementi alla raccolta PropertySet da codice gestito, aggiungi il codice seguente alla 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

In questo codice viene evidenziata un'altra differenza nella modalità di utilizzo dei tipi Windows Runtime nei due ambienti. Se digiti il codice manualmente, noterai che IntelliSense non mostra il metodo insert utilizzato nel codice JavaScript. Visualizza invece il metodo Add, presente comunemente nelle raccolte in .NET Framework. Ciò è dovuto al fatto che alcune interfacce di raccolta utilizzate comunemente hanno nomi diversi ma funzionalità simili in Windows Runtime e .NET Framework. Quando utilizzi queste interfacce nel codice gestito, vengono visualizzate come gli equivalenti di .NET Framework. Questo aspetto viene spiegato in Creazione di componenti Windows Runtime in C# e Visual Basic. Quando utilizzi le stesse interfacce in JavaScript, l'unica differenza rispetto a Windows Runtime è che le lettere maiuscole all'inizio dei nomi dei membri diventano minuscole.

Infine, per chiamare il metodo AddMore con la gestione delle eccezioni, aggiungi la funzione runtime2 in default.js.

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

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

Per eseguire l'app, premi F5. Scegli Runtime 1, quindi Runtime 2. Il gestore eventi JavaScript segnala la prima modifica alla raccolta. La seconda modifica, tuttavia, ha una chiave duplicata. Gli utenti di dizionari di .NET Framework prevedono che il metodo Add generi un'eccezione ed è ciò che accade. JavaScript gestisce l'eccezione di .NET Framework.

Nota

Non puoi visualizzare il messaggio dell'eccezione dal codice JavaScript. Il testo del messaggio viene sostituito da una traccia dello stack. Per ulteriori informazioni, vedi "Generazione di eccezioni" in Creazione di componenti Windows Runtime in C# e Visual Basic.

Al contrario, quando JavaScript ha chiamato il metodo insert con una chiave duplicata, il valore dell'elemento è stato modificato. Questa differenza di comportamento è dovuta alle diverse modalità di supporto di Windows Runtime in JavaScript e .NET Framework, come illustrato in Creazione di componenti Windows Runtime in C# e Visual Basic.

Restituzione di tipi gestiti dal componente

Come descritto in precedenza, puoi passare i tipi di Windows Runtime nativi liberamente tra il codice JavaScript e il codice C# o Visual Basic. Nella maggior parte dei casi, i nomi dei tipi e dei membri saranno gli stessi in entrambi i casi (tranne per il fatto che i nomi dei membri iniziano con la lettera minuscola in JavaScript). Tuttavia, nella sezione precedente la classe PropertySet risultava avere membri diversi nel codice gestito. Ad esempio, in JavaScript è stato chiamato il metodo insert e nel codice .NET Framework è stato chiamato il metodo Add. In questa sezione viene descritto l'effetto di queste differenze sui tipi .NET Framework passati a JavaScript.

Oltre a restituire i tipi di Windows Runtime creati nel componente o passati al componente da JavaScript, puoi restituire un tipo gestito, creato nel codice gestito, a JavaScript come se fosse il tipo di Windows Runtime corrispondente. Anche nel primo semplice esempio di classe di runtime, i parametri e i tipi restituiti dei membri erano tipi primitivi Visual Basic o C#, che sono tipi .NET Framework. Per dimostrare questo concetto per le raccolte, aggiungi il codice seguente alla classe Example, per creare un metodo che restituisce un dizionario generico di stringhe, indicizzate da Integer:

        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

Nota che il dizionario deve essere restituito come interfaccia implementata da Dictionary<TKey, TValue> e mappata a un'interfaccia Windows Runtime. In questo caso l'interfaccia è IDictionary<int, string> (IDictionary(Of Integer, String) in Visual Basic). Quando il tipo IMap<int, string> di Windows Runtime viene passato al codice gestito, viene visualizzato come IDictionary<int, string> e viceversa se il tipo gestito viene passato a JavaScript.

Importante

Quando un tipo gestito implementa più interfacce, JavaScript utilizza l'interfaccia visualizzata nell'elenco. Se ad esempio restituisci Dictionary<int, string> al codice JavaScript, viene visualizzato come IDictionary<int, string> indipendentemente dall'interfaccia specificata come tipo restituito. Ciò significa che se la prima interfaccia non include un membro visualizzato nelle interfacce successive, tale membro non è visibile a JavaScript.

Per verificare il nuovo metodo e utilizzare il dizionario, aggiungi le funzioni returns2 e returns1 in 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>";
}

Ci sono alcuni aspetti interessanti da osservare riguardo al codice JavaScript. Innanzitutto, include una funzione showMap per visualizzare il contenuto del dizionario in HTML. Nel codice per showMap, nota lo schema di iterazione. In .NET Framework, non esiste alcun metodo First nell'interfaccia generica IDictionary e la dimensione viene restituita da una proprietà Count, anziché da un metodo Size. Per JavaScript, IDictionary<int, string> risulta essere il tipo IMap<int, string> di Windows Runtime. Vedi l'interfaccia IMap<K,V>.

Nella funzione returns2, come negli esempi precedenti, JavaScript chiama il metodo Insert (insert in JavaScript) per aggiungere elementi al dizionario.

Per eseguire l'app, premi F5. Per creare e visualizzare il contenuto iniziale del dizionario, scegli il pulsante Valori restituiti 1. Per aggiungere altre due voci al dizionario, scegli il pulsante Valori restituiti 2. Nota che le voci sono visualizzate in ordine di inserimento, come previsto da Dictionary<TKey, TValue>. Se desideri ordinarle, puoi restituire un oggetto SortedDictionary<int, string> da GetMapOfNames. La classe PropertySet utilizzata negli esempi precedenti ha un'organizzazione interna diversa rispetto a Dictionary<TKey, TValue>.

JavaScript non è certamente un linguaggio fortemente tipizzato, pertanto l'utilizzo di raccolte generiche fortemente tipizzate può causare alcuni risultati imprevisti. Scegli di nuovo il pulsante Valori restituiti 2. JavaScript esegue l'assegnazione forzata di "7" a un 7 numerico e del 7 numerico archiviato in ct a una stringa. Esegue inoltre l'assegnazione forzata della stringa "forty" a zero. Si tratta solo dell'inizio. Scegli il pulsante Valori restituiti 2 alcune altre volte. Nel codice gestito, il metodo Add genera eccezioni di chiave duplicata, anche se è stato eseguito il cast dei valori ai tipi corretti. Al contrario, il metodo Insert aggiorna il valore associato a una chiave esistente e restituisce un valore Boolean che indica se è stata aggiunta una nuova chiave al dizionario. Per questo motivo il valore associato alla chiave 7 continua a cambiare.

Un altro comportamento imprevisto: se passi una variabile JavaScript non assegnata come argomento stringa, ottieni la stringa "undefined". In breve, presta attenzione quando passi i tipi di raccolte di .NET Framework al codice JavaScript.

Nota

Se hai grandi quantità di testo da concatenare, puoi eseguire questa operazione in modo più efficiente spostando il codice in un metodo .NET Framework e utilizzando la classe StringBuilder, come illustrato nella funzione showMap.

Sebbene tu non possa esporre tipi generici personalizzati da un componente Windows Runtime, puoi restituire le raccolte generiche di .NET Framework per le classi Windows Runtime utilizzando codice simile al seguente:

        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> implementa IList<T>, che risulta come tipo IVector<T> di Windows Runtime in JavaScript.

Dichiarazione di eventi

Puoi dichiarare gli eventi utilizzando lo schema di eventi .NET Framework standard o altri schemi utilizzati da Windows Runtime. .NET Framework supporta l'equivalenza tra il delegato System.EventHandler<TEventArgs> e il delegato EventHandler<T> Windows Runtime, pertanto l'utilizzo di EventHandler<TEventArgs> è un ottimo modo per implementare lo schema .NET Framework standard. Per verificarne il funzionamento, aggiungi la seguente coppia di classi al progetto 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

Quando esponi un evento in Windows Runtime, la classe di argomenti dell'evento eredita da System.Object. Non eredita da System.EventArgs, come in .NET Framework, poiché EventArgs non è un tipo di Windows Runtime.

Nota

Se dichiari funzioni di accesso a eventi personalizzati per l'evento (parola chiave Custom in Visual Basic), devi utilizzare lo schema di eventi di Windows Runtime. Vedi Eventi personalizzati e funzioni di accesso agli eventi nei componenti Windows Runtime.

Per gestire l'evento Test, aggiungi la funzione events1 in default.js. La funzione events1 crea una funzione del gestore eventi per l'evento Test e richiama immediatamente il metodo OnTest per generare l'evento. Se inserisci un punto di interruzione nel corpo del gestore eventi, puoi osservare che l'oggetto passato al singolo parametro include l'oggetto di origine ed entrambi i membri di 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);
}

Esposizione di operazioni asincrone

.NET Framework offre un ampio set di strumenti per l'elaborazione asincrona e parallela, in base a Task e alle classi Task<TResult> generiche. Per esporre l'elaborazione asincrona basata su attività in un componente Windows Runtime, utilizza le interfacce Windows Runtime IAsyncAction, IAsyncActionWithProgress<TProgress>, IAsyncOperation<TResult> e IAsyncOperationWithProgress<TResult, TProgress>. In Windows Runtime le operazioni restituiscono risultati, al contrario delle azioni.

In questa sezione viene illustrata un'operazione asincrona annullabile che segnala lo stato di avanzamento e restituisce risultati. Il metodo GetPrimesInRangeAsync utilizza la classe AsyncInfo per generare un'attività e connetterne le funzionalità di segnalazione dello stato di avanzamento e di annullamento a un oggetto WinJS.Promise. Inizia ad aggiungere le seguenti istruzioni using (Imports in Visual Basic) alla classe Example:

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

Aggiungi ora il metodo GetPrimesInRangeAsync alla 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

GetPrimesInRangeAsync è un metodo Finder molto semplice per i numeri primi, definito in fase di progettazione. Poiché lo scopo è quello di implementare un'operazione asincrona, la semplicità è importante e un'implementazione lenta costituisce un vantaggio per la dimostrazione dell'annullamento. GetPrimesInRangeAsync trova i numeri primi tramite forza bruta: divide un candidato per tutti gli interi minori o uguali alla relativa radice quadrata, anziché utilizzare solo i numeri primi. Avanzamento tramite codice:

  • Prima di iniziare un'operazione asincrona, esegui le attività di gestione, ad esempio la convalida di parametri e la generazione di eccezioni per l'input non valido.

  • La chiave per questa implementazione è il metodo AsyncInfo.Run<TResult, TProgress>(Func<CancellationToken, IProgress<TProgress>, Task<TResult>>) e il delegato che è l'unico parametro del metodo. Il delegato deve accettare un token di annullamento e un'interfaccia per la segnalazione dello stato di avanzamento e deve restituire un'attività avviata che utilizza tali parametri. Quando JavaScript chiama il metodo GetPrimesInRangeAsync, si verificano i passaggi seguenti (non necessariamente nell'ordine indicato qui):

    • L'oggetto WinJS.Promise fornisce le funzioni per elaborare i risultati restituiti, rispondere all'annullamento e gestire i report sullo stato di avanzamento.

    • Il metodo AsyncInfo.Run crea un'origine di annullamento e un oggetto che implementa l'interfaccia IProgress<T>. Al delegato passa sia un token CancellationToken dall'origine di annullamento sia l'interfaccia IProgress<T>.

      Nota

      Se l'oggetto Promise non fornisce una funzione per rispondere all'annullamento, AsyncInfo.Run passa comunque un token annullabile e l'annullamento può ancora verificarsi. Se l'oggetto Promise non fornisce una funzione per gestire gli aggiornamenti dello stato di avanzamento, AsyncInfo.Run fornisce comunque un oggetto che implementa IProgress<T>, ma i relativi report vengono ignorati.

    • Il delegato utilizza il metodo Task.Run<TResult>(Func<TResult>, CancellationToken) per creare un'attività avviata che utilizza il token e l'interfaccia dello stato di avanzamento. Il delegato per l'attività avviata viene fornito da una funzione lambda che calcola il risultato desiderato. Più avanti verranno fornite altre informazioni in proposito.

    • Il metodo AsyncInfo.Run crea un oggetto che implementa l'interfaccia IAsyncOperationWithProgress<TResult, TProgress>, connette il meccanismo di annullamento di Windows Runtime all'origine del token e connette la funzione di segnalazione dello stato di avanzamento dell'oggetto Promise all'interfaccia IProgress<T>.

    • L'interfaccia IAsyncOperationWithProgress<TResult, TProgress> viene restituita a JavaScript.

  • La funzione lambda rappresentata dall'attività avviata non accetta alcun argomento. Poiché è una funzione lambda, dispone di accesso al token e all'interfaccia IProgress. A ogni valutazione di un numero candidato, la funzione lambda:

    • Verifica se è stato raggiunto il punto di percentuale successivo dello stato di avanzamento. In caso positivo, la funzione lambda chiama il metodo IProgress<T>.Report e la percentuale viene passata alla funzione specificata dall'oggetto Promise per la segnalazione dello stato di avanzamento.

    • Utilizza il token di annullamento per generare un'eccezione se l'operazione è stata annullata. Se è stato chiamato il metodo IAsyncInfo.Cancel (ereditato dall'interfaccia IAsyncOperationWithProgress<TResult, TProgress>), la connessione impostata dal metodo AsyncInfo.Run garantisce la notifica del token di annullamento.

  • Quando la funzione lambda restituisce l'elenco dei numeri primi, tale elenco viene passato alla funzione specificata dall'oggetto WinJS.Promise per l'elaborazione dei risultati.

Per creare un suggerimento JavaScript e impostare il meccanismo di annullamento, aggiungi le funzioni asyncCancel e asyncRun in 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();
}

Chiamando il metodo asincrono GetPrimesInRangeAsync, la funzione asyncRun crea un oggetto WinJS.Promise. Il metodo then dell'oggetto accetta tre funzioni che elaborano i risultati restituiti, rispondono agli errori (incluso l'annullamento) e gestiscono i report sullo stato di avanzamento. In questo esempio, i risultati restituiti vengono stampati nell'area di output. Con l'annullamento o il completamento vengono ripristinati i pulsanti che avviano e annullano l'operazione. La segnalazione dello stato di avanzamento aggiorna il controllo dello stato.

La funzione asyncCancel chiama il metodo cancel dell'oggetto WinJS.Promise.

Per eseguire l'app, premi F5. Per avviare l'operazione asincrona, scegli il pulsante Asincrona. Ciò che si verifica in seguito dipende dalla velocità del computer in uso. Se l'indicatore di stato segnala immediatamente il completamento, aumenta il numero iniziale passato a GetPrimesInRangeAsync di uno o più fattori di dieci. Puoi ottimizzare la durata dell'operazione aumentando o diminuendo il conteggio dei numeri da testare, ma l'aggiunta di zeri al centro del numero iniziale avrà un impatto maggiore. Per annullare l'operazione, scegli il pulsante Annulla operazione asincrona.

Vedere anche

Concetti

Panoramica di .NET per le applicazioni Windows Store

.NET per le applicazioni Windows Store: API supportate

Creazione di componenti Windows Runtime in C# e Visual Basic

Programmazione asincrona con Async e Await (C# e Visual Basic)

Creazione di componenti Windows Runtime

Eventi personalizzati e funzioni di accesso agli eventi nei componenti Windows Runtime

Altre risorse

Supporto .NET Framework per applicazioni Windows Store e Windows Runtime