Поделиться через


Пошаговое руководство по созданию компонента среды выполнения Windows на C# или Visual Basic и его вызову с помощью JavaScript или C#

В этом пошаговом руководстве показано, как использовать .NET с Visual Basic или C# для создания собственных типов среда выполнения Windows, упакованных в компонент среда выполнения Windows, а также способ вызова этого компонента из приложения javaScript универсальная платформа Windows (UWP).

Visual Studio позволяет легко создавать и развертывать собственные пользовательские типы среда выполнения Windows в проекте компонента среда выполнения Windows (WRC), написанном с помощью C# или Visual Basic, а затем ссылаться на этот WRC из проекта приложения JavaScript и использовать эти пользовательские типы из этого приложения.

На внутреннем уровне типы среды выполнения Windows могут использовать любые функции .NET, разрешенные в приложении UWP.

Снаружи члены разрабатываемого типа могут предоставлять доступ только к параметрам и возвращаемым значениями типов среды выполнения Windows. При построении решения Visual Studio создает проект .NET WRC, а затем выполняет шаг построения, создающий файл метаданных Windows (WinMD). Это ваш среда выполнения Windows компонент, который Visual Studio включает в приложение.

Примечание.

Платформа .NET автоматически сопоставляет некоторые часто используемые типы .NET, такие как простые типы данных и коллекций, с их эквивалентами среды выполнения Windows. Эти типы .NET можно использовать в общедоступном интерфейсе компонента среды выполнения Windows, и они будут представляться пользователям компонента в виде соответствующих типов среды выполнения Windows. См. Создание компонентов среды выполнения Windows с помощью C# и Visual Basic.

Необходимые условия:

Примечание.

проекты универсальная платформа Windows (UWP) с помощью JavaScript не поддерживаются в Visual Studio 2019. См . javaScript и TypeScript в Visual Studio 2019. Для выполнения этой статьи рекомендуется использовать Visual Studio 2017. См . JavaScript в Visual Studio 2017.

Создание простого класса среда выполнения Windows

В этом разделе создается приложение UWP JavaScript и добавляется в решение проекта компонента Visual Basic или C# среда выполнения Windows. В нем показано, как определить тип среда выполнения Windows, создать экземпляр типа из JavaScript и вызвать статические и элементы экземпляра. Визуальное отображение примера приложения является намеренно низким ключом, чтобы сосредоточиться на компоненте.

  1. В Visual Studio создайте проект JavaScript: в строке меню выберите "Файл", "Создать", "Проект". В разделе "Установленные шаблоны" диалогового окна "Новый проект" выберите JavaScript, а затем выберите Windows, а затем универсальный. (Если Windows недоступна, убедитесь, что вы используете Windows 8 или более поздней версии.) Выберите шаблон пустого приложения и введите SampleApp для имени проекта.

  2. Создайте проект компонента: в Обозреватель решений откройте контекстное меню решения SampleApp и нажмите кнопку "Добавить", а затем выберите "Новый проект", чтобы добавить новый проект C# или Visual Basic в решение. В разделе "Установленные шаблоны" диалогового окна "Добавить новый проект" выберите Visual Basic или Visual C#, а затем выберите Windows, а затем универсальная. Выберите шаблон компонента среда выполнения Windows и введите SampleComponent для имени проекта.

  3. Измените имя класса на Example. Обратите внимание, что по умолчанию класс помечен как открытый запечатанный (public NotInheritable в Visual Basic). Все классы среды выполнения Windows, доступ к которым предоставляется из компонента, должны быть запечатанными.

  4. Добавьте в класс два простых члена, статический метод (общий метод в Visual Basic) и свойство экземпляра:

    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. Необязательно. Чтобы включить IntelliSense для новых добавленных участников, в Обозреватель решений откройте контекстное меню проекта SampleComponent и нажмите кнопку "Сборка".

  6. В Обозреватель решений в проекте JavaScript откройте контекстное меню для ссылок и нажмите кнопку "Добавить ссылку", чтобы открыть диспетчер ссылок. Выберите "Проекты" и выберите "Решение". Установите флажок для проекта SampleComponent и нажмите кнопку "ОК ", чтобы добавить ссылку.

Вызов компонента из JavaScript

Чтобы использовать тип среда выполнения Windows из JavaScript, добавьте следующий код в анонимную функцию в файл default.js (в папке js проекта), предоставленной шаблоном Visual Studio. Он должен перейти после обработчика событий app.oncheckpoint и перед вызовом app.start.

var ex;

function basics1() {
   document.getElementById('output').innerHTML =
        SampleComponent.Example.getAnswer();

    ex = new SampleComponent.Example();

   document.getElementById('output').innerHTML += "<br/>" +
       ex.sampleProperty;

}

function basics2() {
    ex.sampleProperty += 1;
    document.getElementById('output').innerHTML += "<br/>" +
        ex.sampleProperty;
}

Обратите внимание, что первая буква каждого имени элемента изменяется с верхнего регистра на нижний регистр. Это преобразование является частью поддержки, которую JavaScript предоставляет для обеспечения естественного использования среда выполнения Windows. Пространства имен и имена классов имеют регистр Pascal. Имена членов являются верблюжьими регистрами, за исключением имен событий, которые являются строчными. См. статью "Использование среда выполнения Windows в JavaScript". Правила для регистра верблюда могут быть запутанными. Ряд начальных прописных букв обычно отображается в нижнем регистре, но если за тремя прописными буквами следует строчная буква, то в нижнем регистре отображаются только первые две буквы: например, член с именем IDStringKind отображается как idStringKind. В Visual Studio можно создать проект компонента среда выполнения Windows, а затем использовать IntelliSense в проекте JavaScript для просмотра правильного регистра.

Аналогичным образом .NET обеспечивает поддержку естественного использования среда выполнения Windows в управляемом коде. Это рассматривается в последующих разделах этой статьи, а также в статьях среда выполнения Windows компонентов с поддержкой C# и Visual Basic и .NET для приложений UWP и среда выполнения Windows.

Создание простого пользовательского интерфейса

В проекте JavaScript откройте файл default.html и обновите текст, как показано в следующем коде. Этот код включает полный набор элементов управления для примера приложения и задает имена функций для событий щелчка.

Обратите внимание , что при первом запуске приложения поддерживаются только кнопки Basics1 и Basics2.

<body>
            <div id="buttons">
            <button id="button1" >Basics 1</button>
            <button id="button2" >Basics 2</button>

            <button id="runtimeButton1">Runtime 1</button>
            <button id="runtimeButton2">Runtime 2</button>

            <button id="returnsButton1">Returns 1</button>
            <button id="returnsButton2">Returns 2</button>

            <button id="events1Button">Events 1</button>

            <button id="btnAsync">Async</button>
            <button id="btnCancel" disabled="disabled">Cancel Async</button>
            <progress id="primeProg" value="25" max="100" style="color: yellow;"></progress>
        </div>
        <div id="output">
        </div>
</body>

В проекте JavaScript в папке css откройте default.css. Измените раздел текста, как показано ниже, и добавьте стили для управления макетом кнопок и размещением выходного текста.

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

Теперь добавьте код регистрации прослушивателя событий, добавив предложение в вызов processAll в app.onactivated в default.js. Замените существующую строку кода, которая вызывает setPromise и измените ее на следующий код:

args.setPromise(WinJS.UI.processAll().then(function () {
    var button1 = document.getElementById("button1");
    button1.addEventListener("click", basics1, false);
    var button2 = document.getElementById("button2");
    button2.addEventListener("click", basics2, false);
}));

Это лучший способ добавления событий в элементы управления HTML, чем добавление обработчика событий щелчка непосредственно в HTML. См. статью "Создание приложения Hello, World" (JS).

Сборка и запуск приложения

Перед сборкой измените целевую платформу для всех проектов на Arm, x64 или x86 в соответствии с вашим компьютером.

Чтобы создать и запустить решение, выберите клавишу F5. (Если вы получаете сообщение об ошибке во время выполнения, указывающее, что SampleComponent не определен, ссылка на проект библиотеки классов отсутствует.)

Visual Studio сначала компилирует библиотеку классов, а затем выполняет задачу MSBuild, которая запускает Winmdexp.exe (средство экспорта метаданных среда выполнения Windows) для создания компонента среда выполнения Windows. Компонент включен в winmd-файл, содержащий как управляемый код, так и метаданные Windows, описывающие код. WinMdExp.exe создает сообщения об ошибках сборки при написании кода, недопустимого в компоненте среда выполнения Windows, а сообщения об ошибках отображаются в интегрированной среде разработки Visual Studio. Visual Studio добавляет компонент в пакет приложения (.appx файл) для приложения UWP и создает соответствующий манифест.

Нажмите кнопку Basics 1, чтобы назначить возвращаемое значение из статического метода GetAnswer выходной области, создать экземпляр класса Example и отобразить значение свойства SampleProperty в области вывода. Выходные данные показаны здесь:

"The answer is 42."
0

Нажмите кнопку "Основы 2", чтобы увеличить значение свойства SampleProperty и отобразить новое значение в области вывода. Примитивные типы, такие как строки и числа, можно использовать в качестве типов параметров и возвращаемых типов, а также передаваться между управляемым кодом и JavaScript. Так как числа в JavaScript хранятся в формате с плавающей запятой двойной точности, они преобразуются в платформа .NET Framework числовые типы.

Примечание По умолчанию можно задать точки останова только в коде JavaScript. Сведения об отладке кода Visual Basic или C# см. в статье "Создание компонентов среда выполнения Windows в C# и Visual Basic".

Чтобы остановить отладку и закрыть приложение, перейдите из приложения в Visual Studio и выберите SHIFT+F5.

Использование среда выполнения Windows из JavaScript и управляемого кода

Среда выполнения Windows можно вызывать из JavaScript или управляемого кода. среда выполнения Windows объекты можно передавать между двумя и вперед, а события можно обрабатывать с обеих сторон. Однако способы использования среда выполнения Windows типов в двух средах отличаются в некоторых деталях, так как JavaScript и .NET поддерживают среда выполнения Windows по-разному. В следующем примере показаны эти различия с помощью класса Windows.Foundation.Collections.PropertySet . В этом примере создается экземпляр коллекции PropertySet в управляемом коде и регистрируется обработчик событий для отслеживания изменений в коллекции. Затем вы добавите код JavaScript, который получает коллекцию, регистрирует собственный обработчик событий и использует коллекцию. Наконец, вы добавите метод, который вносит изменения в коллекцию из управляемого кода и показывает, что JavaScript обрабатывает управляемое исключение.

Важно, что в этом примере событие запускается в потоке пользовательского интерфейса. При срабатывании события из фонового потока, например в асинхронном вызове, необходимо выполнить некоторую дополнительную работу, чтобы JavaScript обрабатывал событие. Дополнительные сведения см. в разделе "Повышение событий" в компонентах среда выполнения Windows.

В проекте SampleComponent добавьте новый открытый запечатанный класс (класс Public NotInheritable в Visual Basic) с именем PropertySetStats. Класс упаковывает коллекцию PropertySet и обрабатывает его событие MapChanged. Обработчик событий отслеживает количество изменений каждого типа, и метод DisplayStats создает отчет, форматированный в HTML. Обратите внимание на дополнительную инструкцию using (инструкцию Import в Visual Basic), чтобы добавить ее в существующие инструкции using , а не перезаписывать их.

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

Обработчик событий следует знакомому шаблону событий платформа .NET Framework, за исключением того, что отправитель события (в данном случае объект PropertySet) привязывается к строке IObservableMap, интерфейсу объекта> (IObservableMap(Of String, Object) в Visual Basic), который является среда выполнения Windows экземпляром интерфейса IObservableMap<<K, V>. (При необходимости можно привести отправителя к типу.) Кроме того, аргументы событий представлены как интерфейс, а не как объект.

В файле default.js добавьте функцию Runtime1, как показано ниже. Этот код создает объект PropertySetStats, получает коллекцию PropertySet и добавляет собственный обработчик событий , функцию onMapChanged для обработки события MapChanged. После внесения изменений в коллекцию среда выполнения1 вызывает метод DisplayStats, чтобы отобразить сводку типов изменений.

var propertysetstats;

function runtime1() {
    document.getElementById('output').innerHTML = "";

    propertysetstats = new SampleComponent.PropertySetStats();
    var propertyset = propertysetstats.propertySet;

    propertyset.addEventListener("mapchanged", onMapChanged);

    propertyset.insert("FirstProperty", "First property value");
    propertyset.insert("SuperfluousProperty", "Unnecessary property value");
    propertyset.insert("AnotherProperty", "A property value");

    propertyset.insert("SuperfluousProperty", "Altered property value")
    propertyset.remove("SuperfluousProperty");

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

function onMapChanged(change) {
    var result
    switch (change.collectionChange) {
        case Windows.Foundation.Collections.CollectionChange.reset:
            result = "All properties cleared";
            break;
        case Windows.Foundation.Collections.CollectionChange.itemInserted:
            result = "Inserted " + change.key + ": '" +
                change.target.lookup(change.key) + "'";
            break;
        case Windows.Foundation.Collections.CollectionChange.itemRemoved:
            result = "Removed " + change.key;
            break;
        case Windows.Foundation.Collections.CollectionChange.itemChanged:
            result = "Changed " + change.key + " to '" +
                change.target.lookup(change.key) + "'";
            break;
        default:
            break;
     }

     document.getElementById('output').innerHTML +=
         "<br/>" + result;
}

Способ обработки событий среда выполнения Windows в JavaScript отличается от способа их обработки в платформа .NET Framework коде. Обработчик событий JavaScript принимает только один аргумент. При просмотре этого объекта в отладчике Visual Studio первое свойство является отправителем. Элементы интерфейса аргумента события также отображаются непосредственно в этом объекте.

Чтобы запустить приложение, выберите клавишу F5. Если класс не запечатан, появится сообщение об ошибке " Экспорт неуправляемого типа SampleComponent.Example в настоящее время не поддерживается. Пожалуйста, пометьте его как запечатанный".

Нажмите кнопку "Среда выполнения 1 ". Обработчик событий отображает изменения при добавлении или изменении элементов, а в конце метода DisplayStats вызывается для создания сводки счетчиков. Чтобы остановить отладку и закрыть приложение, вернитесь в Visual Studio и выберите SHIFT+F5.

Чтобы добавить еще два элемента в коллекцию PropertySet из управляемого кода, добавьте следующий код в класс 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

Этот код выделяет еще одно различие в том, как вы используете среда выполнения Windows типы в двух средах. Если вы вводите этот код самостоятельно, вы заметите, что IntelliSense не отображает метод вставки, используемый в коде JavaScript. Вместо этого в нем показан метод Add, который часто отображается в коллекциях в .NET. Это связано с тем, что некоторые часто используемые интерфейсы коллекции имеют разные имена, но аналогичные функции в среда выполнения Windows и .NET. При использовании этих интерфейсов в управляемом коде они отображаются в качестве их платформа .NET Framework эквивалентов. Это рассматривается в среда выполнения Windows компонентах c# и Visual Basic. При использовании одинаковых интерфейсов в JavaScript единственное изменение от среда выполнения Windows заключается в том, что верхние буквы в начале имен элементов становятся строчными.

Наконец, чтобы вызвать метод AddMore с обработкой исключений, добавьте функцию runtime2 в default.js.

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

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

Добавьте код регистрации обработчика событий так же, как и ранее.

var runtimeButton1 = document.getElementById("runtimeButton1");
runtimeButton1.addEventListener("click", runtime1, false);
var runtimeButton2 = document.getElementById("runtimeButton2");
runtimeButton2.addEventListener("click", runtime2, false);

Чтобы запустить приложение, выберите клавишу F5. Выберите среду выполнения 1 , а затем среду выполнения 2. Обработчик событий JavaScript сообщает о первом изменении коллекции. Однако второе изменение имеет повторяющийся ключ. Пользователи словарей платформа .NET Framework ожидают, что метод Add создает исключение, и это то, что происходит. JavaScript обрабатывает исключение .NET.

Примечание. Невозможно отобразить сообщение исключения из кода JavaScript. Текст сообщения заменяется трассировкой стека. Дополнительные сведения см. в разделе "Создание исключений среда выполнения Windows" в C# и Visual Basic.

Напротив, при вызове JavaScript метода вставки с повторяющимся ключом значение элемента было изменено. Это различие в поведении обусловлено различными способами поддержки среда выполнения Windows JavaScript и .NET, как описано в среда выполнения Windows компонентах c# и Visual Basic.

Возврат управляемых типов из компонента

Как описано ранее, вы можете передавать собственные типы среда выполнения Windows между кодом JavaScript и кодом C# или Visual Basic. В большинстве случаев имена типов и имена членов будут одинаковыми в обоих случаях (за исключением того, что имена элементов начинаются с строчных букв в JavaScript). Однако в предыдущем разделе класс PropertySet, как представляется, имеет разные элементы в управляемом коде. (Например, в JavaScript вы назвали метод вставки и в коде .NET, который вы назвали методОм Add.) В этом разделе описывается, как эти различия влияют на платформа .NET Framework типы, передаваемые в JavaScript.

Помимо возврата среда выполнения Windows типов, созданных в компоненте или переданных компоненту из JavaScript, можно вернуть управляемый тип, созданный в управляемом коде, в JavaScript, как если бы он был соответствующим типом среда выполнения Windows. Даже в первом простом примере класса среды выполнения параметры и типы возвращаемых элементов были примитивными типами Visual Basic или C#, которые являются платформа .NET Framework типами. Чтобы продемонстрировать это для коллекций, добавьте следующий код в класс Example, чтобы создать метод, возвращающий универсальный словарь строк, индексированных целыми числами:

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

Обратите внимание, что словарь должен быть возвращен в виде интерфейса, реализуемого словарем<TKey, TValue> и сопоставленным с интерфейсом среда выполнения Windows. В этом случае интерфейс — IDictionary int, string> (IDictionary<(Of Integer, String) в Visual Basic. Когда среда выполнения Windows тип IMap<int, строка> передается в управляемый код, она отображается как IDictionary<int, string> и обратное значение true, если управляемый тип передается в JavaScript.

Важно, если управляемый тип реализует несколько интерфейсов, JavaScript использует интерфейс, который отображается сначала в списке. Например, если вы возвращаете int словаря<, строка> в код JavaScript отображается как IDictionary<int, строка> независимо от того, какой интерфейс указывается в качестве возвращаемого типа. Это означает, что если первый интерфейс не включает элемент, который отображается в последующих интерфейсах, этот элемент не отображается в JavaScript.

 

Чтобы протестировать новый метод и использовать словарь, добавьте возвращаемые 1 и возвращаемые2 функции в default.js:

var names;

function returns1() {
    names = SampleComponent.Example.getMapOfNames();
    document.getElementById('output').innerHTML = showMap(names);
}

var ct = 7;

function returns2() {
    if (!names.hasKey(17)) {
        names.insert(43, "forty-three");
        names.insert(17, "seventeen");
    }
    else {
        var err = names.insert("7", ct++);
        names.insert("forty", "forty");
    }
    document.getElementById('output').innerHTML = showMap(names);
}

function showMap(map) {
    var item = map.first();
    var retval = "<ul>";

    for (var i = 0, len = map.size; i < len; i++) {
        retval += "<li>" + item.current.key + ": " + item.current.value + "</li>";
        item.moveNext();
    }
    return retval + "</ul>";
}

Добавьте код регистрации событий в тот же блок, что и другой код регистрации событий:

var returnsButton1 = document.getElementById("returnsButton1");
returnsButton1.addEventListener("click", returns1, false);
var returnsButton2 = document.getElementById("returnsButton2");
returnsButton2.addEventListener("click", returns2, false);

Есть несколько интересных вещей, которые следует наблюдать за этим кодом JavaScript. Во-первых, она включает функцию showMap для отображения содержимого словаря в HTML. В коде showMap обратите внимание на шаблон итерации. В .NET нет метода First в универсальном интерфейсе IDictionary, а размер возвращается свойством Count, а не методом Size. Для JavaScript iDictionary<int строка, как представляется, является строкой> среда выполнения Windows типа IMap<int, string>. (См. раздел Интерфейс IMap<K, V> .)

В функции возвращает2, как и в предыдущих примерах, JavaScript вызывает метод Insert (вставка в JavaScript), чтобы добавить элементы в словарь.

Чтобы запустить приложение, выберите клавишу F5. Чтобы создать и отобразить начальное содержимое словаря, нажмите кнопку "Возвращает 1 ". Чтобы добавить в словарь еще две записи, нажмите кнопку "Возвращает 2 ". Обратите внимание, что записи отображаются в порядке вставки, так как вы ожидаете от словаря<TKey, TValue>. Если вы хотите отсортировать их, можно вернуть int SortedDictionary<, строку> из GetMapOfNames. (Класс PropertySet, используемый в предыдущих примерах, имеет другую внутреннюю организацию из словаря<TKey, TValue>.)

Конечно, JavaScript не является строго типизированным языком, поэтому использование строго типизированных универсальных коллекций может привести к некоторым удивительным результатам. Снова нажмите кнопку "Возврат 2 ". JavaScript принудительно принужает "7" к числовым 7 и числовым 7, хранящимся в ct строке. И он принуживает строку "сорок" к нулю. Но это только начало. Нажмите кнопку "Возврат 2 " несколько раз. В управляемом коде метод Add создаст повторяющиеся исключения ключей, даже если значения были приведены к правильным типам. Напротив, метод Insert обновляет значение, связанное с существующим ключом, и возвращает логическое значение, указывающее, был ли добавлен новый ключ в словарь. Именно поэтому значение, связанное с ключом 7, продолжает изменяться.

Другое непредвиденное поведение: если вы передаете переменную JavaScript без знака в виде строкового аргумента, то, что вы получаете, строка "не определена". Короче говоря, будьте осторожны при передаче типов коллекций платформа .NET Framework в код JavaScript.

Обратите внимание, что при наличии большого количества текста для объединения можно сделать это более эффективно, переместив код в метод платформа .NET Framework и используя класс StringBuilder, как показано в функции showMap.

Хотя вы не можете предоставлять собственные универсальные типы из компонента среда выполнения Windows, вы можете возвращать универсальные коллекции платформа .NET Framework для классов среда выполнения Windows с помощью кода, например следующего:

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 реализует IList<T>>, который отображается как тип IVector<T> среда выполнения Windows в JavaScript.

Объявление событий

События можно объявить с помощью стандартного шаблона событий платформа .NET Framework или других шаблонов, используемых среда выполнения Windows. Платформа .NET Framework поддерживает эквивалентность между делегатом System.EventHandler TEventArgs среда выполнения Windows<и делегатом EventHandler T>, поэтому использование EventHandler<<TEventArgs>> является хорошим способом реализации стандартного шаблона платформа .NET Framework. Чтобы узнать, как это работает, добавьте в проект 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

Когда вы предоставляете событие в среда выполнения Windows, класс аргументов события наследует от System.Object. Он не наследуется от System.EventArgs, так как он был бы в .NET, так как EventArgs не является типом среда выполнения Windows.

Если вы объявляете пользовательские методы доступа к событиям (пользовательское ключевое слово в Visual Basic), необходимо использовать шаблон событий среда выполнения Windows. Сведения о пользовательских событиях и методах доступа к событиям см. в среда выполнения Windows компонентах.

Чтобы обработать событие Test, добавьте функцию events1 в default.js. Функция events1 создает функцию обработчика событий для события Test и немедленно вызывает метод OnTest для вызова события. Если поместить точку останова в текст обработчика событий, можно увидеть, что объект, переданный одному параметру, включает исходный объект и оба члена 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);
}

Добавьте код регистрации событий в тот же блок, что и другой код регистрации событий:

var events1Button = document.getElementById("events1Button");
events1Button.addEventListener("click", events1, false);

Предоставление асинхронных операций

Платформа .NET Framework имеет широкий набор средств для асинхронной обработки и параллельной обработки на основе классов Task и generic Task<TResult>. Чтобы предоставить асинхронную обработку на основе задач в компоненте среда выполнения Windows, среда выполнения Windows используйте интерфейсы IAsyncAction, IAsyncActionWithProgress< TProgress>, IAsyncOperation TResult и IAsyncOperationWithProgress<TResult>, TProgress><. (В среда выполнения Windows операции возвращают результаты, но действия не выполняются.)

В этом разделе демонстрируется отменяемая асинхронная операция, которая сообщает о ходе выполнения и возвращает результаты. Метод GetPrimesInRangeAsync использует класс AsyncInfo для создания задачи и подключения его функций отмены и отчетов о ходе выполнения к объекту WinJS.Promise. Начните с добавления метода GetPrimesInRangeAsync в пример класса:

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

public static IAsyncOperationWithProgress<IList<long>, double>
GetPrimesInRangeAsync(long start, long count)
{
    if (start < 2 || count < 1) throw new ArgumentException();

    return AsyncInfo.Run<IList<long>, double>((token, progress) =>

        Task.Run<IList<long>>(() =>
        {
            List<long> primes = new List<long>();
            double onePercent = count / 100;
            long ctProgress = 0;
            double nextProgress = onePercent;

            for (long candidate = start; candidate < start + count; candidate++)
            {
                ctProgress += 1;
                if (ctProgress >= nextProgress)
                {
                    progress.Report(ctProgress / onePercent);
                    nextProgress += onePercent;
                }
                bool isPrime = true;
                for (long i = 2, limit = (long)Math.Sqrt(candidate); i <= limit; i++)
                {
                    if (candidate % i == 0)
                    {
                        isPrime = false;
                        break;
                    }
                }
                if (isPrime) primes.Add(candidate);

                token.ThrowIfCancellationRequested();
            }
            progress.Report(100.0);
            return primes;
        }, token)
    );
}
Imports System.Runtime.InteropServices.WindowsRuntime

Public Shared Function GetPrimesInRangeAsync(ByVal start As Long, ByVal count As Long)
As IAsyncOperationWithProgress(Of IList(Of Long), Double)

    If (start < 2 Or count < 1) Then Throw New ArgumentException()

    Return AsyncInfo.Run(Of IList(Of Long), Double)( _
        Function(token, prog)
            Return Task.Run(Of IList(Of Long))( _
                Function()
                    Dim primes As New List(Of Long)
                    Dim onePercent As Long = count / 100
                    Dim ctProgress As Long = 0
                    Dim nextProgress As Long = onePercent

                    For candidate As Long = start To start + count - 1
                        ctProgress += 1

                        If ctProgress >= nextProgress Then
                            prog.Report(ctProgress / onePercent)
                            nextProgress += onePercent
                        End If

                        Dim isPrime As Boolean = True
                        For i As Long = 2 To CLng(Math.Sqrt(candidate))
                            If (candidate Mod i) = 0 Then
                                isPrime = False
                                Exit For
                            End If
                        Next

                        If isPrime Then primes.Add(candidate)

                        token.ThrowIfCancellationRequested()
                    Next
                    prog.Report(100.0)
                    Return primes
                End Function, token)
        End Function)
End Function

GetPrimesInRangeAsync — это очень простой средство поиска простых чисел, и это по дизайну. Основное внимание уделяется реализации асинхронной операции, поэтому простота важна, и медленная реализация является преимуществом при демонстрации отмены. GetPrimesInRangeAsync находит праймы путем подбора силы: он делит кандидата на все целые числа, которые меньше или равно его квадратной корень, а не используют только простые числа. Пошаговое руководство по этому коду:

  • Прежде чем начать асинхронную операцию, выполните действия по обслуживанию дома, такие как проверка параметров и создание исключений для недопустимых входных данных.

  • Ключом этой реализации является метод AsyncInfo.Run TResult, TProgress>(Func<<CancellationToken, IProgress<TProgress>, Task<TResult>>) и делегат, который является единственным параметром метода. Делегат должен принять маркер отмены и интерфейс для выполнения отчетов и вернуть запущенную задачу, которая использует эти параметры. Когда JavaScript вызывает метод GetPrimesInRangeAsync, выполняются следующие действия (не обязательно в указанном ниже порядке):

    • Объект WinJS.Promise предоставляет функции для обработки возвращаемых результатов, реагирования на отмену и обработки отчетов о ходе выполнения.

    • Метод AsyncInfo.Run создает источник отмены и объект, реализующий интерфейс IProgress<T> . Делегату передается как маркер CancellationToken из источника отмены, так и интерфейс IProgress<T>.

      Обратите внимание , что если объект Promise не предоставляет функцию для реагирования на отмену, AsyncInfo.Run по-прежнему передает маркер отмены, и отмена по-прежнему может произойти. Если объект Promise не предоставляет функцию для обработки обновлений хода выполнения, AsyncInfo.Run по-прежнему предоставляет объект, реализующий IProgress<T>, но его отчеты игнорируются.

    • Делегат использует метод Task.Run<TResult(Func<TResult>>, CancellationToken) для создания запущенной задачи, которая использует маркер и интерфейс хода выполнения. Делегат для запущенной задачи предоставляется лямбда-функцией, которая вычисляет требуемый результат. Подробнее об этом мы расскажем немного позже.

    • Метод AsyncInfo.Run создает объект, реализующий интерфейс TResult IAsyncOperationWithProgress<, TProgress>, подключает механизм отмены среда выполнения Windows с источником маркера и подключает функцию отчетов о ходе выполнения объекта Promise с интерфейсом IProgress<T>.

    • Интерфейс IAsyncOperationWithProgress<TResult, TProgress> возвращается в JavaScript.

  • Лямбда-функция, представленная запущенной задачей, не принимает никаких аргументов. Так как это лямбда-функция, она имеет доступ к маркеру и интерфейсу IProgress. Каждый раз, когда вычисляется номер кандидата, лямбда-функция:

    • Проверяет, достигнута ли следующая процентная точка прогресса. Если она имеется, лямбда-функция вызывает IProgress<T>. Метод отчета и процент передается функции, указанной объектом Promise для выполнения отчетов.
    • Использует маркер отмены для создания исключения, если операция отменена. Если метод IAsyncInfo.Cancel (который вызывается интерфейсом IAsyncOperationWithProgress<TResult, TProgress> interface наследует), подключение, настроенное методом AsyncInfo.Run, гарантирует уведомление маркера отмены.
  • Когда лямбда-функция возвращает список простых чисел, список передается функции, указанной объектом WinJS.Promise для обработки результатов.

Чтобы создать обещание JavaScript и настроить механизм отмены, добавьте функции asyncRun и asyncCancel в default.js.

var resultAsync;
function asyncRun() {
    document.getElementById('output').innerHTML = "Retrieving prime numbers.";
    btnAsync.disabled = "disabled";
    btnCancel.disabled = "";

    resultAsync = SampleComponent.Example.getPrimesInRangeAsync(10000000000001, 2500).then(
        function (primes) {
            for (i = 0; i < primes.length; i++)
                document.getElementById('output').innerHTML += " " + primes[i];

            btnCancel.disabled = "disabled";
            btnAsync.disabled = "";
        },
        function () {
            document.getElementById('output').innerHTML += " -- getPrimesInRangeAsync was canceled. -- ";

            btnCancel.disabled = "disabled";
            btnAsync.disabled = "";
        },
        function (prog) {
            document.getElementById('primeProg').value = prog;
        }
    );
}

function asyncCancel() {    
    resultAsync.cancel();
}

Не забывайте код регистрации событий так же, как и ранее.

var btnAsync = document.getElementById("btnAsync");
btnAsync.addEventListener("click", asyncRun, false);
var btnCancel = document.getElementById("btnCancel");
btnCancel.addEventListener("click", asyncCancel, false);

Вызвав асинхронный метод GetPrimesInRangeAsync, функция asyncRun создает объект WinJS.Promise. Затем метод объекта принимает три функции, которые обрабатывают возвращаемые результаты, реагируют на ошибки (включая отмену) и обрабатывают отчеты о ходе выполнения. В этом примере возвращенные результаты печатаются в области выходных данных. Отмена или завершение сбрасывает кнопки, которые запускают и отменяют операцию. Отчеты о ходе выполнения обновляют элемент управления хода выполнения.

Функция asyncCancel просто вызывает метод отмены объекта WinJS.Promise.

Чтобы запустить приложение, выберите клавишу F5. Чтобы запустить асинхронную операцию, нажмите кнопку Async . Что происходит дальше, зависит от того, насколько быстро ваш компьютер. Если индикатор выполнения выполняется до завершения до истечения времени, увеличьте размер начального числа, передаваемого в GetPrimesInRangeAsync одним или несколькими факторами из десяти. Вы можете точно настроить длительность операции, увеличив или уменьшая количество чисел для тестирования, но добавление нуля в середине начального числа будет иметь большее влияние. Чтобы отменить операцию, нажмите кнопку "Отмена асинхронной синхронизации".