Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Cette rubrique s’appuie sur le composant Windows Runtime et l’application consommatrice, que les composants Windows Runtime avec la rubrique C++/WinRT vous montrent comment générer.
Voici les nouvelles fonctionnalités que cette rubrique ajoute.
- Mettez à jour la classe runtime du thermomètre pour déclencher un événement lorsque sa température est inférieure au gel.
- Mettez à jour l’application Core qui consomme la classe runtime du thermomètre afin qu’elle gère cet événement.
Note
Pour plus d’informations sur l’installation et l’utilisation de l’extension de Visual Studio C++/WinRT (VSIX) et du package NuGet (qui fournissent ensemble un modèle de projet et une prise en charge de build), consultez Visual Studio prise en charge de C++/WinRT.
Important
Pour connaître les concepts et termes essentiels qui prennent en charge votre compréhension de l’utilisation et de la création de classes runtime avec C++/ WinRT, consultez Consommer des API avec C++/WinRT et créer des API avec C++/WinRT.
Créer ThermometerWRC et ThermometerCoreApp
Si vous souhaitez suivre les mises à jour indiquées dans cette rubrique, afin de pouvoir générer et exécuter le code, la première étape consiste à suivre la procédure pas à pas dans les composants Windows Runtime avec la rubrique C++/WinRT. En procédant ainsi, vous disposerez du composant ThermometerWRC Windows Runtime et de l'application ThermometerCoreApp Core qui l'utilise.
Mettre à jour ThermometerWRC pour déclencher un événement
Mettez à jour Thermometer.idl pour ressembler à la liste ci-dessous. Voici comment déclarer un événement dont le type de délégué est EventHandler avec un argument de type nombre à virgule flottante en simple précision.
// Thermometer.idl
namespace ThermometerWRC
{
runtimeclass Thermometer
{
Thermometer();
void AdjustTemperature(Single deltaFahrenheit);
event Windows.Foundation.EventHandler<Single> TemperatureIsBelowFreezing;
};
}
Enregistrez le fichier. Le projet ne pourra pas être compilé jusqu’au bout dans son état actuel, mais lancez quand même une compilation maintenant afin de générer des versions mises à jour des fichiers stub \ThermometerWRC\ThermometerWRC\Generated Files\sources\Thermometer.h et Thermometer.cpp. Dans ces fichiers, vous pouvez désormais voir les implémentations stub de l’événement TemperatureIsBelowFreezing . Dans C++/WinRT, un événement déclaré IDL est implémenté en tant qu’ensemble de fonctions surchargées (similaire à la façon dont une propriété est implémentée en tant que paire de fonctions get et set surchargées). Une surcharge accepte un délégué à enregistrer et renvoie un jeton (un winrt::event_token). L’autre accepte un jeton et révoque l’inscription du délégué associé.
À présent, ouvrez Thermometer.h et Thermometer.cpp, puis mettez à jour l’implémentation de la classe d’exécution Thermometer. Dans Thermometer.h, ajoutez les deux fonctions TemperatureIsBelowFreezing surchargées, ainsi qu’un membre de données d’événement privé à utiliser dans l’implémentation de ces fonctions.
// 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;
...
};
}
...
Comme vous pouvez le voir ci-dessus, un événement est représenté par le modèle de struct winrt ::event , paramétré par un type délégué particulier (qui peut lui-même être paramétré par un type d’arguments).
Dans Thermometer.cpp, implémentez les deux fonctions TemperatureIsBelowFreezing surchargées.
// 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
Pour plus d’informations sur ce qu’est un révoqueur d’événement automatique, consultez Révoquer un délégué inscrit. Vous obtenez gratuitement l’implémentation du révoqueur d’événement automatique pour votre événement. En d’autres termes, vous n’avez pas besoin d’implémenter la surcharge pour le révoqueur d’événements, qui est fournie pour vous par la projection C++/WinRT.
Les autres surcharges (l’inscription et les surcharges de révocation manuelle) ne sont pas intégrées dans la projection. C’est pour vous donner la possibilité de les implémenter de manière optimale pour votre scénario. Le fait d’appeler event::add et event::remove, comme indiqué dans ces implémentations, constitue un choix par défaut efficace, sûr en matière de concurrence et vis-à-vis des threads. Mais si vous avez un très grand nombre d’événements, vous ne souhaiterez peut-être pas un champ d’événement pour chacun d’eux, mais plutôt opter pour un certain type d’implémentation éparse à la place.
Vous pouvez également voir ci-dessus que l’implémentation de la fonction AdjustTemperature a été mise à jour pour augmenter l’événement TemperatureIsBelowFreezing si la température passe en dessous du gel.
Mettre à jour ThermometerCoreApp pour gérer l’événement
Dans le projet ThermometerCoreApp , dans App.cpp, apportez les modifications suivantes au code pour inscrire un gestionnaire d’événements, puis provoquez la température sous le gel.
WINRT_ASSERT est une définition de macro, et elle s’étend à _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);
...
}
...
};
Tenez compte de la modification apportée à la méthode OnPointerPressed . Maintenant, chaque fois que vous cliquez dans la fenêtre, vous soustrayez 1 degré Fahrenheit de la température du thermomètre. Et maintenant, l’application gère l’événement qui est déclenché lorsque la température passe en dessous du gel. Pour démontrer que l’événement est déclenché comme prévu, placez un point d’arrêt dans l’expression lambda qui gère l’événement TemperatureIsBelowFreezing, exécutez l’application, puis cliquez à l’intérieur de la fenêtre.
Délégués paramétrés à travers une ABI
Si votre événement doit être accessible sur une interface binaire d’application (ABI) (par exemple entre un composant et son application consommatrice), votre événement doit utiliser un type de délégué Windows Runtime. L’exemple ci-dessus utilise le type délégué Windows Runtime Windows::Foundation::EventHandler<T>. TypedEventHandler<TSender, TResult> est un autre exemple de type délégué Windows Runtime.
Les paramètres de type de ces deux types délégués doivent traverser l’ABI, de sorte que les paramètres de type doivent également être Windows Runtime types. Cela inclut Windows classes runtime, les classes runtime tierces et les types primitifs tels que les nombres et les chaînes. Le compilateur vous signale l’erreur « T doit être de type WinRT » si vous oubliez cette contrainte.
Vous trouverez ci-dessous un exemple sous forme de listes de codes. Commencez par les projets ThermometerWRC et ThermometerCoreApp que vous avez créés précédemment dans cette rubrique, puis modifiez le code dans ces projets pour ressembler au code de ces listes.
Cette première liste concerne le projet ThermometerWRC . Après avoir modifié ThermometerWRC.idl comme indiqué ci-dessous, générez le projet, puis copiez-le MyEventArgs.h.cpp dans le projet (à partir du Generated Files dossier) comme vous l’avez fait précédemment avec Thermometer.h et .cpp. N’oubliez pas de supprimer le static_assert dans les deux fichiers.
// 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);
}
}
...
Cette liste concerne le projet 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.
});
}
...
Signaux simples via une ABI
Si vous n'avez pas besoin de passer de paramètres ou d'arguments avec votre événement, vous pouvez définir votre propre type de délégué simple Windows Runtime. L’exemple ci-dessous montre une version plus simple de la classe runtime Thermometer . Il déclare un type délégué nommé SignalDelegate , puis utilise celui-ci pour déclencher un événement de type signal au lieu d’un événement avec un paramètre.
// 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);
...
}
...
};
Délégués paramétrés, signaux simples et rappels au sein d’un projet
Si vous avez besoin d’événements internes à votre projet Visual Studio (pas entre les fichiers binaires), où ces événements ne sont pas limités aux types Windows Runtime, vous pouvez toujours utiliser le modèle de classe winrt ::event<Delegate>. Utilisez simplement winrt ::d elegate au lieu d’un type de délégué Windows Runtime réel, car winrt ::d elegate prend également en charge les paramètres non Windows Runtime.
L’exemple ci-dessous montre d’abord une signature de délégué qui ne prend aucun paramètre (essentiellement un signal simple), puis une signature qui prend une chaîne.
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!");
Notez comment ajouter à l’événement autant de délégués d’abonnement que vous le souhaitez. Toutefois, une surcharge est associée à un événement. Si vous avez seulement besoin d’un rappel simple avec un seul délégué abonné, vous pouvez utiliser winrt::delegate<... T> tel quel.
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!");
Si vous effectuez un portage à partir d’une base de code C++/CX où les événements et les délégués sont utilisés en interne dans un projet, winrt ::d elegate vous aidera à répliquer ce modèle en C++/WinRT.
Événements différés
Un modèle courant dans la Windows Runtime est l’événement différé. Un gestionnaire d’événements accepte un report en appelant la méthode GetDeferral de l’argument d’événement. Cela indique à la source d’événement que les activités post-événement doivent être reportées jusqu’à ce que le report soit terminé. Cela permet à un gestionnaire d’événements d’effectuer des actions asynchrones en réponse à un événement.
Le modèle de structure winrt::deferrable_event_args est une classe utilitaire permettant d’implémenter (produire) le modèle d’ajournement de Windows Runtime. Voici un exemple.
// 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;
};
}
Voici comment le destinataire de l’événement consomme le modèle d’événement différé.
// 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();
});
En tant qu’implémenteur (producteur) de la source d’événement, vous faites dériver votre classe d’arguments d’événement de winrt::deferrable_event_args. deferrable_event_args<T> implémente T::GetDeferral à votre place. Il expose également une nouvelle méthode d’assistance deferrable_event_args ::wait_for_deferrals, qui se termine lorsque tous les reports en attente ont été terminés (si aucun report n’a été effectué, alors il se termine immédiatement).
// 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;
}
Recommandations en matière de conception
Nous vous recommandons de passer des événements, et non des délégués, comme paramètres de fonction. La fonction d’ajout de winrt ::event est l’une des exceptions, car vous devez passer un délégué dans ce cas. La raison de cette recommandation est que les délégués peuvent prendre des formes différentes selon les différents langages du runtime Windows, selon qu’ils prennent en charge un seul enregistrement de client ou plusieurs. Les événements, avec leur modèle d’abonné multiple, constituent une option beaucoup plus prévisible et cohérente.
La signature d’un délégué de gestionnaire d’événements doit se composer de deux paramètres : l’expéditeur (IInspectable) et les arguments (type d’argument d’événement, par exemple RoutedEventArgs).
Notez que ces instructions ne s’appliquent pas nécessairement si vous concevez une API interne. Bien que les API internes deviennent souvent publiques au fil du temps.
Rubriques connexes
Windows developer