Partilhar via


Eventos (C++/CX)

Um tipo de Tempo de Execução do Windows pode declarar (ou seja, publicar) eventos e o código do cliente no mesmo componente ou em outros componentes pode se inscrever nesses eventos associando métodos chamados manipuladores de eventos ao evento. Vários manipuladores de eventos podem ser associados a um único evento. Quando o objeto de publicação gera o evento, ele faz com que todos os manipuladores de eventos sejam invocados. Dessa forma, uma classe de assinatura pode executar qualquer ação personalizada apropriada quando o editor gera o evento. Um evento tem um tipo de delegado que especifica a assinatura que todos os manipuladores de eventos devem ter para se inscrever no evento.

Consumindo eventos em componentes do Windows

Muitos componentes do Tempo de Execução do Windows expõem eventos. Por exemplo, um objeto LightSensor dispara um evento ReadingChanged quando o sensor relata um novo valor de luminescência. Quando você usa um objeto LightSensor em seu programa, você pode definir um método que será chamado quando o evento ReadingChanged é acionado. O método pode fazer o que você quiser que ele faça; O único requisito é que sua assinatura corresponda à assinatura do delegado invocado. Para obter mais informações sobre como criar um manipulador de eventos delegado e se inscrever em um evento, consulte Delegados.

Criação de eventos personalizados

Declaração

Você pode declarar um evento em uma classe ref ou uma interface, e ele pode ter acessibilidade pública, interna (pública/privada), pública protegida, protegida, privada ou privada. Quando você declara um evento, internamente o compilador cria um objeto que expõe dois métodos de acessador: adicionar e remover. Ao inscrever objetos registram manipuladores de eventos, o objeto de evento os armazena em uma coleção. Quando um evento é acionado, o objeto de evento invoca todos os manipuladores em sua lista por sua vez. Um evento trivial — como o do exemplo a seguir — tem um armazenamento de suporte implícito, bem como métodos implícitos add e remove de acesso. Você também pode especificar seus próprios acessadores, da mesma forma que pode especificar acessadores personalizados get em set uma propriedade. A classe de implementação não pode percorrer manualmente a lista de assinantes do evento em um evento trivial.

O exemplo a seguir mostra como declarar e disparar um evento. Observe que o evento tem um tipo de delegado e é declarado usando o símbolo "^".

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

Utilização

O exemplo a seguir mostra como uma classe de assinatura usa o += operador para se inscrever no evento e fornecer um manipulador de eventos a ser invocado quando o evento é acionado. Observe que a função fornecida corresponde à assinatura do delegado definida no lado do editor no EventTest namespace.

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

Advertência

Em geral, é melhor usar uma função nomeada, em vez de um lambda, para um manipulador de eventos, a menos que você tome muito cuidado para evitar referências circulares. Uma função nomeada captura o ponteiro "this" por referência fraca, enquanto um lambda o captura por referência forte e cria uma referência circular. Para obter mais informações, consulte Referências fracas e ciclos de quebra (C++/CX).

Métodos personalizados de adicionar e remover

Internamente, um evento tem um método add, um método remove e um método rise. Quando o código do cliente se inscreve em um evento, o método add é chamado e o delegado que é passado é adicionado à lista de invocação do evento. A classe publishing invoca o evento, faz com que o método raise() seja chamado e cada delegado na lista é invocado por vez. Um assinante pode se remover da lista de delegados, o que faz com que o método remove do evento seja chamado. O compilador fornece versões padrão desses métodos se você não defini-los em seu código; estes são conhecidos como eventos triviais. Em muitos casos, um evento trivial é tudo o que é necessário.

Você pode especificar métodos personalizados de adicionar, remover e aumentar para um evento se precisar executar lógica personalizada em resposta à adição ou remoção de assinantes. Por exemplo, se você tiver um objeto caro que só é necessário para relatórios de eventos, poderá adiar preguiçosamente a criação do objeto até que um cliente realmente se inscreva no evento.

O próximo exemplo mostra como adicionar métodos personalizados de adicionar, remover e gerar a um evento:

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

Removendo um manipulador de eventos do lado do assinante

Em alguns casos raros, você pode querer remover um manipulador de eventos para um evento que você se inscreveu anteriormente. Por exemplo, você pode querer substituí-lo por outro manipulador de eventos ou talvez queira excluir alguns recursos que são mantidos por ele. Para remover um manipulador, você deve armazenar o EventRegistrationToken retornado da += operação. Em seguida, você pode usar o -= operador no token para remover um manipulador de eventos. No entanto, o manipulador original ainda pode ser invocado mesmo depois de removido. Por exemplo, uma condição de corrida pode surgir quando a fonte do evento obtém uma lista de manipuladores e começa a invocá-los. Se um manipulador de eventos for removido enquanto isso acontece, a lista ficará desatualizada. Portanto, se você pretende remover um manipulador de eventos, crie um sinalizador de membro. Defina-o se o evento for removido e, em seguida, no manipulador de eventos, verifique o sinalizador e retorne imediatamente se estiver definido. O próximo exemplo mostra o padrão básico.

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

Observações

Vários manipuladores podem estar associados ao mesmo evento. A fonte de eventos chama sequencialmente todos os manipuladores de eventos do mesmo thread. Se um recetor de eventos bloquear dentro do método manipulador de eventos, ele impedirá que a fonte de eventos chame outros manipuladores de eventos para esse evento.

A ordem na qual a fonte de eventos invoca manipuladores de eventos em recetores de eventos não é garantida e pode diferir de chamada para chamada.

Ver também

Sistema de tipo
Delegados
Referência da linguagem C++/CX
Referência de namespaces