Condividi tramite


Eventi (C++/CX)

Un tipo di Windows Runtime può dichiarare (ovvero pubblicare) eventi e codice client nello stesso componente o in altri componenti può sottoscrivere tali eventi associando metodi denominati gestori eventi all'evento. A un singolo evento possono essere associati più gestori eventi. Quando l'oggetto di pubblicazione genera l'evento, vengono richiamati tutti i gestori eventi. In questo modo, una classe di sottoscrizione può eseguire qualsiasi azione personalizzata appropriata quando l'editore genera l'evento . Un evento è un tipo delegato che specifica la firma che tutti i gestori eventi devono avere per sottoscrivere l'evento.

Utilizzo di eventi nei componenti di Windows

Molti componenti in Windows Runtime espongono eventi. Ad esempio, un oggetto LightSensor genera un evento ReadingChanged quando il sensore segnala un nuovo valore di luminescenza. Quando usi un oggetto LightSensor nel programma, puoi definire un metodo che sarà chiamato quando viene generato l'evento ReadingChanged. Il metodo può eseguire qualsiasi operazione da eseguire; l'unico requisito è che la firma deve corrispondere alla firma del delegato richiamato. Per altre informazioni su come creare un gestore eventi delegato e sottoscrivere un evento, vedere Delegati.

Creazione di eventi personalizzati

Dichiarazione

Puoi dichiarare un evento in una classe di riferimento o in un'interfaccia e puoi impostarne l'accessibilità pubblica, interna (pubblica/privata), protetta pubblica, protetta, protetta privata o privata. Quando dichiari un evento, il compilatore crea internamente un oggetto che espone due metodi di accesso: aggiunta e rimozione. Quando gli oggetti di sottoscrizione registrano i gestori eventi, l'oggetto evento li archivia in una raccolta. Quando viene generato un evento, l'oggetto evento richiama a sua volta tutti i gestori nel proprio elenco. Un evento semplice, come quello nell'esempio seguente, dispone di un archivio di backup implicito nonché di metodi impliciti della funzione di accesso add e remove . Puoi anche specificare funzioni di accesso personali nello stesso modo in cui puoi specificare funzioni di accesso get e set personalizzate in una proprietà. La classe di implementazione non è in grado di scorrere manualmente l'elenco di sottoscrittori eventi in un evento semplice.

Nell'esempio riportato di seguito viene illustrato come dichiarare e generare un evento. Nota che l'evento include un tipo di delegato e la sua dichiarazione include il simbolo "^".

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.");
        }
    };
}

Utilizzo

L'esempio riportato di seguito mostra in che modo una classe di sottoscrizione usa l'operatore += per sottoscrivere l'evento e come fornire un gestore eventi da richiamare al momento della generazione dell'evento. La funzione fornita corrisponde alla firma del delegato definito sul lato editore nello spazio dei nomi 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;
    };
}

Avviso

In genere, è preferibile utilizzare una funzione denominata, anziché una funzione lambda, per un gestore eventi a meno che non si presti molta attenzione a evitare i riferimenti circolari. Una funzione denominata acquisisce il puntatore "this" per riferimento debole, mentre una funzione lambda lo acquisisce per riferimento forte e crea un riferimento circolare. Per ulteriori informazioni, vedi Riferimenti deboli e interruzione di cicli (C++/CX).

Metodi di aggiunta e rimozione personalizzati

Internamente, un evento dispone di un metodo di aggiunta, un metodo di rimozione e un metodo di generazione. Quando il codice client sottoscrive un evento, viene chiamato il metodo di aggiunta e il delegato passato viene aggiunto all'elenco di chiamate dell'evento. La classe di pubblicazione richiama l'evento, fa in modo che venga chiamato il metodo raise() e a turno vengono richiamati i delegati nell'elenco. Un sottoscrittore può rimuovere se stesso dall'elenco di delegati, facendo così in modo che venga generato il metodo di rimozione dell'evento. Il compilatore fornisce le versioni predefinite di questi metodi se tali versioni non sono definite nel codice; queste risorse sono note come eventi semplici. In molti casi, un evento semplice è tutto ciò che occorre.

Se devi eseguire la logica personalizzata in risposta all'aggiunta o alla rimozione di sottoscrittori, puoi specificare i metodi di aggiunta, rimozione e generazione per un evento. Ad esempio, in presenza di un oggetto dispendioso che viene richiesto solo per la segnalazione di un evento, puoi rinviare la creazione dell'oggetto fino al momento in cui un client non sottoscrive effettivamente l'evento.

Nell'esempio successivo viene illustrato come aggiungere a un evento metodi di aggiunta, rimozione e generazione personalizzati.

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);
            }
        }
    };
}

Rimozione di un gestore eventi dal lato del sottoscrittore

In alcuni casi rari, potrebbe essere necessario rimuovere un gestore eventi per un evento precedentemente sottoscritto. Potresti ad esempio volerlo sostituire con un altro gestore eventi oppure potresti voler eliminare alcune risorse da esso dipendenti. Per rimuovere un gestore, devi archiviare l'oggetto EventRegistrationToken restituito dall'operazione += . A questo punto puoi utilizzare l'operatore -= sul token per rimuovere il gestore eventi. Il gestore originale potrebbe essere comunque richiamato anche dopo la rimozione. Ad esempio, una race condition può verificarsi quando l'origine evento ottiene un elenco di gestori e inizia a richiamarli. Se un gestore eventi viene rimosso mentre si verifica questo problema, l'elenco diventa obsoleto. Pertanto, se si intende rimuovere un gestore eventi, creare un flag membro. Impostarlo se l'evento viene rimosso e quindi nel gestore eventi controllare il flag e restituire immediatamente se è impostato. Nell'esempio successivo viene illustrato il modello di 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;
        }
    };
}

Osservazioni:

È possibile associare più gestori a uno stesso evento. L'origine evento chiama in modo sequenziale tutti i gestori eventi dallo stesso thread. Se un ricevitore di eventi si blocca nel metodo del gestore eventi, impedisce all'origine evento di richiamare altri gestori eventi per quell'evento.

L'ordine in cui l'origine evento richiama i gestori eventi sui ricevitori di eventi non è garantito e può variare da una chiamata all'altra.

Vedi anche

Sistema di tipi
Delegati
Riferimenti al linguaggio C++/CX
Riferimenti a spazi dei nomi