Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
Dit onderwerp bouwt voort op het Windows Runtime-onderdeel en de verbruikte toepassing, zoals uitgelegd in het Windows Runtime-componenten met C++/WinRT onderwerp dat toont hoe u deze kunt bouwen.
Dit zijn de nieuwe functies die in dit onderwerp worden toegevoegd.
- Werk de runtimeklasse van de thermometer bij om een gebeurtenis te verhogen wanneer de temperatuur onder het vriespunt gaat.
- Werk de Core-app bij die de thermometer-runtimeklasse gebruikt, zodat deze de gebeurtenis afhandelt.
Opmerking
Voor informatie over het installeren en gebruiken van de C++/WinRT Visual Studio-extensie (VSIX) en het NuGet-pakket (dat samen projectsjabloon en build-ondersteuning biedt), raadpleegt u Visual Studio-ondersteuning voor C++/WinRT.
Belangrijk
Zie API's gebruiken met C++/WinRT en API's maken met C++/WinRTvoor essentiële concepten en termen die ondersteuning bieden bij uw inzicht in hoe u runtimeklassen met C++/WinRT kunt gebruiken en ontwerpen.
Maak ThermometerWRC en ThermometerCoreApp
Als u de updates in dit onderwerp wilt volgen, zodat u de code kunt bouwen en uitvoeren, is de eerste stap het volgen van de walkthrough in de Windows Runtime-onderdelen met C++/WinRT-onderwerp . Als u dit doet, hebt u het ThermometerWRC Windows Runtime-onderdeel en de ThermometerCoreApp Core-app die deze verbruikt.
Update ThermometerWRC- om een gebeurtenis te activeren
Werk Thermometer.idl bij zodat het lijkt op de onderstaande lijst. Dit is hoe u een gebeurtenis declareert waarvan het type gedelegeerde is EventHandler met een argument van een drijvendekommagetal met één precisie.
// Thermometer.idl
namespace ThermometerWRC
{
runtimeclass Thermometer
{
Thermometer();
void AdjustTemperature(Single deltaFahrenheit);
event Windows.Foundation.EventHandler<Single> TemperatureIsBelowFreezing;
};
}
Sla het bestand op. Het project zal in de huidige staat niet volledig gebouwd worden, maar voer toch nu een build uit om bijgewerkte versies van de \ThermometerWRC\ThermometerWRC\Generated Files\sources\Thermometer.h- en Thermometer.cpp-stub-bestanden te genereren. In deze bestanden kunt u nu stub-implementaties van de TemperatureIsBelowFreezing-gebeurtenis zien. In C++/WinRT wordt een idL-gedeclareerde gebeurtenis geïmplementeerd als een set overbelaste functies (vergelijkbaar met de manier waarop een eigenschap wordt geïmplementeerd als een paar overbelaste get- en setfuncties). Eén overbelasting accepteert een gedelegeerde om zich te registreren en retourneert een token (een winrt::event_token). De andere gebruikt een token en trekt de registratie van de gekoppelde gemachtigde in.
Open Thermometer.h en Thermometer.cpp, en werk de implementatie van de Thermometer runtime-klasse bij. Voeg in Thermometer.hde twee overbelaste TemperatureIsBelowFreezing functies toe, evenals een privé gebeurtenisgegeven dat moet worden gebruikt bij de implementatie van deze functies.
// 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;
...
};
}
...
Zoals u hierboven kunt zien, wordt een gebeurtenis vertegenwoordigd door de winrt::event struct-sjabloon, geparameteriseerd door een bepaald type gemachtigde (die zelf kan worden geparameteriseerd door een args-type).
Implementeer Thermometer.cppde twee overbelaste TemperatureIsBelowFreezing-functies .
// 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);
}
}
Opmerking
Voor details over wat een automatische gebeurtenisverwijderaar is, zie Een geregistreerde gemachtigde intrekken. U krijgt gratis de implementatie van een automatische gebeurtenisintrekking voor uw evenement. Met andere woorden, u hoeft de overloading voor de event revoker niet te implementeren—deze wordt geleverd door de C++/WinRT-projectie.
De andere overbelastingen (de registratie en handmatige intrekkingsoverbelastingen) worden niet in de projectie gebakken. Dat is om u de flexibiliteit te bieden om ze optimaal te implementeren voor uw scenario. Het aanroepen van event::add en event::remove zoals weergegeven in deze implementaties, is een efficiënte en veilige standaard bij concurrentie en threading. Maar als u een zeer groot aantal gebeurtenissen hebt, wilt u mogelijk geen gebeurtenisveld voor elk, maar in plaats daarvan een soort sparse-implementatie kiezen.
U kunt ook hierboven zien dat de implementatie van de functie AdjustTemperature is bijgewerkt om de gebeurtenis TemperatureIsBelowFreezing te verhogen als de temperatuur onder het vriespunt gaat.
Werk ThermometerCoreApp bij om de gebeurtenis te verwerken
Breng in het project ThermometerCoreApp , in App.cpp, de volgende wijzigingen aan in de code om een gebeurtenishandler te registreren en ervoor te zorgen dat de temperatuur onder het vriespunt gaat.
WINRT_ASSERT is een macrodefinitie en wordt uitgebreid naar _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);
...
}
...
};
Let op de wijziging in de methode OnPointerPressed . Telkens wanneer u op het venster klikt, u 1 graden Fahrenheit aftrekken van de temperatuur van de thermometer. Nu verwerkt de app de gebeurtenis die wordt geactiveerd wanneer de temperatuur onder het vriespunt gaat. Om te laten zien dat de gebeurtenis wordt gegenereerd zoals verwacht, plaatst u een onderbrekingspunt in de lambda-expressie die de TemperatureIsBelowFreezing gebeurtenis verwerkt, voert u de app uit en klik in het venster.
Geparameteriseerde gemachtigden in een ABI
Als uw gebeurtenis toegankelijk moet zijn via een binaire interface van een toepassing (ABI), zoals tussen een onderdeel en de consumerende toepassing, dan moet uw gebeurtenis een Windows Runtime-afgevaardigd type gebruiken. In het bovenstaande voorbeeld wordt het gedelegeerdetype Windows::Foundation::EventHandler<T> Windows Runtime gebruikt. TypedEventHandler<TSender, TResult> is een ander voorbeeld van een Windows Runtime-delegate type.
De typeparameters voor deze twee gedelegeerdentypen moeten de ABI kruisen, dus de typeparameters moeten ook Windows Runtime-typen zijn. Dit omvat Windows-runtimeklassen, runtimeklassen van derden en primitieve typen, zoals getallen en tekenreeksen. De compiler helpt u met de foutmelding "T moet een WinRT-type zijn" als u die beperking vergeet.
Hieronder ziet u een voorbeeld in de vorm van codevermeldingen. Begin met de ThermometerWRC - en ThermometerCoreApp-projecten die u eerder in dit onderwerp hebt gemaakt en bewerk de code in deze projecten om eruit te zien als de code in deze vermeldingen.
Deze eerste vermelding is voor het ThermometerWRC-project . Nadat u ThermometerWRC.idl hebt bewerkt zoals hieronder weergegeven, bouwt u het project en kopieert u MyEventArgs.h en .cpp naar het project (uit de map Generated Files), net zoals u eerder met Thermometer.h en .cpp gedaan hebt. Vergeet niet om de static_assert bestanden uit beide bestanden te verwijderen.
// 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);
}
}
...
Deze vermelding is bedoeld voor het project 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.
});
}
...
Eenvoudige signalen in een ABI
Als u geen parameters of argumenten hoeft door te geven aan uw gebeurtenis, kunt u uw eigen eenvoudige type Windows Runtime-gemachtigde definiëren. In het onderstaande voorbeeld ziet u een eenvoudigere versie van de Thermometer runtime-klasse. Het declareert een gemachtigde type met de naam SignalDelegate en gebruikt dat vervolgens om een signaaltypegebeurtenis te genereren in plaats van een gebeurtenis met een 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);
...
}
...
};
Geparameteriseerde gemachtigden, eenvoudige signalen en callbacks binnen een project
Als u gebeurtenissen nodig hebt die intern zijn voor uw Visual Studio-project (niet tussen binaire bestanden), waarbij deze gebeurtenissen niet zijn beperkt tot Windows Runtime-typen, kunt u nog steeds de winrt::event<Delegate> klassesjabloon gebruiken. Gebruik winrt::delegate in plaats van een daadwerkelijke Windows Runtime-delegate type, omdat winrt::delegate ook parameters ondersteunt die niet van het type Windows Runtime zijn.
In het onderstaande voorbeeld ziet u eerst een gedelegeerdehandtekening die geen parameters accepteert (in wezen een eenvoudig signaal) en vervolgens een handtekening die een tekenreeks gebruikt.
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!");
U ziet hoe u aan de gebeurtenis zoveel gedelegeerden kunt toevoegen als u wilt. Er is echter enige overhead gekoppeld aan een gebeurtenis. Als u alleen een eenvoudige callback nodig hebt met slechts één abonnerende gedelegeerde, kunt u winrt::delegate<... T> zelfstandig gebruiken.
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!");
Als u gegevens overzet vanuit een C++/CX-codebasis waarin gebeurtenissen en gemachtigden intern worden gebruikt binnen een project, helpt winrt::d gate u om dat patroon te repliceren in C++/WinRT.
Uitgestelde gebeurtenissen
Een veelvoorkomend patroon in Windows Runtime is de uitstelbare gebeurtenis. Een gebeurtenis-handler een uitstel door de methode getDeferral GetDeferral van het gebeurtenisargument aan te roepen. Dit geeft aan de gebeurtenisbron aan dat activiteiten na gebeurtenissen moeten worden uitgesteld totdat het uitstel is voltooid. Hierdoor kan een gebeurtenishandler asynchrone acties uitvoeren als reactie op een gebeurtenis.
De winrt::deferrable_event_args struct-sjabloon is een helperklasse voor het implementeren (produceren) van het deferralpatroon van Windows Runtime. Hier volgt een voorbeeld.
// 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;
};
}
Hier ziet u hoe de ontvanger van de gebeurtenis het uitstelbare gebeurtenispatroon gebruikt.
// 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();
});
Als implementor (producent) van de gebeurtenisbron leidt u de gebeurtenisargumentenklasse af van winrt::deferrable_event_args. deferrable_event_args<T> implementeert T::GetDeferral voor u. Er wordt ook een nieuwe helpermethode geïntroduceerd deferrable_event_args::wait_for_deferrals, die wordt voltooid wanneer alle openstaande uitstellingen zijn voltooid (als er geen uitstelling is genomen, wordt deze onmiddellijk voltooid).
// 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;
}
Ontwerprichtlijnen
U wordt aangeraden om gebeurtenissen en geen gedelegeerde als functieparameters door te geven. De functie van winrt::event is de enige uitzondering, omdat u in dat geval een gemachtigde moet doorgeven. De reden voor deze richtlijn is dat gemachtigden verschillende vormen kunnen aannemen in verschillende Windows Runtime-talen (wat betreft of ze ondersteuning bieden voor één clientregistratie of meerdere). Gebeurtenissen, die werken met een model van meerdere abonnees, bieden een veel voorspelbaardere en consistentere optie.
De handtekening voor een gebeurtenishandler moet bestaan uit twee parameters: afzender (IInspectable) en args (een gebeurtenisargumenttype, bijvoorbeeld RoutedEventArgs).
Houd er rekening mee dat deze richtlijnen niet noodzakelijkerwijs van toepassing zijn als u een interne API ontwerpt. Hoewel interne API's vaak openbaar worden in de loop van de tijd.