Aracılığıyla paylaş


C++/WinRT'de temsilcileri kullanarak olayları işleme

Bu konu, C++/WinRTkullanarak olay işleme temsilcilerini kaydetme ve iptal etme göstermektedir. Herhangi bir standart C++ işlev benzeri nesneyi kullanarak bir olayı işleyebilirsiniz.

Uyarı

C++/WinRT Visual Studio Uzantısı (VSIX) ve NuGet paketini (birlikte proje şablonu ve derleme desteği sağlayan) yükleme ve kullanma hakkında bilgi için bkz. C++/WinRTiçin Visual Studio desteği .

Olay işleyicisi eklemek için Visual Studio kullanma

Projenize olay işleyicisi eklemenin kullanışlı bir yolu, Visual Studio'da XAML Tasarımcısı kullanıcı arabirimini (UI) kullanmaktır. XAML sayfanız XAML Tasarımcısı'nda açıkken, olayını işlemek istediğiniz denetimi seçin. Bu denetimin özellik sayfasında şimşek simgesine tıklayarak bu denetim tarafından kaynaklanan tüm olayları listeleyin. Ardından, işlemek istediğiniz olaya çift tıklayın; örneğin,OnClicked.

XAML Tasarımcısı, kaynak dosyalarınıza uygun olay işleyicisi işlev prototipini (ve saplama uygulamasını) ekler ve kendi uygulamanızla değiştirmeniz için hazır olur.

Uyarı

Genellikle, olay işleyicilerinizin Midl dosyanızda (.idl ) açıklanması gerekmez. Bu nedenle, XAML Tasarımcısı Midl dosyanıza olay işleyicisi işlev prototipleri eklemez. Sadece onları .h ve .cpp dosyalarınıza ekler.

Bir etkinliği yönetmek için bir delege kaydetme

Basit bir örnek, bir düğmenin tıklama olayını işlemektir. Olayı işlemek için bir üye işlevi kaydetmek için XAML işaretlemesi kullanmak normaldir, örneğin.

// MainPage.xaml
<Button x:Name="myButton" Click="ClickHandler">Click Me</Button>
// MainPage.h
void ClickHandler(
    winrt::Windows::Foundation::IInspectable const& sender,
    winrt::Windows::UI::Xaml::RoutedEventArgs const& args);

// MainPage.cpp
void MainPage::ClickHandler(
    IInspectable const& /* sender */,
    RoutedEventArgs const& /* args */)
{
    myButton().Content(box_value(L"Clicked"));
}

Yukarıdaki kod Visual Studio'daki Boş Uygulama (C++/WinRT) projesinden alınmıştır. kod myButton(), bizim myButtonolarak adlandırdığımız Düğme'yi döndüren oluşturulmuş bir erişimci fonksiyonu çağırır. Eğer bu x:Name öğesinin 'sunu değiştirirseniz, oluşturulan erişimci işlevin adı da değişir.

Uyarı

Bu durumda, olay kaynağı (olayı oluşturan nesne) myButtonadlı Düğmesi'dir. Olay alıcısı (olayı işleyen nesne), MainPage'in bir örneğidir. Bu konunun devamında olay kaynaklarının ve olay alıcılarının yaşam ömrünü yönetme hakkında daha fazla bilgi bulabilirsiniz.

İşaretlemeyi bildirimli olarak yapmak yerine, bir olayı işlemek için bir üye işlevini kesin olarak kaydedebilirsiniz. Aşağıdaki kod örneğinde belirgin olmayabilir, ancak ButtonBase::Click çağrısının bağımsız değişkeni, RoutedEventHandler temsilcisinin bir örneğidir. Bu durumda, bir nesne ve işaretçiden üyeye işlevi alan RoutedEventHandler oluşturucu aşırı yüklemesini kullanıyoruz.

// MainPage.cpp
MainPage::MainPage()
{
    InitializeComponent();

    myButton().Click({ this, &MainPage::ClickHandler });
}

Önemli

Temsilciyi kaydederken, yukarıdaki kod örneği bu işaretçisini (geçerli nesneye işaret eden) ham bir geçirir. Geçerli nesneye güçlü veya zayıf bir başvuru oluşturmayı öğrenmek için bkz. ve üye işlevini temsilci olarak kullanıyorsanız bkz..

Statik üye işlevi kullanan bir örnek aşağıda verilmiştir; daha basit söz dizimine dikkat edin.

// MainPage.h
static void ClickHandler(
    winrt::Windows::Foundation::IInspectable const& sender,
    winrt::Windows::UI::Xaml::RoutedEventArgs const& args);

// MainPage.cpp
MainPage::MainPage()
{
    InitializeComponent();

    myButton().Click( MainPage::ClickHandler );
}
void MainPage::ClickHandler(
    IInspectable const& /* sender */,
    RoutedEventArgs const& /* args */) { ... }

RoutedEventHandler oluşturmanın başka yolları da vardır. Aşağıda, RoutedEventHandler belge konusundan alınan söz dizimi bloğu yer almaktadır (web sayfasının sağ üst köşesindeki Dil açılır menüsünden C++/WinRT seçin). Çeşitli oluşturuculara dikkat edin: biri bir lambda alır; diğeri bir serbest fonksiyon; ve bir diğeri (yukarıda kullandığımız) bir nesne ve üye işlevine işaretçi alır.

struct RoutedEventHandler : winrt::Windows::Foundation::IUnknown
{
    RoutedEventHandler(std::nullptr_t = nullptr) noexcept;
    template <typename L> RoutedEventHandler(L lambda);
    template <typename F> RoutedEventHandler(F* function);
    template <typename O, typename M> RoutedEventHandler(O* object, M method);
    /* ... other constructors ... */
    void operator()(winrt::Windows::Foundation::IInspectable const& sender,
        winrt::Windows::UI::Xaml::RoutedEventArgs const& e) const;
};

Fonksiyon çağrısı operatörünün söz dizimini görmek de faydalıdır. Temsilcinizin parametrelerinin ne olması gerektiğini belirtir. Gördüğünüz gibi, bu durumda işlev çağrısı işleci söz dizimi MainPage::ClickHandler parametrelerimizle eşleşir.

Uyarı

Belirli bir olay için, temsilcisinin ayrıntılarını ve bu temsilcinin parametrelerini bulmak için önce olayın belge konusuna gidin. Örnek olarak UIElement.KeyDown olayını ele alalım. Konuya gidin ve Dil açılır menüsünden C++/WinRT seçeneğini seçin. Konunun başındaki söz dizimi bloğunda bunu görürsünüz.

// Register
event_token KeyDown(KeyEventHandler const& handler) const;

Bu bilgiler, UIElement.KeyDown olayının (üzerinde olduğumuz konu) KeyEventHandlertüründe bir temsilciye sahip olduğunu söyler, çünkü bu olay türünde bir temsilci kaydettiğinizde geçirdiğiniz tür budur. Bu nedenle, şimdi bu KeyEventHandler temsilci türüyle ilgili konudaki bağlantıyı izleyin. Burada söz dizimi bloğu bir işlev çağrısı işleci içerir. Yukarıda da belirtildiği gibi, temsilcinizin parametrelerinin ne olması gerektiğini belirtir.

void operator()(
  winrt::Windows::Foundation::IInspectable const& sender,
  winrt::Windows::UI::Xaml::Input::KeyRoutedEventArgs const& e) const;

Gördüğünüz gibi, temsilcinin gönderen olarak IInspectable alması ve KeyRoutedEventArgs sınıfının bir örneğinin args olarak bildirilmesi gerekir.

Başka bir örnek almak için Popup.Closed olayınagöz atalım. Temsilci türü EventHandler<IInspectable>' dir. Bu nedenle, temsilciniz gönderen olarak bir IInspectable ve başka bir IInspectable (çünkü bu, EventHandlertür parametresidir) args olarak alır.

Olay işleyicinizde çok fazla iş yapmıyorsanız üye işlevi yerine lambda işlevini kullanabilirsiniz. Yine aşağıdaki kod örneğinde belirgin olmayabilir, ancak bir RoutedEventHandler temsilcisi, yukarıda ele aldığımız işlev çağrısı işlecinin söz dizimini eşleştirmesi gereken bir lambda işlevinden oluşturulmakta.

MainPage::MainPage()
{
    InitializeComponent();

    myButton().Click([this](IInspectable const& /* sender */, RoutedEventArgs const& /* args */)
    {
        myButton().Content(box_value(L"Clicked"));
    });
}

Temsilcinizi oluştururken biraz daha açık olabilirsiniz. Örneğin, bunu etrafında dolaştırmak veya birden fazla kez kullanmak istiyorsanız.

MainPage::MainPage()
{
    InitializeComponent();

    auto click_handler = [](IInspectable const& sender, RoutedEventArgs const& /* args */)
    {
        sender.as<winrt::Windows::UI::Xaml::Controls::Button>().Content(box_value(L"Clicked"));
    };
    myButton().Click(click_handler);
    AnotherButton().Click(click_handler);
}

Kayıtlı temsilciyi iptal etme

Bir temsilciyi kaydettiğinizde, genellikle size bir belirteç döndürülür. Daha sonra temsilcinizi iptal etmek için bu belirteci kullanabilirsiniz; , temsilcinin olaydan kaydının silindiği ve olayın yeniden tetiklenmesi durumunda çağrılmayacağı anlamına gelir.

Kolaylık olması açısından, yukarıdaki kod örneklerinden hiçbiri bunun nasıl yapılacağını göstermedi. Ancak bu sonraki kod örneği, belirteci yapının özel veri üyesinde depolar ve yıkıcıdaki işleyicisini iptal eder.

struct Example : ExampleT<Example>
{
    Example(winrt::Windows::UI::Xaml::Controls::Button const& button) : m_button(button)
    {
        m_token = m_button.Click([this](IInspectable const&, RoutedEventArgs const&)
        {
            // ...
        });
    }
    ~Example()
    {
        m_button.Click(m_token);
    }

private:
    winrt::Windows::UI::Xaml::Controls::Button m_button;
    winrt::event_token m_token;
};

Yukarıdaki örnekte olduğu gibi güçlü bir başvuru yerine, düğmeye zayıf bir başvuru depolayabilirsiniz (bkz. C++/WinRT'de güçlü ve zayıf başvurular).

Uyarı

Olay kaynağı olaylarını senkron şekilde yükselttiğinde, işleyicinizi devre dışı bırakabilir ve daha fazla olay almayacağınızdan emin olabilirsiniz. Ancak zaman uyumsuz olaylar için, iptal işleminden sonra bile (ve özellikle yıkıcı içinde iptal ederken), yok etme işlemi başladıktan sonra bir uçuş içi olay nesnenize ulaşabilir. Yok edilmeden önce abonelikten çıkmak için bir yer bulmak sorunu hafifletebilir veya sağlam bir çözüm için 'a bakınız: Olay işleme delegesiile işaretçisine güvenle erişme.

Alternatif olarak, bir temsilciyi kaydederken, winrt::auto_revoke (bu, winrt::auto_revoke_ttüründe bir değerdir) belirterek, bir olay iptal edici (winrt::event_revokertüründen) talep edebilirsiniz. Olay iptal edicisi, sizin için olay kaynağına (olayı oluşturan nesne) zayıf bir referans barındırır. event_revoker::revoke üye işlevini çağırarak manuel olarak iptal edebilirsiniz; ancak event revoker, kapsamın dışına çıktığında bu işlevi otomatik olarak kendi kendine çağırır. revoke işlevi, olay kaynağının hala var olup olmadığını denetler ve varsa temsilcinizi iptal eder. Bu örnekte, olay kaynağını depolamaya ve yok ediciye gerek yoktur.

struct Example : ExampleT<Example>
{
    Example(winrt::Windows::UI::Xaml::Controls::Button button)
    {
        m_event_revoker = button.Click(
            winrt::auto_revoke,
            [this](IInspectable const& /* sender */,
            RoutedEventArgs const& /* args */)
        {
            // ...
        });
    }

private:
    winrt::Windows::UI::Xaml::Controls::Button::Click_revoker m_event_revoker;
};

ButtonBase::Click olayının belge konu başlığından alınan söz dizimi bloğu aşağıdadır. Üç farklı kayıt ve iptal etme işlevini gösterir. Üçüncü aşırı yüklemeden bildirmeniz gereken olay iptal edici türünü tam olarak görebilirsiniz. Aynı türde temsilcileri hem yazmaç'e hem de event_revoker aşırı yüklemeleriyle iptal etme işlemlerine geçirebilirsiniz.

// Register
winrt::event_token Click(winrt::Windows::UI::Xaml::RoutedEventHandler const& handler) const;

// Revoke with event_token
void Click(winrt::event_token const& token) const;

// Revoke with event_revoker
Button::Click_revoker Click(winrt::auto_revoke_t,
    winrt::Windows::UI::Xaml::RoutedEventHandler const& handler) const;

Uyarı

Yukarıdaki kod örneğinde, Button::Click_revoker için winrt::event_revoker<winrt::Windows::UI::Xaml::Controls::Primitives::IButtonBase>bir tür diğer adıdır. Benzer bir desen tüm C++/WinRT olayları için geçerlidir. Her bir Windows Çalışma Zamanı olayının, bir olay iptal edici nesne döndüren bir iptal işlevi aşırı yüklemesi vardır ve iptal edicinin türü olay kaynağının bir üyesidir. Bu nedenle, başka bir örnek almak gerekirse, CoreWindow::SizeChanged olayı, CoreWindow::SizeChanged_revokertüründe bir değer döndüren bir aşırı yüklenmiş kayıt işlevine sahiptir.

Bir sayfa gezinti senaryosunda işleyicileri iptal etmeyi düşünebilirsiniz. Bir sayfaya tekrar tekrar gidip geri çıkıyorsanız, sayfadan çıktığınızda işleyicileri iptal edebilirsiniz. Alternatif olarak, aynı sayfa örneğini tekrar kullanıyorsanız, belirtecinizin değerini kontrol edin ve yalnızca henüz ayarlanmadıysa kaydedin (if (!m_token){ ... }). Üçüncü bir seçenek de sayfada bir olay iptal ediciyi veri üyesi olarak depolamaktır. Dördüncü bir seçenek, bu konunun ilerleyen bölümlerinde açıklandığı gibi, lambda işlevinizdeki bu nesnesine güçlü ya da zayıf bir başvuru yakalamaktır.

Otomatik iptal temsilciniz kaydedilemezse

Temsilci kaydederken winrt::auto_revoke belirtmeye çalışırsanız ve sonuç winrt::hresult_no_interface özel durumu oluşuyorsa, bu genellikle olay kaynağının zayıf başvuruları desteklemediği anlamına gelir. Bu, örneğin Windows.UI.Composition ad alanında sık karşılaşılan bir durumdır. Bu durumda otomatik iptal özelliğini kullanamazsınız. Etkinlik işleyicilerinizi manuel olarak iptal etmeye başvurmanız gerekecek.

Zaman uyumsuz eylemler ve işlemler için temsilci türleri

Yukarıdaki örneklerde RoutedEventHandler temsilci türü kullanılır, ancak elbette başka birçok temsilci türü vardır. Örneğin, zaman uyumsuz eylemler ve işlemler (ilerleme ile ve ilerleme olmadan) ilgili türde temsilciler bekleyen olayları ve/veya ilerleme olaylarını tamamlamış olur. Örneğin, uygulayan herhangi bir asenkron işlemin ilerleme olayı (IAsyncOperationWithProgress), türünde bir AsyncOperationProgressHandlertemsilcisi gerektirir. Aşağıda, lambda işlevi kullanarak bu tür bir temsilci yazmanın kod örneği verilmiştır. Örnekte ayrıca AsyncOperationWithProgressCompletedHandler temsilcisinin nasıl yazıldığı da gösterilmektedir .

#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Web.Syndication.h>

using namespace winrt;
using namespace Windows::Foundation;
using namespace Windows::Web::Syndication;

void ProcessFeedAsync()
{
    Uri rssFeedUri{ L"https://blogs.windows.com/feed" };
    SyndicationClient syndicationClient;

    auto async_op_with_progress = syndicationClient.RetrieveFeedAsync(rssFeedUri);

    async_op_with_progress.Progress(
        [](
            IAsyncOperationWithProgress<SyndicationFeed,
            RetrievalProgress> const& /* sender */,
            RetrievalProgress const& args)
        {
            uint32_t bytes_retrieved = args.BytesRetrieved;
            // use bytes_retrieved;
        });

    async_op_with_progress.Completed(
        [](
            IAsyncOperationWithProgress<SyndicationFeed,
            RetrievalProgress> const& sender,
            AsyncStatus const /* asyncStatus */)
        {
            SyndicationFeed syndicationFeed = sender.GetResults();
            // use syndicationFeed;
        });

    // or (but this function must then be a coroutine, and return IAsyncAction)
    // SyndicationFeed syndicationFeed{ co_await async_op_with_progress };
}

Yukarıdaki "eş yordam" açıklamasının da belirttiği gibi, asenkron eylem ve işlemlerin tamamlanmış olayları için bir temsilci kullanmak yerine, muhtemelen eş yordamları kullanmayı daha doğal bulacaksınız. Ayrıntılar ve kod örnekleri için bkz. C++/WinRT ile Eşzamanlılık ve zaman uyumsuz işlemler.

Uyarı

Zaman uyumsuz bir eylem veya işlem için birden fazla tamamlama işleyicisi uygulamak doğru değildir. Tamamlanmış olay için ya tek bir temsilciniz olabilir ya da onu co_await edebilirsiniz. her ikisine de sahipseniz, ikincisi başarısız olur.

Temsilcilerle birlikte çalışmayı tercih ederseniz, bir eş yordam yerine daha basit bir söz dizimi seçebilirsiniz.

async_op_with_progress.Completed(
    [](auto&& /*sender*/, AsyncStatus const /* args */)
{
    // ...
});

Değer döndüren temsilci türleri

Bazı temsilci türleri bizzat bir değer döndürmelidir. Bir dize döndüren ListViewItemToKeyHandlerörnektir. Bu tür bir temsilci yazma örneği aşağıda verilmiştir (lambda işlevinin bir değer döndürdüğüne dikkat edin).

using namespace winrt::Windows::UI::Xaml::Controls;

winrt::hstring f(ListView listview)
{
    return ListViewPersistenceHelper::GetRelativeScrollPosition(listview, [](IInspectable const& item)
    {
        return L"key for item goes here";
    });
}

Olay işleme temsilcisi kullanarak bu işaretçisine güvenli bir şekilde erişme

Bir olayı nesnenin üye işleviyle veya nesnenin üye işlevinin içindeki lambda işlevinin içinden işlerseniz, olay alıcısının göreli yaşam sürelerini (olayı işleyen nesne) ve olay kaynağını (olayı yükselten nesne) düşünmeniz gerekir. Daha fazla bilgi ve kod örnekleri için bkz. C++/WinRT'de güçlü ve zayıf başvurular.

Önemli API'ler