Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
Topik ini dibangun berdasarkan komponen Windows Runtime dan aplikasi yang menggunakannya, yang cara membuatnya ditunjukkan dalam topik Komponen Windows Runtime dengan C++/WinRT.
Berikut adalah fitur baru yang ditambahkan topik ini.
- Perbarui kelas runtime termometer untuk memicu event saat suhunya turun di bawah titik beku.
- Perbarui Aplikasi Inti yang menggunakan kelas runtime termometer sehingga menangani peristiwa tersebut.
Note
Untuk informasi tentang menginstal dan menggunakan C++/WinRT Visual Studio Extension (VSIX) dan paket NuGet (yang bersama-sama menyediakan templat proyek dan dukungan build), lihat dukungan Visual Studio untuk C++/WinRT.
Important
Untuk konsep dan istilah penting yang mendukung pemahaman Anda tentang cara menggunakan dan menulis kelas runtime dengan C++/WinRT, lihat Menggunakan API dengan C++/WinRT dan API Penulis dengan C++/WinRT.
Membuat ThermometerWRC dan ThermometerCoreApp
Jika Anda ingin mengikuti pembaruan yang ditampilkan dalam topik ini, sehingga Anda dapat membangun dan menjalankan kode, maka langkah pertama adalah mengikuti panduan dalam komponen Windows Runtime dengan topik C++/WinRT. Dengan demikian, Anda akan memiliki komponen ThermometerWRC Windows Runtime, dan Aplikasi ThermometerCoreApp Core yang mengonsumsinya.
Perbarui ThermometerWRC untuk memicu event
Perbarui Thermometer.idl agar terlihat seperti daftar di bawah ini. Berikut adalah cara mendeklarasikan kejadian yang memiliki jenis delegasi EventHandler dengan argumen berupa bilangan floating-point presisi tunggal.
// Thermometer.idl
namespace ThermometerWRC
{
runtimeclass Thermometer
{
Thermometer();
void AdjustTemperature(Single deltaFahrenheit);
event Windows.Foundation.EventHandler<Single> TemperatureIsBelowFreezing;
};
}
Simpan file tersebut. Proyek tidak akan berhasil di-build sampai selesai dalam keadaan saat ini, tetapi tetap lakukan build sekarang untuk menghasilkan versi terbaru dari file stub \ThermometerWRC\ThermometerWRC\Generated Files\sources\Thermometer.h dan Thermometer.cpp. Di dalam file tersebut, Anda sekarang dapat melihat implementasi stub dari peristiwa TemperatureIsBelowFreezing . Dalam C++/WinRT, peristiwa yang dideklarasikan IDL diimplementasikan sebagai serangkaian fungsi yang kelebihan beban (mirip dengan cara properti diimplementasikan sebagai sepasang fungsi get dan set yang kelebihan beban). Salah satu overload menerima delegasi untuk didaftarkan, dan mengembalikan sebuah token (yaitu winrt::event_token). Yang lain mengambil token, dan mencabut pendaftaran delegasi terkait.
Sekarang buka Thermometer.h dan Thermometer.cpp, dan perbarui implementasi kelas runtime Thermometer . Dalam Thermometer.h, tambahkan dua fungsi TemperatureIsBelowFreezing yang kelebihan beban, serta anggota data peristiwa privat untuk digunakan dalam implementasi fungsi tersebut.
// Thermometer.h
...
namespace winrt::ThermometerWRC::implementation
{
struct Thermometer : ThermometerT<Thermometer>
{
...
winrt::event_token TemperatureIsBelowFreezing(Windows::Foundation::EventHandler<float> const& handler);
void TemperatureIsBelowFreezing(winrt::event_token const& token) noexcept;
private:
winrt::event<Windows::Foundation::EventHandler<float>> m_temperatureIsBelowFreezingEvent;
...
};
}
...
Seperti yang Anda lihat di atas, peristiwa diwakili oleh templat winrt::event struct, diparameterkan oleh jenis delegasi tertentu (yang sendiri dapat diparameterkan oleh jenis args).
Dalam Thermometer.cpp, terapkan dua fungsi TemperatureIsBelowFreezing yang kelebihan beban.
// Thermometer.cpp
...
namespace winrt::ThermometerWRC::implementation
{
winrt::event_token Thermometer::TemperatureIsBelowFreezing(Windows::Foundation::EventHandler<float> const& handler)
{
return m_temperatureIsBelowFreezingEvent.add(handler);
}
void Thermometer::TemperatureIsBelowFreezing(winrt::event_token const& token) noexcept
{
m_temperatureIsBelowFreezingEvent.remove(token);
}
void Thermometer::AdjustTemperature(float deltaFahrenheit)
{
m_temperatureFahrenheit += deltaFahrenheit;
if (m_temperatureFahrenheit < 32.f) m_temperatureIsBelowFreezingEvent(*this, m_temperatureFahrenheit);
}
}
Note
Untuk detail tentang apa itu pencabut peristiwa otomatis, lihat Mencabut delegasi terdaftar. Anda mendapatkan implementasi pencabut peristiwa otomatis secara gratis untuk acara Anda. Dengan kata lain, Anda tidak perlu mengimplementasikan overload untuk event revoker—karena C++/WinRT projection sudah menyediakannya untuk Anda.
Varian overload lainnya (overload pendaftaran dan pencabutan manual) tidak disertakan dalam proyeksi. Itu untuk memberi Anda fleksibilitas untuk mengimplementasikannya secara optimal untuk skenario Anda. Memanggil event::add dan event::remove seperti yang ditunjukkan dalam implementasi ini adalah pilihan default yang efisien dan aman untuk konkurensi/thread. Tetapi jika Anda memiliki jumlah kejadian yang sangat besar, Anda mungkin tidak menginginkan field terpisah untuk setiap kejadian, melainkan memilih semacam implementasi sparse.
Anda juga dapat melihat di atas bahwa implementasi fungsi AdjustTemperature telah diperbarui untuk meningkatkan peristiwa TemperatureIsBelowFreezing jika suhu berada di bawah pembekuan.
Perbarui ThermometerCoreApp untuk menangani peristiwa
Dalam proyek ThermometerCoreApp , di App.cpp, buat perubahan berikut pada kode untuk mendaftarkan penanganan aktivitas, lalu menyebabkan suhu berada di bawah pembekuan.
WINRT_ASSERT adalah definisi makro, dan meluas ke _ASSERTE.
struct App : implements<App, IFrameworkViewSource, IFrameworkView>
{
winrt::event_token m_eventToken;
...
void Initialize(CoreApplicationView const &)
{
m_eventToken = m_thermometer.TemperatureIsBelowFreezing([](const auto &, float temperatureFahrenheit)
{
WINRT_ASSERT(temperatureFahrenheit < 32.f); // Put a breakpoint here.
});
}
...
void Uninitialize()
{
m_thermometer.TemperatureIsBelowFreezing(m_eventToken);
}
...
void OnPointerPressed(IInspectable const &, PointerEventArgs const & args)
{
m_thermometer.AdjustTemperature(-1.f);
...
}
...
};
Waspadai perubahan pada metode OnPointerPressed . Sekarang, setiap kali Anda mengklik jendela, Anda mengurangi 1 derajat Fahrenheit dari suhu termometer. Dan sekarang, aplikasi menangani kejadian yang dipicu ketika suhu turun di bawah titik beku. Untuk menunjukkan bahwa peristiwa sedang dinaikkan seperti yang diharapkan, letakkan titik henti di dalam ekspresi lambda yang menangani peristiwa TemperatureIsBelowFreezing , jalankan aplikasi, dan klik di dalam jendela.
Delegasi berparameter di seluruh ABI
Jika event Anda harus dapat diakses melalui antarmuka biner aplikasi (ABI)—misalnya antara sebuah komponen dan aplikasi yang menggunakannya—maka event tersebut harus menggunakan tipe delegasi Windows Runtime. Contoh di atas menggunakan tipe delegasi Windows Runtime Windows::Foundation::EventHandler<T>. TypedEventHandler<TSender, TResult> adalah contoh lain dari jenis delegasi Windows Runtime.
Parameter tipe untuk kedua tipe delegasi tersebut harus melewati ABI, sehingga parameter tipe tersebut juga harus berupa tipe Windows Runtime. Itu termasuk kelas runtime Windows, kelas runtime pihak ketiga, dan tipe primitif seperti angka dan string. Kompiler memberi Anda kesalahan "T harus berupa tipe WinRT" jika Anda lupa batasan tersebut.
Di bawah ini adalah contoh dalam bentuk daftar kode. Mulailah dengan proyek ThermometerWRC dan ThermometerCoreApp yang Anda buat sebelumnya dalam topik ini, dan edit kode dalam proyek tersebut agar terlihat seperti kode dalam daftar ini.
Daftar pertama ini untuk proyek ThermometerWRC . Setelah mengedit seperti yang ThermometerWRC.idl ditunjukkan di bawah ini, bangun proyek, lalu salin MyEventArgs.h dan .cpp ke dalam proyek (dari Generated Files folder) seperti yang Anda lakukan sebelumnya dengan Thermometer.h dan .cpp. Ingatlah untuk menghapus static_assert dari kedua file.
// ThermometerWRC.idl
namespace ThermometerWRC
{
[default_interface]
runtimeclass MyEventArgs
{
Single TemperatureFahrenheit{ get; };
}
[default_interface]
runtimeclass Thermometer
{
...
event Windows.Foundation.EventHandler<ThermometerWRC.MyEventArgs> TemperatureIsBelowFreezing;
...
};
}
// MyEventArgs.h
#pragma once
#include "MyEventArgs.g.h"
namespace winrt::ThermometerWRC::implementation
{
struct MyEventArgs : MyEventArgsT<MyEventArgs>
{
MyEventArgs() = default;
MyEventArgs(float temperatureFahrenheit);
float TemperatureFahrenheit();
private:
float m_temperatureFahrenheit{ 0.f };
};
}
// MyEventArgs.cpp
#include "pch.h"
#include "MyEventArgs.h"
#include "MyEventArgs.g.cpp"
namespace winrt::ThermometerWRC::implementation
{
MyEventArgs::MyEventArgs(float temperatureFahrenheit) : m_temperatureFahrenheit(temperatureFahrenheit)
{
}
float MyEventArgs::TemperatureFahrenheit()
{
return m_temperatureFahrenheit;
}
}
// Thermometer.h
...
struct Thermometer : ThermometerT<Thermometer>
{
...
winrt::event_token TemperatureIsBelowFreezing(Windows::Foundation::EventHandler<ThermometerWRC::MyEventArgs> const& handler);
...
private:
winrt::event<Windows::Foundation::EventHandler<ThermometerWRC::MyEventArgs>> m_temperatureIsBelowFreezingEvent;
...
}
...
// Thermometer.cpp
#include "MyEventArgs.h"
...
winrt::event_token Thermometer::TemperatureIsBelowFreezing(Windows::Foundation::EventHandler<ThermometerWRC::MyEventArgs> const& handler) { ... }
...
void Thermometer::AdjustTemperature(float deltaFahrenheit)
{
m_temperatureFahrenheit += deltaFahrenheit;
if (m_temperatureFahrenheit < 32.f)
{
auto args = winrt::make_self<winrt::ThermometerWRC::implementation::MyEventArgs>(m_temperatureFahrenheit);
m_temperatureIsBelowFreezingEvent(*this, *args);
}
}
...
Daftar ini untuk proyek ThermometerCoreApp .
// App.cpp
...
void Initialize(CoreApplicationView const&)
{
m_eventToken = m_thermometer.TemperatureIsBelowFreezing([](const auto&, ThermometerWRC::MyEventArgs args)
{
float degrees = args.TemperatureFahrenheit();
WINRT_ASSERT(degrees < 32.f); // Put a breakpoint here.
});
}
...
Sinyal sederhana melalui ABI
Jika Anda tidak perlu mengirimkan parameter atau argumen apa pun dengan event Anda, Anda dapat menentukan tipe delegasi Windows Runtime sederhana Anda sendiri. Contoh di bawah ini menunjukkan versi kelas runtime Thermometer yang lebih sederhana. Ini mendeklarasikan tipe delegasi bernama SignalDelegate lalu menggunakannya untuk memicu event bertipe sinyal, alih-alih event dengan parameter.
// ThermometerWRC.idl
namespace ThermometerWRC
{
delegate void SignalDelegate();
runtimeclass Thermometer
{
Thermometer();
event ThermometerWRC.SignalDelegate SignalTemperatureIsBelowFreezing;
void AdjustTemperature(Single value);
};
}
// Thermometer.h
...
namespace winrt::ThermometerWRC::implementation
{
struct Thermometer : ThermometerT<Thermometer>
{
...
winrt::event_token SignalTemperatureIsBelowFreezing(ThermometerWRC::SignalDelegate const& handler);
void SignalTemperatureIsBelowFreezing(winrt::event_token const& token);
void AdjustTemperature(float deltaFahrenheit);
private:
winrt::event<ThermometerWRC::SignalDelegate> m_signal;
float m_temperatureFahrenheit{ 0.f };
};
}
// Thermometer.cpp
...
namespace winrt::ThermometerWRC::implementation
{
winrt::event_token Thermometer::SignalTemperatureIsBelowFreezing(ThermometerWRC::SignalDelegate const& handler)
{
return m_signal.add(handler);
}
void Thermometer::SignalTemperatureIsBelowFreezing(winrt::event_token const& token)
{
m_signal.remove(token);
}
void Thermometer::AdjustTemperature(float deltaFahrenheit)
{
m_temperatureFahrenheit += deltaFahrenheit;
if (m_temperatureFahrenheit < 32.f)
{
m_signal();
}
}
}
// App.cpp
struct App : implements<App, IFrameworkViewSource, IFrameworkView>
{
ThermometerWRC::Thermometer m_thermometer;
winrt::event_token m_eventToken;
...
void Initialize(CoreApplicationView const &)
{
m_eventToken = m_thermometer.SignalTemperatureIsBelowFreezing([] { /* ... */ });
}
...
void Uninitialize()
{
m_thermometer.SignalTemperatureIsBelowFreezing(m_eventToken);
}
...
void OnPointerPressed(IInspectable const &, PointerEventArgs const & args)
{
m_thermometer.AdjustTemperature(-1.f);
...
}
...
};
Delegasi berparameter, sinyal sederhana, dan panggilan balik dalam proyek
Jika Anda memerlukan peristiwa yang bersifat internal untuk proyek Visual Studio Anda (bukan lintas biner), dan peristiwa tersebut tidak terbatas pada tipe Windows Runtime, maka Anda tetap dapat menggunakan templat kelas winrt::event<Delegate>. Cukup gunakan winrt::d elegate alih-alih jenis delegasi Windows Runtime aktual, karena winrt::d elegate juga mendukung parameter non Windows Runtime.
Contoh di bawah ini pertama-tama menunjukkan signature delegate yang tidak menerima parameter apa pun (yang pada dasarnya merupakan sinyal sederhana), kemudian signature delegate yang menerima sebuah string.
winrt::event<winrt::delegate<>> signal;
signal.add([] { std::wcout << L"Hello, "; });
signal.add([] { std::wcout << L"World!" << std::endl; });
signal();
winrt::event<winrt::delegate<std::wstring>> log;
log.add([](std::wstring const& message) { std::wcout << message.c_str() << std::endl; });
log.add([](std::wstring const& message) { Persist(message); });
log(L"Hello, World!");
Perhatikan bagaimana Anda dapat menambahkan ke acara sebanyak mungkin delegasi yang berlangganan sesuai keinginan Anda. Namun, ada beberapa beban tambahan yang terkait dengan suatu event. Jika yang Anda perlukan hanyalah fungsi panggil balik sederhana dengan hanya satu delegasi pelanggan, Anda dapat menggunakan winrt::delegate<... T> secara tersendiri.
winrt::delegate<> signalCallback;
signalCallback = [] { std::wcout << L"Hello, World!" << std::endl; };
signalCallback();
winrt::delegate<std::wstring> logCallback;
logCallback = [](std::wstring const& message) { std::wcout << message.c_str() << std::endl; }f;
logCallback(L"Hello, World!");
Jika Anda melakukan porting dari basis kode C++/CX tempat peristiwa dan delegasi digunakan secara internal dalam proyek, maka winrt::d elegate akan membantu Anda mereplikasi pola tersebut di C++/WinRT.
Peristiwa yang dapat ditangguhkan
Pola umum dalam Windows Runtime adalah peristiwa yang dapat ditangguhkan. Penangan peristiwa meminta penangguhan dengan memanggil metode GetDeferral milik argumen peristiwa. Melakukannya menunjukkan kepada sumber kejadian bahwa aktivitas pasca-peristiwa harus ditunda hingga penangguhkan selesai. Hal ini memungkinkan penanganan aktivitas untuk melakukan tindakan asinkron sebagai respons terhadap suatu peristiwa.
Templat struct winrt::deferrable_event_args merupakan kelas pembantu untuk mengimplementasikan (menghasilkan) pola penangguhan Windows Runtime. Berikut ini contohnya.
// Widget.idl
namespace Sample
{
runtimeclass WidgetStartingEventArgs
{
Windows.Foundation.Deferral GetDeferral();
Boolean Cancel;
};
runtimeclass Widget
{
event Windows.Foundation.TypedEventHandler<
Widget, WidgetStartingEventArgs> Starting;
};
}
// Widget.h
namespace winrt::Sample::implementation
{
struct Widget : WidgetT<Widget>
{
Widget() = default;
event_token Starting(Windows::Foundation::TypedEventHandler<
Sample::Widget, Sample::WidgetStartingEventArgs> const& handler)
{
return m_starting.add(handler);
}
void Starting(event_token const& token) noexcept
{
m_starting.remove(token);
}
private:
event<Windows::Foundation::TypedEventHandler<
Sample::Widget, Sample::WidgetStartingEventArgs>> m_starting;
};
struct WidgetStartingEventArgs : WidgetStartingEventArgsT<WidgetStartingEventArgs>,
deferrable_event_args<WidgetStartingEventArgs>
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
{
bool Cancel() const noexcept { return m_cancel; }
void Cancel(bool value) noexcept { m_cancel = value; }
bool m_cancel = false;
};
}
Berikut adalah cara penerima peristiwa menggunakan pola peristiwa yang dapat ditangguhkan.
// EventRecipient.h
widget.Starting([](auto sender, auto args) -> fire_and_forget
{
auto deferral = args.GetDeferral();
if (!co_await CanWidgetStartAsync(sender))
{
// Do not allow the widget to start.
args.Cancel(true);
}
deferral.Complete();
});
Sebagai pelaksana (produser) sumber peristiwa, Anda memperoleh kelas event args anda dari winrt::d eferrable_event_args. < deferrable_event_argsT> mengimplementasikan T::GetDeferral untuk Anda. Ini juga menyediakan metode bantu baru deferrable_event_args::wait_for_deferrals, yang akan selesai ketika semua deferral yang masih tertunda telah selesai (jika tidak ada deferral yang dibuat, metode ini akan langsung selesai).
// Widget.h
IAsyncOperation<bool> TryStartWidget(Widget const& widget)
{
auto args = make_self<WidgetStartingEventArgs>();
// Raise the event to let people know that the widget is starting
// and give them a chance to prevent it.
m_starting(widget, *args);
// Wait for deferrals to complete.
co_await args->wait_for_deferrals();
// Use the results.
bool started = false;
if (!args->Cancel())
{
widget.InsertBattery();
widget.FlipPowerSwitch();
started = true;
}
co_return started;
}
Pedoman desain
Kami menyarankan agar Anda meneruskan event, bukan delegate, sebagai parameter fungsi. Fungsi add dari winrt::event merupakan satu-satunya pengecualian, karena dalam kasus itu Anda harus meneruskan delegat. Alasan untuk pedoman ini adalah karena delegasi dapat mengambil formulir yang berbeda di berbagai bahasa Windows Runtime (dalam hal apakah mereka mendukung satu pendaftaran klien, atau beberapa). Event, dengan model multi-subscriber, merupakan pilihan yang jauh lebih mudah diprediksi dan konsisten.
Tanda tangan untuk delegasi penanganan peristiwa harus terdiri dari dua parameter: pengirim (IInspectable), dan args (suatu tipe argumen peristiwa, misalnya RoutedEventArgs).
Perhatikan bahwa panduan ini tidak selalu berlaku jika Anda merancang API internal. Meskipun, API internal sering menjadi publik dari waktu ke waktu.
Topik terkait
Windows developer