Dela via


Händelser (C++/CX)

En Windows Runtime-typ kan deklarera (det vill säga publicera) händelser och klientkod i samma komponent eller i andra komponenter kan prenumerera på dessa händelser genom att associera metoder som kallas händelsehanterare med händelsen. Flera händelsehanterare kan associeras med en enskild händelse. När publiceringsobjektet genererar händelsen anropas alla händelsehanterare. På så sätt kan en prenumererande klass utföra den anpassade åtgärd som är lämplig när utgivaren genererar händelsen. En händelse har en ombudstyp som anger signaturen som alla händelsehanterare måste ha för att kunna prenumerera på händelsen.

Använda händelser i Windows-komponenter

Många komponenter i Windows Runtime exponerar händelser. Ett LightSensor-objekt utlöser till exempel en ReadingChanged-händelse när sensorn rapporterar ett nytt luminescence-värde. När du använder ett LightSensor-objekt i programmet kan du definiera en metod som anropas när ReadingChanged-händelsen utlöses. Metoden kan göra vad du vill. det enda kravet är att dess signatur måste matcha signaturen för det ombud som anropas. Mer information om hur du skapar en delegerad händelsehanterare och prenumererar på en händelse finns i Ombud.

Skapa anpassade händelser

Deklaration

Du kan deklarera en händelse i en referensklass eller ett gränssnitt, och den kan ha offentlig, intern (offentlig/privat), offentligt skyddad, skyddad, privat skyddad eller privat tillgänglighet. När du deklarerar en händelse skapar kompilatorn internt ett objekt som exponerar två åtkomstmetoder: lägg till och ta bort. När prenumererande objekt registrerar händelsehanterare lagrar händelseobjektet dem i en samling. När en händelse utlöses anropar händelseobjektet i sin tur alla hanterare i listan. En trivial händelse , som den i följande exempel, har ett implicit säkerhetskopieringslager samt implicita add metoder och remove åtkomstmetoder. Du kan också ange dina egna accessorer på samma sätt som du kan ange anpassade getset och åtkomst till en egenskap. Implementeringsklassen kan inte manuellt bläddra igenom listan över händelseprenumeranter i en trivial händelse.

I följande exempel visas hur du deklarerar och utlöse en händelse. Observera att händelsen har en ombudstyp och deklareras med hjälp av symbolen ^.

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

Användning

I följande exempel visas hur en prenumererande klass använder operatorn += för att prenumerera på händelsen och anger en händelsehanterare som ska anropas när händelsen utlöses. Observera att funktionen som tillhandahålls matchar signaturen för ombudet som definieras på utgivarsidan i EventTest namnområdet.

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

Varning

I allmänhet är det bättre att använda en namngiven funktion, snarare än en lambda, för en händelsehanterare om du inte är noga med att undvika cirkelreferenser. En namngiven funktion fångar pekaren "this" med svag referens, medan en lambda fångar den med stark referens och skapar en cirkelreferens. Mer information finns i Svaga referenser och icke-bakåtkompatibla cykler (C++/CX).

Anpassade metoder för att lägga till och ta bort

Internt har en händelse en tilläggsmetod, en borttagningsmetod och en höjningsmetod. När klientkoden prenumererar på en händelse anropas lägg till-metoden och ombudet som skickas in läggs till i händelsens anropslista. Publiceringsklassen anropar händelsen, den gör att metoden raise() anropas och varje ombud i listan anropas i tur och ordning. En prenumerant kan ta bort sig själv från ombudslistan, vilket gör att händelsens borttagningsmetod anropas. Kompilatorn tillhandahåller standardversioner av dessa metoder om du inte definierar dem i koden. dessa kallas triviala händelser. I många fall är en trivial händelse allt som krävs.

Du kan ange anpassade metoder för att lägga till, ta bort och höja för en händelse om du måste utföra anpassad logik som svar på tillägg eller borttagning av prenumeranter. Om du till exempel har ett dyrt objekt som bara krävs för händelserapportering kan du lätt skjuta upp skapandet av objektet tills en klient faktiskt prenumererar på händelsen.

I nästa exempel visas hur du lägger till anpassade metoder för att lägga till, ta bort och höja till en händelse:

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

Ta bort en händelsehanterare från prenumerantsidan

I vissa sällsynta fall kanske du vill ta bort en händelsehanterare för en händelse som du tidigare prenumererade på. Du kanske till exempel vill ersätta den med en annan händelsehanterare eller ta bort vissa resurser som finns i den. Om du vill ta bort en hanterare måste du lagra EventRegistrationToken som returneras från åtgärden += . Du kan sedan använda operatorn -= på token för att ta bort en händelsehanterare. Den ursprungliga hanteraren kan dock fortfarande anropas även efter att den har tagits bort. Ett konkurrenstillstånd kan till exempel uppstå när händelsekällan hämtar en lista över hanterare och börjar anropa dem. Om en händelsehanterare tas bort när detta inträffar blir listan inaktuell. Så om du tänker ta bort en händelsehanterare skapar du en medlemsflagga. Ange den om händelsen tas bort och kontrollera sedan flaggan i händelsehanteraren och returnera omedelbart om den har angetts. I nästa exempel visas det grundläggande mönstret.

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

Anmärkningar

Flera hanterare kan associeras med samma händelse. Händelsekällan anropar sekventiellt alla händelsehanterare från samma tråd. Om en händelsemottagare blockerar i händelsehanterarmetoden blockerar den händelsekällan från att anropa andra händelsehanterare för den här händelsen.

Den ordning i vilken händelsekällan anropar händelsehanterare på händelsemottagare garanteras inte och kan skilja sig från anrop till anrop.

Se även

typsystem
Ombuden
C++/CX-språkreferens
referens för namnområden