Passo a passo: Criando um componente simples no C# ou Visual Basic e chamá-lo a partir do JavaScript

Esta explicação passo a passo mostra como usar .NET Framework 4.5 com o Visual Basic ou C# para criar seus próprios tipos do Tempo de Execução do Windows, compactados em um componente do Tempo de Execução do Windows, e como chamar o componente do seu aplicativo Windows Store compilado para o Windows usando JavaScript.

O Visual Studio facilita a inclusão de um componente do Tempo de Execução do Windows gravado com C# ou Visual Basic no seu aplicativo, bem como a criação de tipos do Tempo de Execução do Windows que você pode chamar a partir de JavaScript. Internamente, os tipos do Tempo de Execução do Windows podem usar qualquer funcionalidade do .NET Framework que seja permitida em um aplicativo Windows Store. (Para obter mais informações, consulte Criando componentes do Tempo de Execução do Windows em C# e Visual Basic e Visão geral dos aplicativos .NET para Windows Store.) Externamente, os membros do seu tipo podem expor somente tipos do Tempo de Execução do Windows para seus parâmetros e valores de retorno. Quando você cria sua solução, o Visual Studio compila seu projeto de Componente do Tempo de Execução do Windows .NET Framework e executa uma etapa de compilação que cria um arquivo de metadados do Windows (.winmd). Este é o componente do Tempo de Execução do Windows, que inclui o Visual Studio em seu aplicativo.

Dica

O .NET Framework automaticamente mapeia alguns tipos de uso geral do .NET Framework, como tipos de dados primitivos e tipos de coleção, para seus equivalentes do Tempo de Execução do Windows. Esses tipos do .NET Framework podem ser usados na interface pública de um componente do Tempo de Execução do Windows, e aparecerão para os usuários do componente como os tipos do Tempo de Execução do Windows correspondentes. Consulte Criando componentes do Tempo de Execução do Windows em C# e Visual Basic.

Esta explicação passo a passo mostra as seguintes tarefas. Depois de concluir a primeira seção, que configura o aplicativo Windows Store com JavaScript, você pode concluir as seções restantes em qualquer ordem.

  • Criando uma classe simples do Tempo de Execução do Windows

  • Usando o Tempo de Execução do Windows de JavaScript e de código gerenciado

  • Retornando tipos gerenciados do seu componente

  • Declarando eventos

  • Expondo operações assíncronas

Pré-requisitos:

Você precisa dos seguintes itens para concluir esta explicação:

  • Windows 8

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

Criando uma classe simples do Tempo de Execução do Windows

Esta seção cria um aplicativo Windows Store compilado para Windows usando JavaScript e adiciona um projeto de Componente do Tempo de Execução do Windows no Visual Basic ou C#. Ela mostra como definir um tipo do Tempo de Execução do Windows gerenciado, criar uma instância do tipo de JavaScript e chamar membros estáticos e de instância. A exibição visual do aplicativo de exemplo é deliberadamente simplória a fim de manter o foco no componente. Fique à vontade para torná-la mais atraente.

  1. No Visual Studio, crie um novo projeto JavaScript: na barra de menus, escolha Arquivo, Novo, Projeto (em Visual Studio Express 2012 for Windows 8, escolha Arquivo, Novo Projeto). Na seção Modelos Instalados da caixa de diálogo Novo Projeto, escolha JavaScript e, em seguida, escolha Windows Store. (Se Windows Store não estiver disponível, verifique se está usando Windows 8.) Escolha o modelo Aplicativo em Branco e insira SampleApp para o nome do projeto.

  2. Crie o projeto de componente: no Gerenciador de Soluções, abra o menu de atalhos para a solução SampleApp e Adicionar e Novo Projeto para adicionar um novo projeto C# ou Visual Basic à solução. Na seção Modelos Instalados da caixa de diálogo Adicionar Novo Projeto, escolha Visual Basic ou Visual C# e, em seguida, escolha Windows Store. Escolha o modelo Componente do Tempo de Execução do Windows e insira SampleComponent para o nome do projeto.

  3. Altere o nome da classe para Exemplo. Observe que, por padrão, a classe é marcada com public sealed (Public NotInheritable no Visual Basic). Todas as classes do Tempo de Execução do Windows que você expuser a partir do seu componente devem ser fechadas.

  4. Adicione dois membros simples à classe, um método static (método Shared no Visual Basic) e uma propriedade da instância:

    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. Opcional: para ativar o IntelliSense para os membros recém-adicionados, no Gerenciador de Soluções, abra o menu de atalhos do projeto SampleComponent e clique em Compilar.

  6. No Gerenciador de Soluções, no projeto JavaScript, abra o menu de atalhos para Referências e escolha Adicionar Referência para abrir o Gerenciador de Referências. Escolha Solução e, em seguida, Projetos. Marque a caixa de seleção para o projeto SampleComponent e escolha OK para adicionar uma referência.

Hh779077.collapse_all(pt-br,VS.110).gifChame o componente de JavaScript

Para usar o tipo do Tempo de Execução do Windows de JavaScript, adicione o seguinte código ao final do arquivo default.js (na pasta js do projeto), após a função existente fornecida pelo modelo do 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;
}

Observe que a primeira letra de cada nome do membro é alterada de maiúscula para minúscula. Essa transformação faz parte do suporte que o JavaScript fornece para permitir o uso natural do Tempo de Execução do Windows. Namespaces e nomes de classes são escritos na formatação Pascal de maiúsculas e minúsculas. Os nomes de membros são escritos em caixa mista (alta e baixa), exceto para nomes de eventos, cujas letras são todas minúsculas. Consulte Usando o Tempo de Execução do Windows em JavaScript. As regras de caixa mista podem ser confusas. Uma série de letras maiúsculas iniciais normalmente aparece como minúsculas, mas se três letras maiúsculas são seguida por uma letra minúscula, somente as duas primeiras letras aparecerão em minúsculas: por exemplo, um membro chamado IDStringKind aparece como idStringKind. No Visual Studio, você pode compilar o projeto de componente do Tempo de Execução do Windows e usar o IntelliSense em seu projeto JavaScript para ver a caixa correta.

De forma similar, o .NET Framework fornece suporte para permitir o uso natural do Tempo de Execução do Windows em código gerenciado. Isso é discutido em seções subsequentes deste artigo e nos artigos Criando componentes do Tempo de Execução do Windows em C# e Visual Basic e Suporte do .NET Framework para aplicativos da Windows Store e Tempo de Execução do Windows.

Hh779077.collapse_all(pt-br,VS.110).gifCriar uma interface do usuário simples

No seu projeto JavaScript, abra o arquivo default.html e atualize o corpo conforme mostrado no código a seguir. Esse código inclui o conjunto completo de controles para o exemplo de aplicativo e especifica os nomes de função para os eventos de clique.

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

No seu projeto JavaScript, na pasta css, abra default.css. Modifique a seção body como mostrado e adicione estilos para controlar o layout dos botões e o posicionamento do texto de saída.

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(pt-br,VS.110).gifCriar e executar o aplicativo

Para compilar e executar a solução, escolha a chave F5. (Se você receber uma mensagem de erro em tempo de execução que indica que SampleComponent está indefinido, a referência ao projeto de biblioteca de classe estará faltando.)

O Visual Studio compila primeiro a biblioteca de classes e, em seguida, executa uma tarefa MSBuild que executa Winmdexp.exe (Ferramenta de Exportação de Metadados do Tempo de Execução do Windows) para criar o componente do Tempo de Execução do Windows. O componente é incluído em um arquivo .winmd que contém o código gerenciado e os metadados do Windows que descrevem o código. O WinMdExp.exe gera mensagens de erro de compilação quando você escreve código inválido em um componente do Tempo de Execução do Windows, e mensagens de erro são exibidas no Visual Studio IDE. O Visual Studio adiciona o seu componente ao pacote de aplicativos (arquivo .appx) para o seu aplicativo Windows Store e gera o manifesto apropriado.

Escolha o botão Noções básicas 1 para atribuir o valor de retorno do método estático GetAnswer à área de saída, para criar uma instância da classe Example e exibir o valor da propriedade SampleProperty na área de saída.

Escolha o botão Noções básicas 2 para incrementar o valor da propriedade SampleProperty e exibir o novo valor na área de saída. Os tipos primitivos como cadeias de caracteres e números podem ser usados como tipos de parâmetro e tipos de retorno, e podem ser transmitidos entre o código gerenciado e JavaScript. Como os números em JavaScript são armazenados no formato de ponto flutuante de precisão dupla, são convertidos em tipos numéricos do .NET Framework.

Dica

Por padrão, você pode definir pontos de interrupção somente no seu código JavaScript. Para depurar seu código do Visual Basic ou C#, consulte Criando componentes do Tempo de Execução do Windows em C# e Visual Basic.

Para parar a depuração e fechar o aplicativo, passe do aplicativo para o Visual Studio e escolha Shift+F5.

Usando o Tempo de Execução do Windows de JavaScript e de código gerenciado

O Tempo de Execução do Windows pode ser chamado do JavaScript ou do código gerenciado. Os objetos do Tempo de Execução do Windows podem ser passados bidirecionalmente entre os dois e os eventos podem ser tratados por um dos lados. No entanto, as formas como você usa os tipos do Tempo de Execução do Windows nos dois ambientes diferem em alguns detalhes, porque o JavaScript e o .NET Framework oferecem suporte ao Tempo de Execução do Windows de formas diferentes. O exemplo a seguir demonstra essas diferenças, usando a classe Windows.Foundation.Collections.PropertySet. Nesse exemplo, você cria uma instância de coleção PropertySet em código gerenciado e registra um manipulador de eventos para controlar as alterações na coleção. Em seguida, você adiciona o código JavaScript que obtém a coleção, registra seu próprio manipulador de eventos e usa a coleção. Finalmente, adicione um método que faça alterações à coleção de código gerenciado e mostre o JavaScript tratando uma exceção gerenciada.

No projeto SampleComponent, adicione uma nova classe public sealed (classe Public NotInheritable no Visual Basic) denominada PropertySetStats. A classe envolve uma coleção PropertySet e trata o evento MapChanged. O manipulador de eventos controla o número de alterações de cada tipo que ocorre, e o método DisplayStats gera um relatório que é formatado em HTML. Observe a declaração adicional using (declaraçãoImports no Visual Basic); adicione-a às instruções using existentes em vez de substituí-las.

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

O manipulador de eventos segue o padrão de evento familiar do .NET Framework, exceto que o remetente do evento (nesse caso, o objeto PropertySet) é convertido para a interface IObservableMap<string, object> (IObservableMap(Of String, Object) no Visual Basic), que é uma instanciação da interface IObservableMap<K, V>Tempo de Execução do Windows. (Você pode converter o remetente no seu tipo, se necessário.) Além disso, os argumentos de evento são apresentados como uma interface em vez de como um objeto.

No arquivo default.js, adicione a função Runtime1 como mostrado. Esse código cria um objeto PropertySetStats, obtém a coleção PropertySet e adiciona seu próprio manipulador de eventos, a função onMapChanged, para manipular o evento MapChanged. Depois de fazer alterações à coleção, runtime1 chama o método DisplayStats para mostrar um resumo dos tipos de alteração.

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

A maneira como você manipula eventos do Tempo de Execução do Windows em JavaScript é muito diferente da maneira como você os manipula em código do .NET Framework. O manipulador de eventos JavaScript usa apenas um argumento. Quando você exibe este objeto no depurador do Visual Studio, a primeira propriedade é o remetente. Os membros da interface de argumentos de eventos também aparecem diretamente nesse objeto.

Para executar o aplicativo, escolha a chave F5. Se a classe não for fechada, você receberá a mensagem de erro “A exportação do tipo aberto “SampleComponent.Example” não é suportada atualmente. Marque-a como fechado.”

Escolha o botão Tempo de Execução 1. O manipulador de eventos exibe alterações à medida que elementos são adicionados ou alterados, e no final, o método DisplayStats é chamado para gerar um resumo de contagens. Para parar a depuração e fechar o aplicativo, volte para o Visual Studio e escolha Shift+F5.

Para adicionar mais dois itens à coleção PropertySet de código gerenciado, adicione o seguinte código à 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

Esse código realça outra diferença na forma como você usa os tipos do Tempo de Execução do Windows nos dois ambientes. Se você mesmo digitar esse código, observará que o IntelliSense não mostra o método insert usado no código JavaScript. Em vez de isso, ele mostra o método Add geralmente visto em coleções do .NET Framework. Isso ocorre porque algumas interfaces de coleção usadas com frequência têm nomes diferentes mas funcionalidade semelhante no Tempo de Execução do Windows e no .NET Framework. Quando você usa essas interfaces em código gerenciado, aparecem como seus equivalentes do .NET Framework. Isso é discutido em Criando componentes do Tempo de Execução do Windows em C# e Visual Basic. Quando você usa as mesmas interfaces em JavaScript, a única alteração do Tempo de Execução do Windows é que as letras maiúsculas no começo de nomes de membros se tornam minúsculas.

Finalmente, para chamar o método AddMore com manipulação de exceção, adicione a função runtime2 a default.js.

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

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

Para executar o aplicativo, escolha a chave F5. Escolha Tempo de Execução 1 e, em seguida, Tempo de Execução 2. O manipulador de eventos JavaScript relata a primeira alteração na coleção. A segunda alteração, no entanto, tem uma chave duplicada. Os usuários de dicionários do .NET Framework esperam o método Add para lançar uma exceção, e é o que acontece. O JavaScript lida com a exceção do .NET Framework.

Dica

Não é possível exibir a mensagem da exceção do código JavaScript. Este texto da mensagem é substituído por um rastreamento de pilha. Para obter mais informações, consulte "Lançando exceções" em Criando componentes do Tempo de Execução do Windows em C# e Visual Basic.

Por outro lado, quando o JavaScript chama o método insert com uma chave duplicada, o valor do item é alterado. Essa diferença no comportamento se deve às diferentes maneiras como o JavaScript e o .NET Framework oferecem suporte ao Tempo de Execução do Windows, conforme explicado em Criando componentes do Tempo de Execução do Windows em C# e Visual Basic.

Retornando tipos gerenciados do seu componente

Como discutido anteriormente, você pode transmitir tipos do Tempo de Execução do Windows nativo livremente entre seu código JavaScript e seu código C# ou do Visual Basic. Na maioria das vezes, os nomes de tipos e os nomes de membros serão os mesmos em ambos os casos (exceto que o início dos nomes de membros são grafados com letras minúsculas em JavaScript). No entanto, na seção anterior, a classe PropertySet pareceu ter membros diferentes em código gerenciado. (Por exemplo, em JavaScript, você chamou o método insert, e no código do .NET Framework você chamou o método Add.) Esta seção explora a maneira como essas diferenças afetam os tipos do .NET Framework transmitidos para JavaScript.

Além de retornar os tipos do Tempo de Execução do Windows que você criou no seu componente ou transmitiu para o seu componente de JavaScript, você pode retornar um tipo gerenciado, criado em código gerenciado, para JavaScript como se fosse o tipo correspondente do Tempo de Execução do Windows. Mesmo no primeiro, o exemplo simples de uma classe de tempo de execução, os parâmetros e tipos de retorno dos membros foram os tipos primitivos do Visual Basic ou C#, que são tipos do .NET Framework. Para demonstrar isso para coleções, adicione o seguinte código à classe Example, para criar um método que retorna um dicionário genérico de cadeias de caracteres, indexadas por números inteiros:

        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

Observe que o dicionário deve ser retornado como uma interface implementada por Dictionary<TKey, TValue>, e que ela mapeia para uma interface do Tempo de Execução do Windows. Nesse caso, a interface é IDictionary<int, string> (IDictionary(Of Integer, String) no Visual Basic). Quando o tipo IMap<int, string> do Tempo de Execução do Windows é passado para código gerenciado, ele aparece como IDictionary<int, string>, e o inverso é verdadeiro quando o tipo gerenciado é passado para JavaScript.

Importante

Quando um tipo gerenciado implementa várias interfaces, o JavaScript usa a interface que aparece primeiro na lista. Por exemplo, se você retornarDictionary<int, string> para o código JavaScript, ele aparecerá como IDictionary<int, string>, não importando qual interface você especificar como o tipo de retorno. Isso significa que se a primeira interface não incluir um membro que apareça nas interfaces posteriores, esse membro não será visível para JavaScript.

Para testar o novo método e usar o dicionário, adicione as funções returns1 e returns2 a 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>";
}

Existem alguns fatos interessantes a observar sobre esse código JavaScript. Antes de mais nada, ele inclui uma função showMap para exibir o conteúdo do dicionário em HTML. No código para showMap, observe o padrão de iteração. No .NET Framework, não há nenhum método First na interface genérica IDictionary, e o tamanho é retornado por uma propriedade Count em vez de um método Size. Para JavaScript, IDictionary<int, string> parecer ser o tipo do Tempo de Execução do Windows IMap<int, string>. (Consulte a interface IMap<K,V>.)

Na função returns2, como nos exemplos anteriores, o JavaScript chama o método Insert (insert em JavaScript) para adicionar itens ao dicionário.

Para executar o aplicativo, escolha a chave F5. Para criar e exibir o conteúdo inicial do dicionário, escolha o botão Retorna 1. Para adicionar duas mais entradas ao dicionário, escolha o botão Retorna 2. Observe que as entradas serão exibidas por ordem de inserção, como se esperaria de Dictionary<TKey, TValue>. Se você desejar classificá-las, poderá retornar SortedDictionary<int, string> de GetMapOfNames. (A classe PropertySet usada nos exemplos anteriores tem uma organização interna diferente de Dictionary<TKey, TValue>.)

Naturalmente, o JavaScript não é uma linguagem fortemente tipada, portanto, o uso de coleções genéricas fortemente tipadas pode resultar em alguns resultados surpreendentes. Escolha o botão Retorna 2 novamente. O JavaScript força o “7 " a um 7 numérico, e o 7 numérico armazenado em ct a uma cadeia de caracteres. E força a cadeia de caracteres “quarenta” a zero. Mas isso é somente o início. Escolha o botão Retorna 2 mais algumas vezes. No código gerenciado, o método Add produziria exceções de chave duplicada, mesmo se os valores fossem convertidos em tipos corretos. Por outro lado, o método Insert atualiza o valor associado com uma chave existente e retorna um valor Boolean que indica se uma nova chave foi adicionada ao dicionário. É por isso que o valor associado com a chave 7 é alterado continuamente.

Outro comportamento inesperado: se você passar uma variável JavaScript não atribuída como um argumento de cadeia de caracteres, o que você obterá é a cadeia de caracteres “indefinida”. Resumindo, seja cuidadoso quando passar tipos de coleção do .NET Framework ao seu código JavaScript.

Dica

Se você tiver grandes quantidades de texto para concatenar, poderá fazê-lo com mais eficiência movendo o código em um método do .NET Framework e usando a classe StringBuilder, conforme mostrado na função showMap.

Embora você não pode expor seus próprios tipos genéricos de um componente do Tempo de Execução do Windows, pode retornar coleções genéricas do .NET Framework para classes Tempo de Execução do Windows usando código como o seguinte:

        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>, que aparece como o tipo do Tempo de Execução do Windows IVector<T> em JavaScript.

Declarando eventos

Você pode declarar eventos usando o padrão de evento padrão do .NET Framework ou outros padrões usados pelo Tempo de Execução do Windows. O .NET Framework oferece suporte à equivalência entre o representante System.EventHandler<TEventArgs> e o representante EventHandler<T> do Tempo de Execução do Windows, portanto o uso de EventHandler<TEventArgs> é uma boa maneira de implementar o padrão do .NET Framework. Para ver como isso funciona, adicione o seguinte par de classes ao projeto 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 você expõe um evento no Tempo de Execução do Windows, a classe de argumento de evento herda de System.Object. Ela não herda de System.EventArgs, como no .NET Framework, porque EventArgs não é um tipo do Tempo de Execução do Windows.

Dica

Se você declarar acessadores de evento personalizados para o evento (palavra-chave Custom no Visual Basic), você deve usar o padrão de evento do Tempo de Execução do Windows. Consulte Eventos personalizados e acessadores de evento nos componentes do Tempo de Execução do Windows.

Para manipular o evento Test, adicione a função events1 a default.js. A função events1 cria uma função do manipulador de eventos para o evento Test e imediatamente chama o método OnTest para gerar o evento. Se você colocar um ponto de interrupção no corpo do manipulador de eventos, poderá ver que o objeto passado para o único parâmetro inclui o objeto de origem e os dois membros 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);
}

Expondo operações assíncronas

O .NET Framework tem um rico conjunto de ferramentas para processamento assíncrono e processamento paralelo, com base na classe Task e nas classes genéricas Task<TResult>. Para expor um processamento assíncrono baseado em tarefa em um componente do Tempo de Execução do Windows, use as interfaces do Tempo de Execução do Windows IAsyncAction, IAsyncActionWithProgress<TProgress>, IAsyncOperation<TResult> e IAsyncOperationWithProgress<TResult, TProgress>. (No Tempo de Execução do Windows, operações retornam resultados, mas as ações não.)

Esta seção demonstra uma operação assíncrona anulável que relata o andamento e retorna resultados. O método GetPrimesInRangeAsync usa a classe AsyncInfo para gerar uma tarefa e conectar seus recursos de relatórios de cancelamento e de andamento a um objeto WinJS.Promise. Inicie adicionando as seguintes declarações using (Imports no Visual Basic) à classe Example:

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

Agora, adicione o método GetPrimesInRangeAsync à 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 é um localizador muito simples de número primo e está presente por design. O foco aqui é na implementação de uma operação assíncrona, de modo que a simplicidade é importante e uma implementação lenta é uma vantagem quando estamos demonstrando o cancelamento. O GetPrimesInRangeAsync encontra números primos por força bruta: ele divide um candidato por todos os inteiros que forem menores ou iguais à sua raiz quadrada, em vez de usar somente os números primos. Percorrendo este código:

  • Antes de iniciar uma operação assíncrona, execute atividades de tarefas de organização como validar parâmetros e lançar exceções para a entrada inválida.

  • A chave para essa implementação é o método AsyncInfo.Run<TResult, TProgress>(Func<CancellationToken, IProgress<TProgress>, Task<TResult>>), e o representante que é o único parâmetro do método. O representante deve aceitar um token de cancelamento e uma interface para relatar o andamento, e deve retornar uma tarefa iniciada que use esses parâmetros. Quando o JavaScript chama o método GetPrimesInRangeAsync, as seguintes etapas ocorrem (não necessariamente nesta ordem):

    • O objeto WinJS.Promise fornece funções para processar os resultados retornados, reagir ao cancelamento e tratar relatórios de andamento.

    • O método AsyncInfo.Run cria uma fonte de cancelamento e um objeto que implementa a interface IProgress<T>. Para o representante, passa um token CancellationToken da origem de cancelamento e a interface IProgress<T>.

      Dica

      Se o objeto Promise não fornecer uma função para reagir ao cancelamento, AsyncInfo.Run ainda passará um token anulável, e o cancelamento ainda poderá ocorrer. Se o objeto Promise não fornecer uma função para manipular atualizações de andamento, AsyncInfo.Run ainda fornecerá um objeto que implementa IProgress<T>, mas seus relatórios serão ignorados.

    • O representante usa o método Task.Run<TResult>(Func<TResult>, CancellationToken) para criar uma tarefa iniciada que use o token e a interface de andamento. O representante para a tarefa iniciado é fornecido por uma função lambda que calcula o resultado desejado. Forneceremos em breve mais informações sobre isso.

    • O método AsyncInfo.Run cria um objeto que implementa a interface IAsyncOperationWithProgress<TResult, TProgress>, conecta o mecanismo de cancelamento do objeto do Tempo de Execução do Windows com a fonte do token e conecta a função de relatório de andamento do objeto Promise com a interface IProgress<T>.

    • A interface IAsyncOperationWithProgress<TResult, TProgress> é retornada para JavaScript.

  • A função lambda que é representada pela tarefa iniciada não tem nenhum argumento. Como é uma função lambda, ela tem acesso ao token e à interface IProgress. Cada vez que um número de candidato é avaliado, a função lambda:

    • Verifica se o próximo ponto de porcentagem de andamento foi alcançado. Em caso positivo, a função lambda chama o método IProgress<T>.Report, e a porcentagem é passada para a função que o objeto Promise especificou para relatar o andamento.

    • Usa o token de cancelamento para lançar uma exceção se a operação foi cancelada. Se o método IAsyncInfo.Cancel (que a interface IAsyncOperationWithProgress<TResult, TProgress> herda) for chamado, a conexão que o método AsyncInfo.Run configura assegura a notificação do token de cancelamento.

  • Quando a função lambda retorna a lista de números primos, a lista é passada para a função que o objeto WinJS.Promise especificou para processar os resultados.

Para criar a promessa JavaScript e configurar o mecanismo de cancelamento, adicione as funções asyncRun e asyncCancel a 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();
}

Ao chamar o método assíncrono GetPrimesInRangeAsync, a função asyncRun cria um objeto WinJS.Promise. O método e, em seguida, do objeto usa três funções que processam os resultados de retorno, reagem a erros (inclusive o cancelamento) e tratam os relatórios de andamento. Nesse exemplo, os resultados retornados são impressos na área de saída. O cancelamento ou a conclusão redefine os botões que iniciam e cancelam a operação. O relatório de andamento atualiza o controle de andamento.

A função asyncCancel apenas chama o método cancelar do objeto WinJS.Promise.

Para executar o aplicativo, escolha a chave F5. Para iniciar a operação assíncrona, escolha o botão Assíncrono. O que acontece em seguida depende da rapidez do seu computador. Se a barra de andamento fechar a conclusão antes que você tenha tempo de piscar, aumente o tamanho do número inicial que é transmitido para GetPrimesInRangeAsync em um ou vários fatores de dez. Você pode ajustar a duração da operação aumentando ou diminuindo a contagem de números para testar, mas adicionar zero ao meio do número inicial terá um maior impacto. Para cancelar a operação, escolha o botão Cancelar Assíncrono.

Consulte também

Conceitos

Visão geral dos aplicativos .NET para Windows Store

Aplicativos .NET para Windows Store - APIs com suporte

Criando componentes do Tempo de Execução do Windows em C# e Visual Basic

Programação com Async assíncrona e esperar (C# e Visual Basic)

Criando componentes do Tempo de Execução do Windows

Eventos personalizados e acessadores de evento nos componentes do Tempo de Execução do Windows

Outros recursos

Suporte do .NET Framework para aplicativos da Windows Store e Tempo de Execução do Windows