Panduan membuat komponen C# atau Visual Basic Windows Runtime, dan memanggilnya dari JavaScript

Panduan ini menunjukkan bagaimana Anda dapat menggunakan .NET dengan Visual Basic atau C# untuk membuat jenis Windows Runtime Anda sendiri, dipaketkan dalam komponen Windows Runtime, dan cara memanggil komponen tersebut dari aplikasi JavaScript Platform Windows Universal (UWP).

Visual Studio memudahkan penulisan dan penyebaran jenis Windows Runtime kustom Anda sendiri di dalam proyek komponen Windows Runtime (WRC) yang ditulis dengan C# atau Visual Basic, lalu mereferensikan WRC tersebut dari proyek aplikasi JavaScript, dan untuk menggunakan jenis kustom tersebut dari aplikasi tersebut.

Secara internal, jenis Windows Runtime Anda dapat menggunakan fungsionalitas .NET apa pun yang diizinkan dalam aplikasi UWP.

Secara eksternal, anggota jenis Anda hanya dapat mengekspos jenis Windows Runtime untuk parameter mereka dan mengembalikan nilai. Saat Anda membangun solusi, Visual Studio membangun proyek .NET WRC Anda, lalu menjalankan langkah build yang membuat file metadata Windows (.winmd). Ini adalah komponen Windows Runtime Anda, yang disertakan Visual Studio di aplikasi Anda.

Catatan

.NET secara otomatis memetakan beberapa jenis .NET yang umum digunakan, seperti jenis data primitif dan jenis pengumpulan, ke windows Runtime yang setara. Jenis .NET ini dapat digunakan di antarmuka publik komponen Windows Runtime, dan akan muncul kepada pengguna komponen sebagai jenis Windows Runtime yang sesuai. Lihat Komponen Windows Runtime dengan C# dan Visual Basic.

Prasyarat:

Catatan

proyek Platform Windows Universal (UWP) yang menggunakan JavaScript tidak didukung di Visual Studio 2019. Lihat JavaScript dan TypeScript di Visual Studio 2019. Untuk mengikuti topik ini, kami sarankan Anda menggunakan Visual Studio 2017. Lihat JavaScript di Visual Studio 2017.

Membuat kelas Windows Runtime sederhana

Bagian ini membuat aplikasi JavaScript UWP, dan menambahkan ke solusi proyek komponen Visual Basic atau C# Windows Runtime. Ini menunjukkan cara menentukan jenis Windows Runtime, membuat instans jenis dari JavaScript, dan memanggil anggota statis dan instans. Tampilan visual aplikasi contoh sengaja low-key untuk menjaga fokus pada komponen.

  1. Di Visual Studio, buat proyek JavaScript baru: Pada bilah menu, pilih File, Baru, Proyek. Di bagian Templat Terinstal dari kotak dialog Proyek Baru , pilih JavaScript, lalu pilih Windows, lalu Universal. (Jika Windows tidak tersedia, pastikan Anda menggunakan Windows 8 atau yang lebih baru.) Pilih templat Aplikasi Kosong dan masukkan SampleApp untuk nama proyek.

  2. Buat proyek komponen: Di Penjelajah Solusi, buka menu pintasan untuk solusi SampleApp dan pilih Tambahkan, lalu pilih Proyek Baru untuk menambahkan proyek C# atau Visual Basic baru ke solusi. Di bagian Templat Terinstal dari kotak dialog Tambahkan Proyek Baru , pilih Visual Basic atau Visual C#, lalu pilih Windows, lalu Universal. Pilih templat Komponen Runtime Windows dan masukkan SampleComponent untuk nama proyek.

  3. Ubah nama kelas menjadi Contoh. Perhatikan bahwa secara default, kelas ditandai disegel publik (NotInheritable Publik di Visual Basic). Semua kelas Windows Runtime yang Anda ekspos dari komponen Anda harus disegel.

  4. Tambahkan dua anggota sederhana ke kelas , metode statis (Metode bersama di Visual Basic) dan properti instans:

    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. Opsional: Untuk mengaktifkan IntelliSense untuk anggota yang baru ditambahkan, di Penjelajah Solusi, buka menu pintasan untuk proyek SampleComponent, lalu pilih Bangun.

  6. Di Penjelajah Solusi, di proyek JavaScript, buka menu pintasan untuk Referensi, lalu pilih Tambahkan Referensi untuk membuka Pengelola Referensi. Pilih Proyek, lalu pilih Solusi. Pilih kotak centang untuk proyek SampleComponent dan pilih OK untuk menambahkan referensi.

Memanggil komponen dari JavaScript

Untuk menggunakan jenis Windows Runtime dari JavaScript, tambahkan kode berikut dalam fungsi anonim dalam file default.js (di folder js proyek) yang disediakan oleh templat Visual Studio. Ini harus pergi setelah penanganan aktivitas app.oncheckpoint dan sebelum panggilan ke 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;
}

Perhatikan bahwa huruf pertama dari setiap nama anggota diubah dari huruf besar menjadi huruf kecil. Transformasi ini adalah bagian dari dukungan yang disediakan JavaScript untuk mengaktifkan penggunaan alami Windows Runtime. Namespace layanan dan nama kelas berkode Pascal. Nama anggota adalah camel-cased kecuali untuk nama peristiwa, yang semuanya huruf kecil. Lihat Menggunakan Windows Runtime di JavaScript. Aturan untuk casing unta bisa membingungkan. Serangkaian huruf besar awal biasanya muncul sebagai huruf kecil, tetapi jika tiga huruf besar diikuti oleh huruf kecil, hanya dua huruf pertama yang muncul dalam huruf kecil: misalnya, anggota bernama IDStringKind muncul sebagai idStringKind. Di Visual Studio, Anda dapat membuat proyek komponen Windows Runtime lalu menggunakan IntelliSense di proyek JavaScript Anda untuk melihat casing yang benar.

Dengan cara yang sama, .NET menyediakan dukungan untuk mengaktifkan penggunaan alami Windows Runtime dalam kode terkelola. Ini dibahas di bagian berikutnya dari artikel ini, dan dalam artikel komponen Windows Runtime dengan dukungan C# dan Visual Basic dan .NET untuk aplikasi UWP dan Windows Runtime.

Membuat antarmuka pengguna sederhana

Di proyek JavaScript Anda, buka file default.html dan perbarui isi seperti yang ditunjukkan dalam kode berikut. Kode ini mencakup serangkaian kontrol lengkap untuk aplikasi contoh dan menentukan nama fungsi untuk peristiwa klik.

Catatan Saat Anda pertama kali menjalankan aplikasi, hanya tombol Dasar1 dan Dasar2 yang didukung.

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

Di proyek JavaScript Anda, di folder css, buka default.css. Ubah bagian isi seperti yang ditunjukkan, dan tambahkan gaya untuk mengontrol tata letak tombol dan penempatan teks output.

body
{
    -ms-grid-columns: 1fr;
    -ms-grid-rows: 1fr 14fr;
    display: -ms-grid;
}

#buttons {
    -ms-grid-rows: 1fr;
    -ms-grid-columns: auto;
    -ms-grid-row-align: start;
}
#output {
    -ms-grid-row: 2;
    -ms-grid-column: 1;
}

Sekarang tambahkan kode pendaftaran pendengar peristiwa dengan menambahkan klausa kemudian ke prosesSemua panggilan di app.onactivated di default.js. Ganti baris kode yang ada yang memanggil setPromise dan ubah ke kode berikut:

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

Ini adalah cara yang lebih baik untuk menambahkan peristiwa ke kontrol HTML daripada menambahkan penanganan aktivitas klik langsung di HTML. Lihat Membuat aplikasi "Halo, Dunia" (JS).

Buat dan jalankan aplikasi

Sebelum Anda membuat, ubah platform target untuk semua proyek ke Arm, x64, atau x86, yang sesuai untuk komputer Anda.

Untuk membangun dan menjalankan solusi, pilih kunci F5. (Jika Anda mendapatkan pesan kesalahan run-time yang menyatakan bahwa SampleComponent tidak ditentukan, referensi ke proyek pustaka kelas hilang.)

Visual Studio pertama-tama mengkompilasi pustaka kelas, lalu menjalankan tugas MSBuild yang berjalan Winmdexp.exe (Alat Ekspor Metadata Runtime Windows) untuk membuat komponen Windows Runtime Anda. Komponen disertakan dalam file .winmd yang berisi kode terkelola dan metadata Windows yang menjelaskan kode. WinMdExp.exe menghasilkan pesan kesalahan build saat Anda menulis kode yang tidak valid dalam komponen Windows Runtime, dan pesan kesalahan ditampilkan di IDE Visual Studio. Visual Studio menambahkan komponen Anda ke paket aplikasi (file.appx) untuk aplikasi UWP Anda, dan menghasilkan manifes yang sesuai.

Pilih tombol Dasar 1 untuk menetapkan nilai pengembalian dari metode GetAnswer statis ke area output, membuat instans kelas Contoh, dan menampilkan nilai properti SampleProperty-nya di area output. Output ditampilkan di sini:

"The answer is 42."
0

Pilih tombol Dasar 2 untuk menambah nilai properti SampleProperty dan untuk menampilkan nilai baru di area output. Jenis primitif seperti string dan angka dapat digunakan sebagai jenis parameter dan jenis pengembalian, dan dapat diteruskan antara kode terkelola dan JavaScript. Karena angka dalam JavaScript disimpan dalam format floating-point presisi ganda, angka tersebut dikonversi ke jenis numerik .NET Framework.

Catatan Secara default, Anda hanya dapat mengatur titik henti dalam kode JavaScript Anda. Untuk men-debug kode Visual Basic atau C#, lihat Membuat komponen Windows Runtime di C# dan Visual Basic.

Untuk menghentikan penelusuran kesalahan dan menutup aplikasi Anda, beralih dari aplikasi ke Visual Studio, dan pilih Shift+F5.

Menggunakan Windows Runtime dari JavaScript dan kode terkelola

Windows Runtime dapat dipanggil dari JavaScript atau kode terkelola. Objek Windows Runtime dapat diteruskan bolak-balik antara keduanya, dan peristiwa dapat ditangani dari kedua sisi. Namun, cara Anda menggunakan jenis Windows Runtime di dua lingkungan berbeda dalam beberapa detail, karena JavaScript dan .NET mendukung Windows Runtime secara berbeda. Contoh berikut menunjukkan perbedaan ini, menggunakan kelas Windows.Foundation.Collections.PropertySet . Dalam contoh ini, Anda membuat instans koleksi PropertySet dalam kode terkelola dan mendaftarkan penanganan aktivitas untuk melacak perubahan dalam koleksi. Kemudian Anda menambahkan kode JavaScript yang mendapatkan koleksi, mendaftarkan penanganan aktivitasnya sendiri, dan menggunakan koleksi. Terakhir, Anda menambahkan metode yang membuat perubahan pada koleksi dari kode terkelola dan menunjukkan JavaScript yang menangani pengecualian terkelola.

Penting Dalam contoh ini, peristiwa sedang dipicu pada utas UI. Jika Anda mengaktifkan peristiwa dari utas latar belakang, misalnya dalam panggilan asinkron, Anda harus melakukan beberapa pekerjaan tambahan agar JavaScript dapat menangani peristiwa tersebut. Untuk informasi selengkapnya, lihat Meningkatkan Peristiwa di komponen Windows Runtime.

Dalam proyek SampleComponent, tambahkan kelas tertutup publik baru (kelas NotInheritable Publik di Visual Basic) bernama PropertySetStats. Kelas ini membungkus koleksi PropertySet dan menangani peristiwa MapChanged-nya. Penanganan aktivitas melacak jumlah perubahan dari setiap jenis yang terjadi, dan metode DisplayStats menghasilkan laporan yang diformat dalam HTML. Perhatikan pernyataan penggunaan tambahan (Pernyataan impor di Visual Basic); berhati-hatilah untuk menambahkan ini ke pernyataan penggunaan yang ada daripada menimpanya.

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

Penanganan aktivitas mengikuti pola peristiwa .NET Framework yang sudah dikenal, kecuali bahwa pengirim peristiwa (dalam hal ini, objek PropertySet) ditransmisikan ke string IObservableMap<, antarmuka objek> (IObservableMap(Of String, Object) di Visual Basic), yang merupakan instansiasi antarmuka Windows Runtime IObservableMap<K, V>. (Anda dapat melemparkan pengirim ke jenisnya jika perlu.) Selain itu, argumen peristiwa disajikan sebagai antarmuka daripada sebagai objek.

Dalam file default.js, tambahkan fungsi Runtime1 seperti yang ditunjukkan. Kode ini membuat objek PropertySetStats, mendapatkan koleksi PropertySet-nya, dan menambahkan penanganan aktivitasnya sendiri, fungsi onMapChanged, untuk menangani peristiwa MapChanged. Setelah membuat perubahan pada koleksi, runtime1 memanggil metode DisplayStats untuk menampilkan ringkasan jenis perubahan.

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

Cara Anda menangani peristiwa Windows Runtime di JavaScript sangat berbeda dari cara Anda menanganinya dalam kode .NET Framework. Penanganan aktivitas JavaScript hanya mengambil satu argumen. Saat Anda melihat objek ini di debugger Visual Studio, properti pertama adalah pengirim. Anggota antarmuka argumen peristiwa juga muncul langsung pada objek ini.

Untuk menjalankan aplikasi, pilih kunci F5. Jika kelas tidak disegel, Anda mendapatkan pesan kesalahan, "Mengekspor jenis yang tidak disegel 'SampleComponent.Example' saat ini tidak didukung. Tolong tandai sebagai disegel."

Pilih tombol Runtime 1 . Penanganan aktivitas menampilkan perubahan saat elemen ditambahkan atau diubah, dan di akhir metode DisplayStats dipanggil untuk menghasilkan ringkasan jumlah. Untuk menghentikan penelusuran kesalahan dan menutup aplikasi, beralih kembali ke Visual Studio dan pilih Shift+F5.

Untuk menambahkan dua item lagi ke koleksi PropertySet dari kode terkelola, tambahkan kode berikut ke kelas 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

Kode ini menyoroti perbedaan lain dalam cara Anda menggunakan jenis Windows Runtime di dua lingkungan. Jika Anda mengetik kode ini sendiri, Anda akan melihat bahwa IntelliSense tidak menampilkan metode sisipkan yang Anda gunakan dalam kode JavaScript. Sebaliknya, ini menunjukkan metode Tambahkan yang umumnya terlihat pada koleksi di .NET. Ini karena beberapa antarmuka koleksi yang umum digunakan memiliki nama yang berbeda tetapi fungsionalitas serupa di Windows Runtime dan .NET. Saat Anda menggunakan antarmuka ini dalam kode terkelola, antarmuka tersebut muncul sebagai .NET Framework yang setara. Ini dibahas dalam komponen Windows Runtime dengan C# dan Visual Basic. Saat Anda menggunakan antarmuka yang sama di JavaScript, satu-satunya perubahan dari Windows Runtime adalah bahwa huruf besar di awal nama anggota menjadi huruf kecil.

Terakhir, untuk memanggil metode AddMore dengan penanganan pengecualian, tambahkan fungsi runtime2 ke default.js.

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

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

Tambahkan kode pendaftaran penanganan aktivitas dengan cara yang sama seperti yang Anda lakukan sebelumnya.

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

Untuk menjalankan aplikasi, pilih kunci F5. Pilih Runtime 1 lalu Runtime 2. Penanganan aktivitas JavaScript melaporkan perubahan pertama pada koleksi. Namun, perubahan kedua memiliki kunci duplikat. Pengguna kamus .NET Framework mengharapkan metode Tambahkan untuk memberikan pengecualian, dan itulah yang terjadi. JavaScript menangani pengecualian .NET.

Catatan Anda tidak dapat menampilkan pesan pengecualian dari kode JavaScript. Teks pesan digantikan oleh jejak tumpukan. Untuk informasi selengkapnya, lihat "Melemparkan pengecualian" dalam Membuat komponen Windows Runtime di C# dan Visual Basic.

Sebaliknya, ketika JavaScript memanggil metode sisipkan dengan kunci duplikat, nilai item diubah. Perbedaan perilaku ini disebabkan oleh berbagai cara JavaScript dan .NET mendukung Windows Runtime, seperti yang dijelaskan dalam komponen Windows Runtime dengan C# dan Visual Basic.

Mengembalikan jenis terkelola dari komponen Anda

Seperti yang dibahas sebelumnya, Anda dapat meneruskan jenis Windows Runtime asli secara bebas antara kode JavaScript dan kode C# atau Visual Basic Anda. Sebagian besar waktu, nama jenis dan nama anggota akan sama dalam kedua kasus (kecuali bahwa nama anggota dimulai dengan huruf kecil di JavaScript). Namun, di bagian sebelumnya, kelas PropertySet tampaknya memiliki anggota yang berbeda dalam kode terkelola. (Misalnya, di JavaScript Anda memanggil metode sisipkan, dan dalam kode .NET Yang Anda sebut metode Tambahkan.) Bagian ini mengeksplorasi cara perbedaan tersebut memengaruhi jenis .NET Framework diteruskan ke JavaScript.

Selain mengembalikan jenis Windows Runtime yang Anda buat di komponen Anda atau diteruskan ke komponen Anda dari JavaScript, Anda dapat mengembalikan jenis terkelola, yang dibuat dalam kode terkelola, ke JavaScript seolah-olah itu adalah jenis Windows Runtime yang sesuai. Bahkan di contoh pertama yang sederhana dari kelas runtime, parameter dan jenis pengembalian anggota adalah jenis primitif Visual Basic atau C#, yang merupakan jenis .NET Framework. Untuk menunjukkan ini untuk koleksi, tambahkan kode berikut ke kelas Contoh, untuk membuat metode yang mengembalikan kamus generik string, yang diindeks oleh bilangan bulat:

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

Perhatikan bahwa kamus harus dikembalikan sebagai antarmuka yang diimplementasikan oleh Kamus<TKey, TValue>, dan yang memetakan ke antarmuka Windows Runtime. Dalam hal ini, antarmukanya adalah int IDictionary<, string> (IDictionary(Of Integer, String) di Visual Basic). Saat int IMap<jenis Windows Runtime, string> diteruskan ke kode terkelola, string, dan int> IDictionary<muncul true saat jenis terkelola diteruskan ke JavaScript.

Penting Saat jenis terkelola mengimplementasikan beberapa antarmuka, JavaScript menggunakan antarmuka yang muncul terlebih dahulu dalam daftar. Misalnya, jika Anda mengembalikan int Kamus<, string> ke kode JavaScript, itu muncul sebagai int IDictionary<, string> apa pun antarmuka yang Anda tentukan sebagai jenis pengembalian. Ini berarti bahwa jika antarmuka pertama tidak menyertakan anggota yang muncul di antarmuka selanjutnya, anggota tersebut tidak terlihat oleh JavaScript.

 

Untuk menguji metode baru dan menggunakan kamus, tambahkan fungsi returns1 dan returns2 ke 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>";
}

Tambahkan kode pendaftaran peristiwa ke yang sama lalu blokir sebagai kode pendaftaran peristiwa lainnya:

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

Ada beberapa hal menarik untuk diamati tentang kode JavaScript ini. Pertama-tama, ini termasuk fungsi showMap untuk menampilkan konten kamus dalam HTML. Dalam kode untuk showMap, perhatikan pola iterasi. Di .NET, tidak ada metode Pertama pada antarmuka IDictionary generik, dan ukurannya dikembalikan oleh properti Count daripada dengan metode Ukuran. Untuk JavaScript, int IDictionary<, string> tampaknya merupakan int IMap<jenis Windows Runtime, string>. (Lihat antarmuka IMap<K,V> .)

Dalam fungsi returns2, seperti dalam contoh sebelumnya, JavaScript memanggil metode Insert (sisipkan di JavaScript) untuk menambahkan item ke kamus.

Untuk menjalankan aplikasi, pilih kunci F5. Untuk membuat dan menampilkan konten awal kamus, pilih tombol Kembalikan 1 . Untuk menambahkan dua entri lagi ke kamus, pilih tombol Kembalikan 2 . Perhatikan bahwa entri ditampilkan dalam urutan penyisipan, seperti yang Anda harapkan dari Kamus<TKey, TValue>. Jika Anda ingin diurutkan, Anda bisa mengembalikan int SortedDictionary<, string> dari GetMapOfNames. (Kelas PropertySet yang digunakan dalam contoh sebelumnya memiliki organisasi internal yang berbeda dari Kamus<TKey, TValue>.)

Tentu saja, JavaScript bukan bahasa yang diketik dengan kuat, jadi menggunakan koleksi generik yang diketik dengan kuat dapat menyebabkan beberapa hasil yang mengejutkan. Pilih lagi tombol Kembali 2 . JavaScript secara wajib memaksa "7" ke numerik 7, dan numerik 7 yang disimpan dalam ct ke string. Dan memaksa string "empat puluh" ke nol. Tapi itu hanya permulaan. Pilih tombol Kembali 2 beberapa kali lagi. Dalam kode terkelola, metode Tambahkan akan menghasilkan pengecualian kunci duplikat, bahkan jika nilai ditransmisikan ke jenis yang benar. Sebaliknya, metode Sisipkan memperbarui nilai yang terkait dengan kunci yang ada dan mengembalikan nilai Boolean yang menunjukkan apakah kunci baru ditambahkan ke kamus. Inilah sebabnya mengapa nilai yang terkait dengan kunci 7 terus berubah.

Perilaku tak terduga lainnya: Jika Anda meneruskan variabel JavaScript yang tidak ditetapkan sebagai argumen string, yang Anda dapatkan adalah string "tidak terdefinisi". Singkatnya, berhati-hatilah saat Anda meneruskan .NET Framework jenis koleksi ke kode JavaScript Anda.

Catatan Jika Anda memiliki teks dalam jumlah besar untuk digabungkan, Anda dapat melakukannya dengan lebih efisien dengan memindahkan kode ke dalam metode .NET Framework dan menggunakan kelas StringBuilder, seperti yang ditunjukkan dalam fungsi showMap.

Meskipun Anda tidak dapat mengekspos jenis generik Anda sendiri dari komponen Windows Runtime, Anda dapat mengembalikan koleksi generik .NET Framework untuk kelas Windows Runtime dengan menggunakan kode seperti berikut ini:

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> mengimplementasikan IList<T>, yang muncul sebagai jenis Windows Runtime IVector<T> di JavaScript.

Mendeklarasikan peristiwa

Anda dapat mendeklarasikan peristiwa dengan menggunakan pola peristiwa .NET Framework standar atau pola lain yang digunakan oleh Windows Runtime. .NET Framework mendukung kesetaraan antara delegasi System.EventHandler<TEventArgs> dan delegasi Windows Runtime EventHandler<T>, jadi menggunakan EventHandler<TEventArgs> adalah cara yang baik untuk menerapkan pola .NET Framework standar. Untuk melihat cara kerjanya, tambahkan sepasang kelas berikut ke proyek 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

Saat Anda mengekspos peristiwa di Windows Runtime, kelas argumen peristiwa mewarisi dari System.Object. Ini tidak mewarisi dari System.EventArgs, seperti di .NET, karena EventArgs bukan jenis Windows Runtime.

Jika Anda mendeklarasikan pengaktif peristiwa kustom untuk peristiwa Anda (Kata kunci kustom di Visual Basic), Anda harus menggunakan pola peristiwa Windows Runtime. Lihat Peristiwa kustom dan pengaktif peristiwa di komponen Windows Runtime.

Untuk menangani peristiwa Uji, tambahkan fungsi events1 ke default.js. Fungsi events1 membuat fungsi penanganan aktivitas untuk peristiwa Uji, dan segera memanggil metode OnTest untuk meningkatkan peristiwa. Jika Anda menempatkan titik henti di isi penanganan aktivitas, Anda dapat melihat bahwa objek yang diteruskan ke parameter tunggal menyertakan objek sumber dan kedua anggota 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);
}

Tambahkan kode pendaftaran peristiwa ke yang sama lalu blokir sebagai kode pendaftaran peristiwa lainnya:

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

Mengekspos operasi asinkron

.NET Framework memiliki seperangkat alat yang kaya untuk pemrosesan asinkron dan pemrosesan paralel, berdasarkan kelas Tugas dan TResult> Tugas< generik. Untuk mengekspos pemrosesan asinkron berbasis tugas dalam komponen Windows Runtime, gunakan antarmuka Windows Runtime IAsyncAction, IAsyncActionWithProgress<TProgress>, IAsyncOperation<TResult>, dan IAsyncOperationWithProgress<TResult, TProgress>. (Di Windows Runtime, operasi mengembalikan hasil, tetapi tindakan tidak.)

Bagian ini menunjukkan operasi asinkron yang dapat dibatalkan yang melaporkan kemajuan dan mengembalikan hasil. Metode GetPrimesInRangeAsync menggunakan kelas AsyncInfo untuk menghasilkan tugas dan untuk menghubungkan fitur pembatalan dan pelaporan kemajuannya ke objek WinJS.Promise. Mulailah dengan menambahkan metode GetPrimesInRangeAsync ke kelas contoh:

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 adalah penemu angka utama yang sangat sederhana, dan itu berdasarkan desain. Fokus di sini adalah menerapkan operasi asinkron, sehingga kesederhanaan penting, dan implementasi yang lambat adalah keuntungan ketika kami menunjukkan pembatalan. GetPrimesInRangeAsync menemukan primes dengan brute force: Ini membagi kandidat dengan semua bilangan bulat yang kurang dari atau sama dengan akar kuadratnya, daripada hanya menggunakan angka utama. Menelusuri kode ini:

  • Sebelum memulai operasi asinkron, lakukan aktivitas housekeeping seperti memvalidasi parameter dan melemparkan pengecualian untuk input yang tidak valid.

  • Kunci untuk implementasi ini adalah metode AsyncInfo.Run<TResult, TProgress>(Func<CancellationToken, IProgress<TProgress>, Task<TResult>>), dan delegasi yang merupakan satu-satunya parameter metode. Delegasi harus menerima token pembatalan dan antarmuka untuk kemajuan pelaporan, dan harus mengembalikan tugas yang dimulai yang menggunakan parameter tersebut. Ketika JavaScript memanggil metode GetPrimesInRangeAsync, langkah-langkah berikut terjadi (belum tentu dalam urutan yang diberikan di sini):

    • Objek WinJS.Promise menyediakan fungsi untuk memproses hasil yang dikembalikan, bereaksi terhadap pembatalan, dan menangani laporan kemajuan.

    • Metode AsyncInfo.Run membuat sumber pembatalan dan objek yang mengimplementasikan antarmuka IProgress<T> . Kepada delegasi, ia meneruskan token CancellationToken dari sumber pembatalan, dan antarmuka IProgress<T> .

      Catatan Jika objek Promise tidak menyediakan fungsi untuk bereaksi terhadap pembatalan, AsyncInfo.Run masih meneruskan token yang dapat dibatalkan, dan pembatalan masih dapat terjadi. Jika objek Promise tidak menyediakan fungsi untuk menangani pembaruan kemajuan, AsyncInfo.Run masih menyediakan objek yang mengimplementasikan IProgress<T>, tetapi laporannya diabaikan.

    • Delegasi menggunakan metode Task.Run<TResult>(Func<TResult>, CancellationToken) untuk membuat tugas yang dimulai yang menggunakan token dan antarmuka kemajuan. Delegasi untuk tugas yang dimulai disediakan oleh fungsi lambda yang menghitung hasil yang diinginkan. Selengkapnya akan segera kami bahas.

    • Metode AsyncInfo.Run membuat objek yang mengimplementasikan IAsyncOperationWithProgress<TResult, antarmuka TProgress> , menghubungkan mekanisme pembatalan Windows Runtime dengan sumber token, dan menghubungkan fungsi pelaporan kemajuan objek Promise dengan antarmuka IProgress<T> .

    • Antarmuka TResult IAsyncOperationWithProgress<, TProgress> dikembalikan ke JavaScript.

  • Fungsi lambda yang diwakili oleh tugas yang dimulai tidak mengambil argumen apa pun. Karena ini adalah fungsi lambda, ia memiliki akses ke token dan antarmuka IProgress. Setiap kali nomor kandidat dievaluasi, fungsi lambda:

    • Memeriksa untuk melihat apakah titik persentase kemajuan berikutnya telah tercapai. Jika sudah, fungsi lambda memanggil IProgress<T>. Metode laporan, dan persentase diteruskan ke fungsi yang ditentukan objek Promise untuk kemajuan pelaporan.
    • Menggunakan token pembatalan untuk memberikan pengecualian jika operasi telah dibatalkan. Jika metode IAsyncInfo.Cancel (yang disiapkan oleh IAsyncOperationWithProgress<TResult, antarmuka TProgress> ) telah dipanggil, koneksi yang disiapkan metode AsyncInfo.Run memastikan bahwa token pembatalan diberi tahu.
  • Ketika fungsi lambda mengembalikan daftar angka utama, daftar diteruskan ke fungsi yang ditentukan objek WinJS.Promise untuk memproses hasilnya.

Untuk membuat janji JavaScript dan menyiapkan mekanisme pembatalan, tambahkan fungsi asyncRun dan asyncCancel ke 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();
}

Jangan lupa kode pendaftaran peristiwa sama seperti yang Anda lakukan sebelumnya.

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

Dengan memanggil metode GetPrimesInRangeAsync asinkron, fungsi asyncRun membuat objek WinJS.Promise. Metode objek kemudian mengambil tiga fungsi yang memproses hasil yang dikembalikan, bereaksi terhadap kesalahan (termasuk pembatalan), dan menangani laporan kemajuan. Dalam contoh ini, hasil yang dikembalikan dicetak di area output. Pembatalan atau penyelesaian mengatur ulang tombol yang meluncurkan dan membatalkan operasi. Pelaporan kemajuan memperbarui kontrol kemajuan.

Fungsi asyncCancel hanya memanggil metode pembatalan objek WinJS.Promise.

Untuk menjalankan aplikasi, pilih kunci F5. Untuk memulai operasi asinkron, pilih tombol Asinkron . Apa yang terjadi selanjutnya tergantung pada seberapa cepat komputer Anda. Jika bilah kemajuan zip selesai sebelum Anda memiliki waktu untuk berkedip, tingkatkan ukuran nomor awal yang diteruskan ke GetPrimesInRangeAsync dengan satu atau beberapa faktor sepuluh. Anda dapat menyempurnakan durasi operasi dengan meningkatkan atau mengurangi jumlah angka untuk diuji, tetapi menambahkan nol di tengah nomor awal akan berdampak lebih besar. Untuk membatalkan operasi, pilih tombol Batalkan Asinkron .