Sdílet prostřednictvím


Události (C++/CX)

Typ prostředí Windows Runtime může deklarovat události (tj. publikovat) události a kód klienta ve stejné komponentě nebo v jiných komponentách se může přihlásit k odběru těchto událostí přidružením metod nazývaných obslužné rutiny událostí. K jedné události může být přidruženo více obslužných rutin událostí. Když objekt publikování vyvolá událost, způsobí vyvolání všech obslužných rutin událostí. Tímto způsobem může předplacená třída provést jakoukoli vlastní akci, když vydavatel vyvolá událost. Událost má typ delegáta, který určuje podpis, který musí mít všechny obslužné rutiny událostí k odběru události.

Využívání událostí v komponentách Windows

Mnoho komponent v prostředí Windows Runtime zveřejňuje události. Například objekt LightSensor aktivuje událost ReadingChanged, když senzor hlásí novou hodnotu světelnosti. Pokud v programu použijete objekt LightSensor, můžete definovat metodu, která bude volána při spuštění události ReadingChanged. Metoda může dělat cokoli, co chcete udělat; jediným požadavkem je, aby jeho podpis odpovídal podpisu delegáta, který je vyvolán. Další informace o tom, jak vytvořit obslužnou rutinu události delegáta a přihlásit se k odběru události, naleznete v tématu Delegáti.

Vytváření vlastních událostí

Deklarace

Událost můžete deklarovat ve třídě ref nebo rozhraní a může mít veřejnou, interní (veřejnou/soukromou), veřejnou ochranu, chráněnou, soukromou ochranu nebo privátní přístupnost. Když deklarujete událost, interně kompilátor vytvoří objekt, který zveřejňuje dvě metody přístupového objektu: přidat a odebrat. Při přihlašování k odběru objektů registrují obslužné rutiny událostí, objekt události je ukládá do kolekce. Při spuštění události vyvolá objekt události všechny obslužné rutiny v seznamu. Triviální událost ( podobně jako v následujícím příkladu) má implicitní záložní úložiště a také implicitní add metody a remove metody příslušenství. Můžete také zadat vlastní přístupové objekty stejným způsobem, jakým můžete u vlastnosti zadat vlastní get objekty a set přístupové objekty. Implementační třída nemůže ručně cyklicky procházet seznam odběratelů událostí v triviální události.

Následující příklad ukazuje, jak deklarovat a aktivovat událost. Všimněte si, že událost má typ delegáta a je deklarována pomocí symbolu ^.

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

Využití

Následující příklad ukazuje, jak přihlášení k odběru třídy používá += operátor k přihlášení k odběru události a poskytnout obslužnou rutinu události, která se má vyvolat při spuštění události. Všimněte si, že zadanou funkci odpovídá podpisu delegáta definovaného na straně vydavatele EventTest v oboru názvů.

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

Upozorňující

Obecně je lepší použít pojmenovanou funkci místo lambda pro obslužnou rutinu události, pokud se nestaráte o to, abyste se vyhnuli cyklický odkaz. Pojmenovaná funkce zachycuje ukazatel "this" slabým odkazem, zatímco lambda ho zachycuje silným odkazem a vytvoří cyklický odkaz. Další informace najdete v tématu Slabé odkazy a cykly způsobující chybu (C++/CX).

Vlastní metody přidání a odebrání

Událost má interně metodu přidání, metodu remove a metodu raise. Když se kód klienta přihlásí k odběru události, volá se metoda add a delegát, který se předá do seznamu vyvolání události. Třída publikování vyvolá událost, způsobí volání metody raise() a každý delegát v seznamu je vyvolán zase. Odběratel se může odebrat ze seznamu delegátů, což způsobí, že metoda odebrání události bude volána. Kompilátor poskytuje výchozí verze těchto metod, pokud je v kódu nedefinujete; označují se jako triviální události. V mnoha případech je triviální událost vše, co je potřeba.

Pokud potřebujete provést vlastní logiku v reakci na přidání nebo odebrání odběratelů, můžete pro událost zadat vlastní metody přidání, odebrání a vyvolání. Pokud máte například nákladný objekt, který se vyžaduje pouze pro generování sestav událostí, můžete líně odložit vytvoření objektu, dokud se klient skutečně k odběru události neodhlásí.

Další příklad ukazuje, jak do události přidat vlastní přidání, odebrání a vyvolání metod:

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

Odebrání obslužné rutiny události ze strany odběratele

V některých výjimečných případech můžete chtít odebrat obslužnou rutinu události události, ke které jste se dříve přihlásili. Můžete ho například nahradit jinou obslužnou rutinou události nebo můžete chtít odstranit některé prostředky, které jsou v něm uloženy. Chcete-li odebrat obslužnou rutinu, je nutné uložit EventRegistrationToken vrácený z += operace. Potom můžete pomocí -= operátoru na tokenu odebrat obslužnou rutinu události. Původní obslužnou rutinu je však možné vyvolat i po jeho odebrání. Například může dojít k konfliktu časování, když zdroj události získá seznam obslužných rutin a začne je vyvolat. Pokud se obslužná rutina události v době, kdy k tomu dojde, odebere se seznam zastaralý. Pokud tedy chcete odebrat obslužnou rutinu události, vytvořte příznak člena. Nastavte ji, pokud je událost odebrána, a pak v obslužné rutině události zkontrolujte příznak a vraťte se okamžitě, pokud je nastavena. Následující příklad ukazuje základní vzor.

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

Poznámky

Ke stejné události může být přidruženo více obslužných rutin. Zdroj událostí postupně volá do všech obslužných rutin událostí ze stejného vlákna. Pokud příjemce události blokuje v rámci metody obslužné rutiny události, blokuje zdroj události vyvolání jiných obslužných rutin událostí pro tuto událost.

Pořadí, ve kterém zdroj událostí vyvolá obslužné rutiny událostí u příjemců událostí, není zaručeno a může se lišit od volání.

Viz také

Systém typů
Delegáti
Referenční zdroje k jazyku C++/CX
Referenční informace o oborech názvů