Instruções passo a passo: componente simples em C# ou Visual Basic e chamando-o em JavaScript
Esta explicação passo a passo mostra como usar o .NET Framework com o Visual Basic ou C# para criar seus próprios tipos do Tempo de Execução do Windows empacotados 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 de aplicativos .NET da 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.
Como criar uma classe simples do Tempo de Execução do Windows
Como usar o Tempo de Execução do Windows do código gerenciado e JavaScript
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 (ou versões posteriores)
Microsoft Visual Studio 2012 ou Microsoft Visual Studio Express 2012 for Windows 8 (ou versões posteriores)
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.
No Visual Studio, crie um novo projeto JavaScript: na barra de menus, escolha Arquivo, Novo, Projeto (no Visual Studio Express, 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 a Windows Store não estiver disponível, verifique se você está usando o Windows 8 ou posterior.) Escolha o modelo Aplicativo em Branco e insira SampleApp para o nome do projeto.
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.
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.
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
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.
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.
Chame o componente de JavaScript
Para usar o tipo Tempo de Execução do Windows do JavaScript, adicione o seguinte código após a função anônima ao final do arquivo default.js (na pasta js do projeto), 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. Veja Como usar 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 .NET Framework Support for Windows Store Apps and Windows Runtime.
Criar 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.
Aviso
Quando você executa o aplicativo pela primeira vez, apenas os botões Basics1 e Basics2 têm suporte.
<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;
}
Criar 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. A saída é mostrada aqui:
"The answer is 42."
0
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 de código gerenciado ou JavaScript. Objetos Tempo de Execução do Windows podem ser passados para trás e para a frente entre os dois, e os eventos podem ser tratados dos dois 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 que segue demonstra essas diferenças, utilizando 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.
Importante
Nesse exemplo, o evento está sendo acionado no thread de interface do usuário.Se você acionar o evento de um thread em segundo plano, por exemplo, em um chamada assíncrona, deverá realizar algum trabalho adicional para que o JavaScript manipule o evento.Para obter mais informações, consulte Acionando eventos em componentes do Tempo de Execução do Windows.
No projeto SampleComponent, adicione uma nova classe public sealed (classe Public NotInheritable no Visual Basic) denominada PropertySetStats. A classe encapsula uma coleção PropertySet e identifica seu 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 identificador de eventos segue o padrão familiar de eventos do .NET Framework, exceto pelo fato de que o emissor do evento (neste caso, o objeto PropertySet) é convertido para a interface IObservableMap<string, object> (IObservableMap(Of String, Object) no Visual Basic), que é uma instanciação da interface Tempo de Execução do Windows IObservableMap<K, V>. (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 diferentes membros no 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>. (Veja 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 Tempo de Execução do Windows IVector<T> no 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 suporta equivalência entre o System.EventHandler<TEventArgs> delegado e o Tempo de Execução do Windows EventHandler<T> delegado, portanto, usar o EventHandler<TEventArgs> é uma boa maneira de implementar o padrão. 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 o processamento assíncrono baseado em tarefas de um componente do Tempo de Execução do Windows, use as interfaces 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 utiliza a classe AsyncInfo para gerar uma tarefa e conectar seus recursos de relatórios de progresso e cancelamento para 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 é a implementação de uma operação assíncrona, por isso a simplicidade é importante, e uma implementação lenta é uma vantagem quando demonstramos o cancelamento. GetPrimesInRangeAsync encontra números primos por força bruta: Divide um candidato por todos os números inteiros menores ou iguais à sua raiz quadrada, em vez de utilizar apenas 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 lidar com relatórios de progresso.
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 Tempo de Execução do Windows com a origem do token e conecta a função de relatórios de progresso do objeto do Promise com a interface IProgress<T>.
A interface IAsyncOperationWithProgress<TResult, TProgress> é retornada ao 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 (do qual a interface herda IAsyncOperationWithProgress<TResult, TProgress>) foi chamado, a conexão que o método AsyncInfo.Run define garante que o token de cancelamento seja notificado.
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();
}
Por chamar o método assíncrono GetPrimesInRangeAsync, a função asyncRun cria um objeto um WinJS.Promise. O método do objeto then usa três funções que processam os resultados retornados, reagem a erros (inclusive ao cancelamento) e identificam os relatórios de progresso. 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 cancel 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 de aplicativos .NET da Windows Store
APIs para aplicativos .NET para Windows Store
Criando componentes do Tempo de Execução do Windows em C# e Visual Basic
Programação assíncrona com Async e Await (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
.NET Framework Support for Windows Store Apps and Windows Runtime