次の方法で共有


チュートリアル : C# または Visual Basic での単純なコンポーネントの作成および JavaScript による呼び出し

このチュートリアルでは、Visual Basic または C# と共に .NET Framework を使用して独自の Windows ランタイム 型を作成し、Windows ランタイム コンポーネント内にパッケージ化する方法について説明します。また、JavaScript を使用して Windows 用にビルドされた Windows ストア アプリケーションから、コンポーネントを呼び出す方法についても説明します。

Visual Studio により、C# または Visual Basic で記述された Windows ランタイム コンポーネントをアプリケーションに追加し、JavaScript から呼び出すことのできる Windows ランタイム 型を作成しやすくなります。 内部では、Windows ランタイム型は Windows ストア アプリで許可されている .NET Framework の機能をすべて使用できます。 (詳細については、「C++ および Visual Basic での Windows ランタイム コンポーネントの作成」および「Windows ストア アプリ用 .NET の概要」を参照してください。)外部的には、型のメンバーはパラメーターと戻り値の Windows ランタイム 型のみを公開できます。 ソリューションをビルドすると、Visual Studio は .NET Framework の Windows ランタイム コンポーネント プロジェクトをビルドし、Windows メタデータ (.winmd) ファイルを作成するビルド ステップを実行します。 これは Windows ランタイム コンポーネントであり、Visual Studio がアプリケーションに含まれています。

注意

.NET Framework により、一般的に使用される .NET Framework の型が Windows ランタイム の同等に自動的にマップされます。たとえば、プリミティブ データ型とコレクション型などがあります。これらの .NET Framework の型は Windows ランタイム コンポーネントのパブリック インターフェイスで使用できます。また、対応する Windows ランタイム 型としてコンポーネントのユーザーに表示されます。「C++ および Visual Basic での Windows ランタイム コンポーネントの作成」を参照してください。

このチュートリアルでは、次のタスクについて説明します。 JavaScript で Windows ストア アプリケーションを設定する最初のセクションを完了すると、任意の順序で残りのセクションに進むことができます。

  • シンプルな Windows ランタイム クラスの作成

  • JavaScript とマネージ コードから Windows ランタイムを使用する

  • コンポーネントからマネージ型を返す

  • イベントの宣言

  • 非同期操作の公開

必要条件:

このチュートリアルを完了するための要件は次のとおりです。

  • Windows 8 (またはそれ以降)

  • Microsoft Visual Studio 2012 または Microsoft Visual Studio Express 2012 for Windows 8 (またはそれ以降)

シンプルな Windows ランタイム クラスの作成

このセクションでは、JavaScript を使用して Windows 用にビルドされた Windows ストア アプリケーションを作成し、Visual Basic または C# Windows ランタイム コンポーネント プロジェクトを追加します。 マネージ Windows ランタイム 型を定義し、JavaScript から型のインスタンスを作成する方法を説明します。また、静的メンバーとインスタンス メンバーを呼び出す方法についても説明します。 例のアプリケーションのビジュアル表示は意図的に曇ったような効果になっており、コンポーネントにフォーカスを保持します。 見た目をよりよくすることをお勧めします。

  1. Visual Studio で新しい JavaScript プロジェクトを作成する: メニュー バーで、[ファイル][新規作成][プロジェクト] を順にクリックします (Visual Studio Express では、[ファイル][新しいプロジェクト] を順にクリックします)。 [新しいプロジェクト] ダイアログ ボックスの [インストールされたテンプレート] セクションで、[JavaScript] を選択し、[Windows ストア] を選択します ([Windows ストア] が使用できない場合は、Windows 8 以降を使用していることを確認してください)。[新しいアプリケーション] テンプレートを選択し、プロジェクト名の「SampleApp」を入力します。

  2. コンポーネント プロジェクトを作成する: ソリューション エクスプローラーで、SampleApp のソリューションのショートカット メニューを開き、[追加][新しいプロジェクト] の順にクリックして、ソリューションに新しい C# または Visual Basic プロジェクトを追加します。 [新しいプロジェクトの追加] ダイアログ ボックスの [インストールされたテンプレート] セクションで、[Visual Basic] または [Visual C#] を選択し、[Windows ストア] を選択します。 [Windows ランタイム コンポーネント] テンプレートを選択し、プロジェクト名の「SampleComponent」を入力します。

  3. クラス名を「Example」に変更します。 既定では、クラスが public sealed に設定されていることに注意してください (Visual Basic では Public NotInheritable)。 コンポーネントから公開するすべての Windows ランタイム クラスをシールする必要があります。

  4. 2 つの単純なメンバーをクラスに追加します。static メソッド (Visual Basic では Shared メソッド) およびインスタンス プロパティです。

    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 プロジェクトのチェック ボックスをオンにし、[OK] をクリックして参照を追加します。

Hh779077.collapse_all(ja-jp,VS.120).gifJavaScript からコンポーネントを呼び出します。

JavaScript から Windows ランタイム 型を使用するには、default.js ファイル (プロジェクトの js フォルダーにある) の末尾で、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;
}

各メンバー名の最初の文字が大文字でも小文字に変更されることに注意してください。 この変換は、Windows ランタイム の自然な使用を有効にするために JavaScript が提供するサポートの一部です。 名前空間とクラス名は Pascal 形式です。 メンバー名は、すべて小文字のイベント名を除いて Camel 形式です。 「JavaScript での Windows ランタイムの使用」を参照してください。 Camel 形式の規則は複雑になる場合があります。 一連の先頭の大文字は通常小文字として表示されますが、3 桁の英大文字の後に英小文字が続く場合、最初の 2 桁の文字が小文字で表示されます。たとえば、IDStringKind という名前のメンバーは idStringKind として表示されます。 Visual Studio では、Windows ランタイム コンポーネント プロジェクトをビルドし、JavaScript プロジェクトで IntelliSense を使用して正しい大文字小文字の区別を参照できます。

同様に、.NET Framework はマネージ コードでの Windows ランタイム の自然な使用を有効にするサポートを提供します。 これについては、この技術情報の後続のセクションと技術情報「C++ および Visual Basic での Windows ランタイム コンポーネントの作成」および「.NET Framework Support for Windows Store Apps and Windows Runtime」で説明します。

Hh779077.collapse_all(ja-jp,VS.120).gifシンプルなユーザー インターフェイスを作成する

JavaScript のプロジェクトで、default.html ファイルを開き、次のコードに示すとおり本体を更新します。 このコードには例のアプリケーションのコントロールの完全なセットが含まれており、クリック イベント用に関数名を指定します。

注意

アプリを初めて実行する場合、Basics1Basics2 ボタンだけがサポートされます。

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

css フォルダーの JavaScript のプロジェクトで default.css を開きます。 body セクションを示すとおりに変更し、スタイルを追加してボタンのレイアウトと出力テキストの配置を制御します。

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(ja-jp,VS.120).gifアプリケーションをビルドし、実行します。

ソリューションをビルドして実行するには、F5 キーを押します。 (SampleComponent が未定義であることを指摘する実行時エラー メッセージを取得する場合は、クラス ライブラリ プロジェクトへの参照が欠落しています。)

Visual Studio では、まずクラス ライブラリをコンパイルし、Winmdexp.exe (Windows ランタイム メタデータのエクスポート ツール) を実行する MSBuild タスクを実行して、Windows ランタイム コンポーネントを作成します。 コンポーネントは、マネージ コードとコードを説明する Windows メタデータの両方を含む .winmd ファイルに含まれています。 Windows ランタイム コンポーネント内の無効なコードを記述すると、WinMdExp.exe がビルド エラー メッセージを生成します。そして、そのエラー メッセージが Visual Studio IDE に表示されます。 Visual Studio は Windows ストア アプリケーションのアプリケーション パッケージ (.appx ファイル) にコンポーネントを追加し、適切なマニフェストを生成します。

[Basics 1] (基本 1) ボタンをクリックして GetAnswer の静的メソッドから出力領域に戻り値を割り当て、Example クラスのインスタンスを作成します。そして、出力領域の SampleProperty プロパティの値を表示します。 次の出力が表示されます。

"The answer is 42."
0

[Basics 2] (基本 2) ボタンをクリックして SampleProperty プロパティの値をインクリメントし、出力領域の新しい値を表示します。 文字列と数などの基本型は、パラメーターの型と戻り値の型として使用され、マネージ コードと JavaScript の間で渡されます。 JavaScript の数が倍精度浮動小数点形式で保存されるため、.NET Framework の数値型に変換されます。

注意

既定では、JavaScript コードでのみブレークポイントを設定できます。Visual Basic または C# コードをデバッグするには、「C++ および Visual Basic での Windows ランタイム コンポーネントの作成」を参照してください。

デバッグを終了してアプリケーションを閉じるには、アプリケーションから Visual Studio に切り替えて、Shift + F5 を押します。

JavaScript とマネージ コードから Windows ランタイム を使用する

Windows ランタイム は、JavaScript またはマネージ コードから呼び出すことができます。 Windows ランタイム オブジェクトは 2 つの間で双方向に渡すことができ、イベントはどちらの側からでも処理できます。 ただし、JavaScript と .NET Framework は異なる方法で Windows ランタイム をサポートしているので、2 つの環境で Windows ランタイム 型を使用する方法はいくつかの詳細な点で異なります。 次の例では、Windows.Foundation.Collections.PropertySet クラスを使用してこれらの相違点を示します。 この例では、マネージ コードで PropertySet コレクションのインスタンスを作成し、イベント ハンドラーを登録してコレクションの変更を追跡します。 次に、コレクションを取得し、独自のイベント ハンドラーを登録して、コレクションを使用する JavaScript コードを追加します。 最後に、マネージ コードからコレクションを変更し、マネージ例外を処理する JavaScript を示すメソッドを追加します。

重要

この例では、UI スレッドでイベントを発生させます。たとえば非同期呼び出しのようなバックグラウンド スレッドでイベントを発生させる場合、JavaScript がそのイベントを処理できるように追加の作業をいくつか実行する必要があります。詳細については、「Windows ランタイム コンポーネントでのイベントの発生」を参照してください。

SampleComponent のプロジェクトでは、PropertySetStats という名前の新しい public sealed クラス (Visual Basic では Public NotInheritable クラス) を追加します。 クラスでは、PropertySet コレクションをラップし、MapChanged イベントを処理します。 イベント ハンドラーは発生する各種の変更の数を追跡し、DisplayStats のメソッドは HTML 形式のレポートを生成します。 追加の using ステートメント (Visual Basic では Imports ステートメント) に注意してください。上書きするのではなく、既存の 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 オブジェクト) が、Windows ランタイム インターフェイスの IObservableMap<K, V> をインスタンス化したものである IObservableMap<string, object> インターフェイス (Visual Basic の場合は IObservableMap(Of String, Object)) にキャストされる点で異なります。 (必要に応じて、送信元を型にキャストできます。)また、イベント引数はオブジェクトとしてではなくインターフェイスとして表示されます。

default.js ファイルで Runtime1 関数を示すとおりに追加します。 このコードは PropertySetStats オブジェクトを作成し、PropertySet コレクションを取得します。また、独自のイベント ハンドラーの onMapChanged 関数を追加し、MapChanged イベントを処理します。 コレクションを変更した後に、runtime1 は 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;
    }

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

JavaScript で Windows ランタイム イベントを処理する方法は、.NET Framework コードでそれらを処理する方法とは大きく異なります。 JavaScript イベント ハンドラーは 1 つの引数しか受け取りません。 Visual Studio デバッガーでこのオブジェクトを表示する場合、最初のプロパティが送信元です。 イベント引数インターフェイスのメンバーは、このオブジェクトにも直接表示されます。

アプリケーションを実行するには、F5 キーを押します。 クラスがシールされていない場合、次のようなエラー メッセージを取得します。「シールされていない型 "SampleComponent.Example" をエクスポートすることは現在サポートされていません。 シール済みとして指定してください。」

[[Runtime 1]] (ランタイム 1) ボタンをクリックします。 イベント ハンドラーは要素が追加または変更されるたびに変更を表示し、末尾に DisplayStats メソッドが呼び出され、数の概要を生成します。 デバッグを終了してアプリケーションを閉じるには、Visual Studio に戻り、Shift + F5 を押します。

マネージ コードから PropertySet コレクションに 2 つの項目をさらに追加するには、次のコードを 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

このコードは、2 つの環境で Windows ランタイム 型を使用する方法の別の相違点を強調表示します。 このコードを独自に入力しても、IntelliSense は JavaScript コードで使用した insert メソッドを表示しません。 代わりに、.NET Framework のコレクションで一般的に表示される Add メソッドを表示します。 これは、一般的に使用されるコレクション インターフェイスが異なる名前であるのに、Windows ランタイム と .NET Framework で同様の機能を持つためです。 マネージ コードでこれらのインターフェイスを使用すると、.NET Framework の同等として表示されます。 これについては、「C++ および Visual Basic での Windows ランタイム コンポーネントの作成」で説明します。 JavaScript で同じインターフェイスを使用した場合、Windows ランタイム からの変更点は、メンバー名の先頭の大文字が小文字になることだけです。

最後に、例外処理で AddMore メソッドを呼び出すには、runtime2 関数を default.js に追加します。

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

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

アプリケーションを実行するには、F5 キーを押します。 [Runtime 1] (ランタイム 1) と [Runtime 2] (ランタイム 2) をクリックします。 JavaScript イベント ハンドラーはコレクションに最初の変更を報告します。 ただし、2 番目の変更に重複するキーがあります。 .NET Framework のディクショナリのユーザーは Add メソッドを想定して例外をスローしますが、それが実際に発生します。 JavaScript で .NET Framework の例外を処理します。

注意

JavaScript コードから例外のメッセージを表示することはできません。メッセージ テキストは、スタック トレースで置き換えられます。詳細については、「C++ および Visual Basic での Windows ランタイム コンポーネントの作成」の「例外のスロー」を参照してください。

一方、JavaScript が重複キーで insert メソッドを呼び出した際に、項目の値が変更されました。 動作の違いは、C++ および Visual Basic での Windows ランタイム コンポーネントの作成 で説明したように、JavaScript と .NET Framework で Windows ランタイム をサポートする方法が異なる点にあります。

コンポーネントからマネージ型を返す

既に説明したとおり、JavaScript コードと C# コード、または Visual Basic コードの間で Windows ランタイム のネイティブ型を自由に前後へ渡すことができます。 ほとんどの場合、型名およびメンバー名はどちらの場合も同じになります (JavaScript ではメンバー名が英小文字から開始する点を除きます)。 ただし、前のセクションで、PropertySet クラスは、マネージ コードとは異なるメンバーを持っているように見えます。 (たとえば、JavaScript では insert メソッドを呼び出し、.NET Framework コードでは Add メソッドを呼び出しました。)このセクションでは、それらの相違点が JavaScript に渡される .NET Framework の型に影響を与える方法を検討します。

コンポーネントで作成、または JavaScript からコンポーネントに渡された Windows ランタイム 型を返すことに加えて、マネージ コード内に作成されたマネージ型を対応する Windows ランタイム 型のように JavaScript に返すことができます。 ランタイム クラスの最初の簡単な例においても、メンバーのパラメーターと戻り値の型は .NET Framework の型である Visual Basic または C# の基本型でした。 コレクション用にこれを実行するには、次のコードを 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

ディクショナリは Dictionary<TKey, TValue> によって実装され、Windows ランタイム インターフェイスにマップされるインターフェイスとして返される必要があることに注意してください。 この場合、インターフェイスは IDictionary<int, string> (Visual Basic の IDictionary(Of Integer, String)) です。 Windows ランタイム 型の IMap<int, string> がマネージ コードに渡されると、IDictionary<int, string> として表示されます。また、マネージ型が JavaScript に渡されるとこの逆になります。

重要

マネージ型が複数のインターフェイスを実装すると、JavaScript はリストの最上位に表示されるインターフェイスを使用します。たとえば、JavaScript コードに Dictionary<int, string> を返した場合、戻り値の型としてどのインターフェイスを指定しても IDictionary<int, string> として表示されます。これは、後のインターフェイスに表示されるメンバーが最初のインターフェイスに含まれていない場合、そのメンバーは JavaScript に参照されないことを意味します。

新しいメソッドをテストしてディクショナリを使用するには、returns1 および returns2 関数を 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>";
}

この JavaScript コードについて確認するいくつかの重要な点があります。 まずは、showMap 関数が含まれており、HTML のディクショナリのコンテンツを表示できるという点です。 showMap のコードでは、イテレーション パターンに注意してください。 .NET Framework では、ジェネリック IDictionary インターフェイスに First メソッドがありません。また、サイズは Size メソッドではなく Count プロパティによって返されます。 JavaScript に対して、IDictionary<int, string> は Windows ランタイム 型の IMap<int, string> として表示されます。 (IMap<K,V> インターフェイスを参照してください。)

前の例のように、returns2 関数では、JavaScript は Insert メソッド (JavaScript では insert) を呼び出して、項目をディクショナリに追加します。

アプリケーションを実行するには、F5 キーを押します。 ディクショナリの初期コンテンツを作成して表示するには、[Returns 1] (戻り 1) ボタンをクリックします。 ディクショナリにさらに 2 つのエントリを追加するには、[Returns 2] (戻り 2) ボタンをクリックします。 Dictionary<TKey, TValue> から想定されるように、エントリが挿入の順序で表示されることに注意してください。 並べ替える場合は、GetMapOfNames から SortedDictionary<int, string> を返すことができます。 (前の例で使用されている PropertySet クラスには、Dictionary<TKey, TValue> とは異なる内部構成があります。)

もちろん、JavaScript は厳密に型指定された言語ではないため、厳密に型指定されたジェネリック コレクションを使用することにより意外な結果が生じることがあります。 [戻り 2] ボタンをもう一度クリックします。 JavaScript は "7" を数値 7 に、および ct に格納されている数値 7 を文字列に変換します。 そして、文字列 "forty" をゼロに強制的に変換します。 ただし、それは先頭部分でのみ実行されます。 [戻り 2] ボタンをさらに数回クリックします。 マネージ コードでは、値が正しい型にキャストされていたとしても Add メソッドが重複キーの例外を生成します。 一方、Insert メソッドは既存のキーに関連付けられた値を更新し、新しいキーがディクショナリに追加されたかどうかを示す Boolean 値を返します。 このため、7 キーに関連付けられた値は変更を保持します。

別の予期しない動作: 文字列引数として未割り当ての JavaScript 変数を渡す場合、文字列 "undefined" を取得します。 つまり、.NET Framework のコレクション型を JavaScript コードに渡す際には注意が必要です。

注意

連結する多くのテキストがある場合、showMap 関数に示すように、コードを .NET Framework メソッドに移動し、StringBuilder クラスを使用することによってより効率的に実行することができます。

Windows ランタイム コンポーネントから独自のジェネリック型を公開することはできませんが、以下のようなコードを使用して Windows ランタイム クラスの .NET Framework ジェネリック コレクションを返すことができます。

        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> を実装します。これは JavaScript で、Windows ランタイム 型の IVector<T> として表示されます。

イベントの宣言

標準 .NET Framework のイベント パターン、または Windows ランタイム で使用される他のパターンを使用してイベントを宣言できます。 .NET Framework は、System.EventHandler<TEventArgs> デリゲートと Windows ランタイム EventHandler<T> デリゲートの間で等価性をサポートするため、標準的な .NET Framework パターンを実装する目的で EventHandler<TEventArgs> を使用する方法が適しています。 この処理を確認するために、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 から継承します。 .NET Framework の場合と同様に、EventArgs は Windows ランタイム 型ではないため、System.EventArgs からは継承されません。

注意

イベント向けにカスタム イベント アクセサー (Visual Basic では Custom キーワード) を宣言する場合、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);
}

非同期操作の公開

Task<TResult>、およびジェネリック Task クラスに基づいて、.NET Framework には非同期処理と並列処理向けの充実したツールのセットがあります。 Windows ランタイム コンポーネントの中でタスク ベースの非同期処理を公開するには、Windows ランタイム のインターフェイスである IAsyncActionIAsyncActionWithProgress<TProgress>IAsyncOperation<TResult>IAsyncOperationWithProgress<TResult, TProgress> を使用します。 (Windows ランタイム では、操作は結果を返しますが、アクションは結果を返しません。)

このセクションでは、進行状況を報告して結果を返す取り消し可能な非同期操作について説明します。 GetPrimesInRangeAsync メソッドは AsyncInfo クラスを使用してタスクを生成し、自らのキャンセル機能と進行状況レポート機能を WinJS.Promise オブジェクトに関連付けます。 まず、次の using ステートメント (Visual Basic では Imports) を Example クラスに追加します。

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

次に、GetPrimesInRangeAsync メソッドを 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 は単純な素数のファインダーです (設計仕様)。 ここでの焦点は非同期操作の実行にあるため、簡潔さが重要であり、キャンセルを表示している場合は低速の実装が利点となります。 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 メソッドは、IAsyncOperationWithProgress<TResult, TProgress> インターフェイスを実装するオブジェクトを作成し、Windows ランタイム のキャンセル メカニズムをトークン ソースに関連付け、Promise オブジェクトの進行状況レポート関数を IProgress<T> インターフェイスに関連付けます。

    • IAsyncOperationWithProgress<TResult, TProgress> インターフェイスは、JavaScript に制御を返します。

  • 開始したタスクによって表されるラムダ関数は引数を受け取りません。 それはラムダ関数であるため、トークンと IProgress インターフェイスにアクセスできます。 候補数が評価されるたびに、ラムダ関数:

    • 進行状況の次の割合のポイントに到達したかどうかを確認します。 到達している場合、ラムダ関数は IProgress<T>.Report メソッドを呼び出し、割合は Promise オブジェクトが進行状況をレポートするために指定した関数に渡されます。

    • 操作が取り消された場合、キャンセル トークンを使用して例外をスローします。 IAsyncInfo.Cancel メソッド (IAsyncOperationWithProgress<TResult, TProgress> インターフェイスが継承するメソッド) を呼び出した場合、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();
}

非同期の GetPrimesInRangeAsync メソッドを呼び出すことにより、asyncRun 関数は WinJS.Promise オブジェクトを作成します。 オブジェクトの then メソッドは、返される結果を処理し、エラーに対応し (キャンセルを含む)、進行状況レポートを処理する 3 つの関数を受け取ります。 この例では、返される結果は出力領域に表示されます。 キャンセルまたは完了により、操作を起動して取り消すボタンをリセットします。 進行状況レポートはプログレス コントロールを更新します。

asyncCancel 関数は、WinJS.Promise オブジェクトの cancel メソッドを呼び出すだけです。

アプリケーションを実行するには、F5 キーを押します。 非同期操作を開始するには、[非同期] ボタンをクリックします。 次に行われる処理は、ご使用のコンピューターの速度によって異なります。 点滅時間が生じる前に進行状況バーが完了に進む場合は、10 の要素の 1 つ以上によって GetPrimesInRangeAsync に渡される開始番号のサイズを大きくします。 テストする数値のカウントの増加または減少により操作の時間を調整できます。ただし、開始番号の途中でゼロを追加すると、より大きな影響を及ぼします。 操作を取り消すには、[非同期のキャンセル] ボタンをクリックします。

参照

概念

Windows ストア アプリ用 .NET の概要

Windows ストア アプリ用 .NET の API

C++ および Visual Basic での Windows ランタイム コンポーネントの作成

Async および Await を使用した非同期プログラミング (C# および Visual Basic)

Windows ランタイム コンポーネントの作成

Windows ランタイム コンポーネントのカスタム イベントおよびイベント アクセサー

その他の技術情報

.NET Framework Support for Windows Store Apps and Windows Runtime