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 de Windows Runtime-onderdelen en de app die hiervan gebruikmaakt, waarvan in het onderwerp Windows Runtime-onderdelen met C++/WinRT wordt uitgelegd hoe u deze bouwt.
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 lager is dan het vriespunt.
- Werk de Core-app die de runtimeklasse voor de thermometer gebruikt zodanig bij dat deze die gebeurtenis afhandelt.
Note
Zie Visual Studio-ondersteuning voor C++/WinRT voor informatie over het installeren en gebruiken van de Visual Studio-extensie (VSIX) en het NuGet-pakket (die samen projectsjablonen en ondersteuning voor builds bieden).
Belangrijk
Zie API's gebruiken met C++/WinRT en Author API's met C++/WinRT voor essentiële concepten en termen die ondersteuning bieden voor uw begrip van het gebruiken en schrijven van runtimeklassen met C++/WinRT.
ThermometerWRC en ThermometerCoreApp maken
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 procedure 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 hiervan gebruikmaakt.
ThermometerWRC bijwerken om een gebeurtenis te genereren
Werk Thermometer.idl bij zodat deze eruitziet zoals de onderstaande lijst. Zo declareert u een gebeurtenis waarvan het delegatetype EventHandler is, met als argument een zwevendekommagetal met enkele precisie.
// Thermometer.idl
namespace ThermometerWRC
{
runtimeclass Thermometer
{
Thermometer();
void AdjustTemperature(Single deltaFahrenheit);
event Windows.Foundation.EventHandler<Single> TemperatureIsBelowFreezing;
};
}
Sla het bestand op. Het project kan in de huidige staat niet volledig worden gebouwd, maar voer nu hoe dan ook 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). Een van de overbelastingen neemt een delegate die wordt geregistreerd en geeft een token terug (een winrt::event_token). De andere gebruikt een token en trekt de registratie van de gekoppelde gemachtigde in.
Open nu Thermometer.h en Thermometer.cpp, en werk de implementatie van de runtimeklasse Thermometer bij. Voeg in Thermometer.h de twee overladen functies TemperatureIsBelowFreezing toe, evenals een privégegevenslid voor gebeurtenissen voor gebruik in de implementatie van die 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 in Thermometer.cpp de twee overladen functies TemperatureIsBelowFreezing.
// 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
Zie Een geregistreerde delegate intrekken voor meer informatie over wat een automatische gebeurtenisintrekker is. U krijgt gratis een automatische gebeurtenisintrekkingsimplementatie voor uw evenement. Met andere woorden, u hoeft de overload voor de event revoker niet te implementeren — die wordt voor u verzorgd door de C++/WinRT-projectie.
De andere overloads (de overloads voor registratie en handmatige intrekking) zijn niet ingebakken in de projectie. 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 in deze implementaties wordt getoond, is een efficiënte en concurrency-/threadveilige standaardkeuze. 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.
ThermometerCoreApp bijwerken om de gebeurtenis af te handelen
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, trekt u 1 graden Fahrenheit af van de temperatuur van de thermometer. En nu verwerkt de app de gebeurtenis die wordt geactiveerd wanneer de temperatuur onder het vriespunt komt. Om aan te tonen dat de gebeurtenis wordt geactiveerd zoals verwacht, plaatst u een onderbrekingspunt in de lambda-expressie die de gebeurtenis TemperatureIsBelowFreezing afhandelt, voert u de app uit en klikt u in het venster.
Geparameteriseerde gemachtigden in een ABI
Als uw gebeurtenis toegankelijk moet zijn in een binaire interface van een toepassing (ABI), zoals tussen een onderdeel en de bijbehorende toepassing, moet uw gebeurtenis een Windows Runtime gemachtigdentype gebruiken. In het bovenstaande voorbeeld wordt het Windows::Foundation::EventHandler<T>-delegatetype van Windows Runtime gebruikt. TypedEventHandler<TSender, TResult> is een ander voorbeeld van een Windows Runtime type gemachtigde.
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 geeft u de foutmelding "T moet een WinRT-type zijn" als u die voorwaarde 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 vervolgens het project en kopieert u MyEventArgs.h en .cpp naar het project (vanuit de map Generated Files), net zoals u eerder met Thermometer.h en .cpp hebt gedaan. 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 met uw gebeurtenis hoeft door te geven, kunt u uw eigen eenvoudige Windows Runtime type gemachtigde definiëren. In het onderstaande voorbeeld ziet u een eenvoudigere versie van de runtimeklasse Thermometer . 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 in binaire bestanden), waarbij deze gebeurtenissen niet beperkt zijn tot Windows Runtime typen, kunt u nog steeds de klassesjabloon winrt::event<Delegate> gebruiken. Gebruik gewoon winrt::delegate in plaats van een daadwerkelijk Windows Runtime-gedelegeerdetype, aangezien winrt::delegate ook niet-Windows Runtime-parameters ondersteunt.
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 delegate waarop geabonneerd is, kunt u winrt::delegate<... T> zonder meer 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 de Windows Runtime is de uitstelbare gebeurtenis. Een eventhandler vraagt uitstel aan door de GetDeferral-methode van het eventargument 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-structsjabloon is een helperklasse voor het implementeren (produceren) van het uitstelpatroon in Windows Runtime. Dit is 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 implementator (producent) van de gebeurtenisbron leidt u uw klasse voor gebeurtenisargumenten af van winrt::deferrable_event_args. < deferrable_event_argsT> implementeert T::GetDeferral voor u. Er wordt ook een nieuwe helpermethode weergegeven deferrable_event_args::wait_for_deferrals, die wordt voltooid wanneer alle openstaande uitstels zijn voltooid (als er geen uitstel 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
We raden aan om events, en niet delegates, als functieparameters door te geven. De functie add 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 het feit of ze ondersteuning bieden voor één clientregistratie of meerdere). Events vormen, dankzij hun model met meerdere abonnees, een veel voorspelbaardere en consistentere optie.
De handtekening voor een gemachtigde van een gebeurtenishandler moet bestaan uit twee parameters: afzender (IInspectable) en args (een type gebeurtenisargument, 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.
Verwante onderwerpen
Windows developer