Événements (C++/CX)
Un type Windows Runtime peut déclarer (autrement dit, publier) des événements et du code client dans le même composant ou dans d’autres composants peut s’abonner à ces événements en associant des méthodes appelées gestionnaires d’événements à l’événement. Plusieurs gestionnaires d'événements peuvent être associés à un seul événement. Lorsque l'objet de publication déclenche l'événement, il entraîne l'appel de tous les gestionnaires d'événements. Ainsi, une classe d'abonnement peut effectuer l'action personnalisée appropriée lorsque l'éditeur déclenche l'événement. Un événement a un type délégué qui spécifie la signature que tous les gestionnaires d'événements doivent posséder pour s'abonner à l'événement.
Utilisation d'événements dans les composants Windows
De nombreux composants dans Windows Runtime exposent des événements. Par exemple, un objet LightSensor déclenche un événement ReadingChanged lorsque le capteur signale une nouvelle valeur de luminescence. Lorsque vous utilisez un objet LightSensor dans votre programme, vous pouvez définir une méthode qui est appelée lorsque l'événement ReadingChanged se déclenche. La méthode peut faire ce que vous voulez faire ; la seule exigence est que sa signature doit correspondre à la signature du délégué appelé. Pour plus d’informations sur la création d’un gestionnaire d’événements délégués et l’abonnement à un événement, consultez Délégués.
Création d'événements personnalisés
Déclaration
Vous pouvez déclarer un événement dans une interface ou une classe ref, et celui-ci peut avoir un accès public, interne (public/privé), protégé public, protégé, protégé privé ou privé. Lorsque vous déclarez un événement, le compilateur crée de façon interne un objet qui expose deux méthodes d'accesseur : add et remove. Lorsque des objets d'abonnement inscrivent des gestionnaires d'événements, l'objet événement les stocke dans une collection. Lorsqu'un événement est déclenché, l'objet événement appelle tous les gestionnaires de sa liste, chacun à leur tour. Un événement trivial (comme celui illustré dans l'exemple suivant) détient un magasin de stockage implicite, ainsi que des méthodes d'accesseur add
et remove
implicites. Vous pouvez également spécifier vos propres accesseurs, de la même façon que vous pouvez spécifier des accesseurs get
et set
personnalisés pour une propriété. La classe d'implémentation ne peut pas parcourir manuellement la liste d'abonnés dans un événement trivial.
L'exemple suivant montre comment déclarer et déclencher un événement : Notez que l'événement a un type délégué et est déclaré à l'aide du symbole « ^ ».
namespace EventTest
{
ref class Class1;
public delegate void SomethingHappenedEventHandler(Class1^ sender, Platform::String^ s);
public ref class Class1 sealed
{
public:
Class1(){}
event SomethingHappenedEventHandler^ SomethingHappened;
void DoSomething()
{
//Do something....
// ...then fire the event:
SomethingHappened(this, L"Something happened.");
}
};
}
Utilisation
L'exemple suivant montre comment une classe d'abonnement utilise l'opérateur +=
pour s'abonner à l'événement et fournir un gestionnaire d'événements à appeler lorsque l'événement se déclenche. Notez que la fonction qui est fournie correspond à la signature du délégué défini côté serveur de publication dans l'espace de noms EventTest
.
namespace EventClient
{
using namespace EventTest;
namespace PC = Platform::Collections; //#include <collection.h>
public ref class Subscriber sealed
{
public:
Subscriber() : eventCount(0)
{
// Instantiate the class that publishes the event.
publisher= ref new EventTest::Class1();
// Subscribe to the event and provide a handler function.
publisher->SomethingHappened +=
ref new EventTest::SomethingHappenedEventHandler(
this,
&Subscriber::MyEventHandler);
eventLog = ref new PC::Map<int, Platform::String^>();
}
void SomeMethod()
{
publisher->DoSomething();
}
void MyEventHandler(EventTest::Class1^ mc, Platform::String^ msg)
{
// Our custom action: log the event.
eventLog->Insert(eventCount, msg);
eventCount++;
}
private:
PC::Map<int, Platform::String^>^ eventLog;
int eventCount;
EventTest::Class1^ publisher;
};
}
Avertissement
En général, il est préférable d'utiliser une fonction nommée, plutôt qu'une fonction lambda, pour un gestionnaire d'événements à moins que vous ne souhaitiez particulièrement éviter les références circulaires. Une fonction nommée capture le pointeur « this » par une référence faible, tandis qu'une fonction lambda le capture par une référence forte et crée une référence circulaire. Pour plus d'informations, consultez Références faibles et cycles avec rupture (C++/CX).
Méthodes add et remove personnalisées
En interne, un événement contient une méthode add, une méthode remove et une méthode raise. Lorsque le code client s'abonne à un événement, la méthode add est appelée et le délégué qui est passé est ajouté à la liste des appels de l'événement. La classe d'édition appelle l'événement, elle provoque la méthode raise() à appeler, et chaque délégué de la liste est appelé à son tour. Un abonné peut se supprimer de la liste de délégués, ce qui provoque l'appel de la méthode de suppression de l'événement. Le compilateur fournit les versions par défaut de ces méthodes si vous ne les définissez pas dans votre code ; ils sont appelés événements triviaux. Dans de nombreux cas, un événement trivial constitue l'unique exigence.
Vous pouvez spécifier des méthodes add, remove et raise personnalisées pour un événement si vous devez exécuter une logique personnalisée en réponse à l'ajout ou la suppression d'abonnés. Par exemple, si vous avez un objet coûteux qui est uniquement requis pour l'enregistrement d'événements, vous pouvez différer la création de l'objet jusqu'à ce qu'un client s'abonne réellement à l'événement.
L'exemple suivant indique comment ajouter des méthodes add, remove et raise personnalisées à un événement :
namespace EventTest2
{
ref class Class1;
public delegate void SomethingHappenedEventHandler(Class1^ sender, Platform::String^ msg);
public ref class Class1 sealed
{
public:
Class1(){}
event SomethingHappenedEventHandler^ SomethingHappened;
void DoSomething(){/*...*/}
void MethodThatFires()
{
// Fire before doing something...
BeforeSomethingHappens(this, "Something's going to happen.");
DoSomething();
// ...then fire after doing something...
SomethingHappened(this, L"Something happened.");
}
event SomethingHappenedEventHandler^ _InternalHandler;
event SomethingHappenedEventHandler^ BeforeSomethingHappens
{
Windows::Foundation::EventRegistrationToken add(SomethingHappenedEventHandler^ handler)
{
// Add custom logic here:
//....
return _InternalHandler += handler;
}
void remove(Windows::Foundation::EventRegistrationToken token)
{
// Add custom logic here:
//....
_InternalHandler -= token;
}
void raise(Class1^ sender, Platform::String^ str)
{
// Add custom logic here:
//....
return _InternalHandler(sender, str);
}
}
};
}
Suppression d'un gestionnaire d'événements du côté abonné
Dans certains cas rares, vous pouvez supprimer un gestionnaire d'événements pour un événement auquel vous vous êtes précédemment abonné. Par exemple, vous pouvez le remplacer par un autre gestionnaire d'événements ou vous pouvez supprimer des ressources qui sont détenues par celui-ci. Pour supprimer un gestionnaire, vous devez enregistrer EventRegistrationToken qui est retourné par l'opération +=
. Vous pouvez ensuite utiliser l'opérateur -=
du jeton pour supprimer un gestionnaire d'événements. Toutefois, le gestionnaire d'origine peut être appelé même après avoir été supprimé. Par exemple, une condition de concurrence peut survenir lorsque la source d’événement obtient une liste de gestionnaires et commence à les appeler. Si un gestionnaire d’événements est supprimé alors que cela se produit, la liste devient obsolète. Par conséquent, si vous envisagez de supprimer un gestionnaire d’événements, créez un indicateur de membre. Définissez-le si l’événement est supprimé, puis dans le gestionnaire d’événements, vérifiez l’indicateur et retournez immédiatement s’il est défini. L'exemple suivant illustre le modèle de base.
namespace EventClient2
{
using namespace EventTest2;
ref class Subscriber2 sealed
{
private:
bool handlerIsActive;
Platform::String^ lastMessage;
void TestMethod()
{
Class1^ c1 = ref new Class1();
handlerIsActive = true;
Windows::Foundation::EventRegistrationToken cookie =
c1->SomethingHappened +=
ref new EventTest2::SomethingHappenedEventHandler(this, &Subscriber2::MyEventHandler);
c1->DoSomething();
// Do some other work�..then remove the event handler and set the flag.
handlerIsActive = false;
c1->SomethingHappened -= cookie;
}
void MyEventHandler(Class1^ mc, Platform::String^ msg)
{
if (!handlerIsActive)
return;
lastMessage = msg;
}
};
}
Notes
Plusieurs gestionnaires peuvent être associés au même événement. La source d'événements appelle de manière séquentielle tous les gestionnaires d'événements du même thread. Si un récepteur d'événements bloque dans la méthode de gestionnaire d'événements, il empêche la source d'événements d'appeler d'autres gestionnaires d'événements pour cet événement.
L'ordre dans lequel la source d'événements appelle les gestionnaires d'événements sur les récepteurs d'événements n'est pas garanti et peut différer d'un appel à un autre.
Voir aussi
Système de type
Délégués
Informations de référence sur le langage C++/CX
Référence aux espaces de noms