Kommentar
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Det här avsnittet bygger på Windows Runtime-komponenten och det anropande programmet som artikeln Windows Runtime components with C++/WinRT visar hur du skapar.
Här är de nya funktionerna som det här avsnittet lägger till.
- Uppdatera termometerns runtime-klass så att den utlöser en händelse när dess temperatur sjunker under fryspunkten.
- Uppdatera Core-appen som använder termometerns runtime-klass så att den hanterar händelsen.
Note
Information om hur du installerar och använder VSIX (C++/WinRT Visual Studio Extension) och NuGet-paketet (som tillsammans tillhandahåller projektmall och byggstöd) finns i Visual Studio support för C++/WinRT.
Viktigt!
Viktiga begrepp och termer som stöder din förståelse av hur du använder och skapar körningsklasser med C++/WinRT finns i Använda API:er med C++/WinRT och Redigerings-API:er med C++/WinRT.
Skapa ThermometerWRC och ThermometerCoreApp
Om du vill följa med i de uppdateringar som visas i det här avsnittet, så att du kan skapa och köra koden, är det första steget att följa genomgången i avsnittet Windows Runtime komponenter med C++/WinRT. På så sätt får du Windows Runtime-komponenten ThermometerWRC och Core-appen ThermometerCoreApp som använder den.
Uppdatera ThermometerWRC för att skapa en händelse
Uppdatera Thermometer.idl för att se ut som listan nedan. Så här deklarerar du en händelse vars delegattyp är EventHandler med ett argument av typen flyttal med enkel precision.
// Thermometer.idl
namespace ThermometerWRC
{
runtimeclass Thermometer
{
Thermometer();
void AdjustTemperature(Single deltaFahrenheit);
event Windows.Foundation.EventHandler<Single> TemperatureIsBelowFreezing;
};
}
Spara filen. Projektet går inte att bygga klart i sitt nuvarande tillstånd, men kör ändå en byggning nu för att generera uppdaterade versioner av stubbfilerna \ThermometerWRC\ThermometerWRC\Generated Files\sources\Thermometer.h och Thermometer.cpp. Inuti dessa filer kan du nu se stub-implementeringar av händelsen TemperatureIsBelowFreezing . I C++/WinRT implementeras en IDL-deklarerad händelse som en uppsättning överlagrade funktioner (ungefär som hur en egenskap implementeras som ett par överlagrade get- och set-funktioner). En överlagrad metod tar en delegat för registrering och returnerar ett tokenvärde (ett winrt::event_token). Den andra tar en token och återkallar registreringen av det associerade ombudet.
Öppna nu Thermometer.h och Thermometer.cpp, och uppdatera implementationen av Thermometer-runtime-klassen. I Thermometer.h lägger du till de två överlagrade funktionerna TemperatureIsBelowFreezing samt en privat händelsedatamedlem som används i implementeringen av dessa funktioner.
// 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;
...
};
}
...
Som du ser ovan representeras en händelse av mallen winrt::event struct, som parametriseras av en viss delegattyp (som i sig kan parametriseras av en args-typ).
I Thermometer.cppimplementerar du de två överlagrade TemperatureIsBelowFreezing-funktionerna .
// 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
Mer information om vad en automatisk händelse-återkallare är finns i Återkalla ett registrerat ombud. Du får automatisk implementering av återkallande av händelser kostnadsfritt för ditt evenemang. Med andra ord behöver du inte implementera överlagringen för händelseåterkallaren – den tillhandahålls av C++/WinRT-projektionen.
De andra överlagringarna (överlagring av registrering och manuell återkallelse) bakas inte in i projektionen. Det är för att ge dig flexibiliteten att implementera dem optimalt för ditt scenario. Att anropa event::add och event::remove på det sätt som visas i dessa implementeringar är ett effektivt och samtidighets- och trådsäkert standardval. Men om du har ett mycket stort antal händelser kanske du inte vill ha ett händelsefält för var och en, utan snarare väljer någon form av gles implementering i stället.
Du kan också se ovan att implementeringen av funktionen AdjustTemperature har uppdaterats för att höja händelsen TemperatureIsBelowFreezing om temperaturen går under fryspunkten.
Uppdatera ThermometerCoreApp för att hantera händelsen
I Projektet ThermometerCoreApp i gör du följande ändringar i App.cppkoden för att registrera en händelsehanterare och gör sedan att temperaturen hamnar under fryspunkten.
WINRT_ASSERT är en makrodefinition och expanderas till _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);
...
}
...
};
Var medveten om ändringen av metoden OnPointerPressed . Varje gång du klickar på fönstret subtraherar du 1 grad Fahrenheit från termometerns temperatur. Och nu hanterar appen händelsen som utlöses när temperaturen sjunker under fryspunkten. För att visa att händelsen utlöses som förväntat placerar du en brytpunkt i lambda-uttrycket som hanterar händelsen TemperatureIsBelowFreezing, kör appen och klicka i fönstret.
Parametriserade delegater över ett ABI
Om din händelse måste vara tillgänglig via ett binärt programgränssnitt (ABI), till exempel mellan en komponent och den app som använder den, måste den använda en delegeringstyp i Windows Runtime. Exemplet ovan använder Windows Runtime-delegattypen Windows::Foundation::EventHandler<T>. TypedEventHandler<TSender, TResult> är ett annat exempel på en Windows Runtime-delegattyp.
Typparametrarna för dessa två delegattyper måste passera ABI-gränsen, så typparametrarna måste också vara Windows Runtime-typer. Det omfattar Windows körningsklasser, körningsklasser från tredje part och primitiva typer som tal och strängar. Kompilatorn hjälper dig med felet "T måste vara WinRT-typ" om du glömmer den begränsningen.
Nedan visas ett exempel i form av kodlistor. Börja med termometerWRC - och ThermometerCoreApp-projekten som du skapade tidigare i det här avsnittet och redigera koden i dessa projekt för att se ut som koden i dessa listor.
Den här första listan är avsedd för ThermometerWRC-projektet . När du har redigerat ThermometerWRC.idl enligt nedan skapar du projektet och kopierar MyEventArgs.h sedan och .cpp till projektet (från Generated Files mappen) precis som du gjorde tidigare med Thermometer.h och .cpp. Kom ihåg att ta bort static_assert från båda filerna.
// 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);
}
}
...
Den här listan är avsedd för ThermometerCoreApp-projektet .
// 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.
});
}
...
Enkla signaler över en ABI
Om du inte behöver skicka några parametrar eller argument med din händelse, kan du definiera din egen enkla Windows Runtime-delegattyp. Exemplet nedan visar en enklare version av termometerns körningsklass. Den deklarerar en delegattyp med namnet SignalDelegate och använder sedan den för att generera en signaltyphändelse i stället för en händelse med en 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);
...
}
...
};
Parametriserade ombud, enkla signaler och återanrop i ett projekt
Om du behöver händelser som är interna för ditt Visual Studio projekt (inte mellan binärfiler), där dessa händelser inte är begränsade till Windows Runtime typer, kan du fortfarande använda klassmallen winrt::event<Delegate>. Använd bara winrt::d elegate i stället för en faktisk Windows Runtime delegattyp, eftersom winrt::d elegate också stöder parametrar som inte är Windows Runtime.
Exemplet nedan visar först en ombudssignatur som inte tar några parametrar (i huvudsak en enkel signal) och sedan en som tar en sträng.
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!");
Observera hur du kan lägga till i händelsen så många prenumererande ombud som du vill. Det finns dock vissa omkostnader som är associerade med en händelse. Om allt du behöver är ett enkelt återanrop med bara en enda prenumererande delegat, kan du använda winrt::delegate<... T> i sig självt.
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!");
Om du porterar från en C++/CX-kodbas där händelser och ombud används internt i ett projekt hjälper winrt::d elegate dig att replikera det mönstret i C++/WinRT.
Uppskjutbara händelser
Ett vanligt mönster i Windows Runtime är den uppskjutbara händelsen. En händelsehanterare tar en uppskjutning genom att anropa händelseargumentets GetDeferral-metod . Detta indikerar för händelsekällan att aktiviteter efter händelsen ska skjutas upp tills uppskjutningen har slutförts. Detta gör att en händelsehanterare kan utföra asynkrona åtgärder som svar på en händelse.
Strukturmallen winrt::deferrable_event_args är en hjälpklass för att implementera (tillhandahålla) Windows Runtimes uppskjutningsmönster. Här följer ett exempel.
// 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;
};
}
Så här använder händelsemottagaren det uppskjutbara händelsemönstret.
// 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();
});
Som implementerare (producent) av händelsekällan låter du din klass för händelseargument härledas från winrt::deferrable_event_args. < deferrable_event_argsT> implementerar T::GetDeferral åt dig. Den exponerar också en ny hjälpmetod deferrable_event_args::wait_for_deferrals, som slutförs när alla utestående uppskjutningar har slutförts (om inga uppskjutningar har tagits slutförs den omedelbart).
// 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;
}
Designriktlinjer
Vi rekommenderar att du skickar händelser, inte delegater, som funktionsparametrar. Lägg till funktionen winrt::event är det enda undantaget, eftersom du måste skicka ett ombud i så fall. Anledningen till den här riktlinjen är att ombud kan ha olika former på olika Windows Runtime språk (oavsett om de stöder en klientregistrering eller flera). Händelser, med sin modell med flera prenumeranter, utgör ett mycket mer förutsägbart och konsekvent alternativ.
Signaturen för ett händelsehanterardelegat bör bestå av två parametrar: avsändaren (IInspectable) och args (någon typ av händelseargument, till exempel RoutedEventArgs).
Observera att dessa riktlinjer inte nödvändigtvis gäller om du utformar ett internt API. Även om interna API:er ofta blir offentliga över tid.
Relaterade ämnen
Windows developer