Win32 uygulamasında pencere öğesi sağlayıcısı uygulama (C++/WinRT)

Bu makale , IWidgetProvider arabirimini uygulayan basit bir pencere öğesi sağlayıcısı oluşturma işleminde size yol gösterir. Bu arabirimin yöntemleri, pencere öğesini tanımlayan verileri istemek veya pencere öğesi sağlayıcısının bir pencere öğesindeki kullanıcı eylemine yanıt vermesini sağlamak için pencere öğesi konağı tarafından çağrılır. Pencere öğesi sağlayıcıları tek bir pencere öğesini veya birden çok pencere öğesini destekleyebilir. Bu örnekte iki farklı pencere öğesi tanımlayacağız. Uyarlamalı Kartlar çerçevesi tarafından sağlanan biçimlendirme seçeneklerinden bazılarını gösteren bir örnek hava durumu widget'ıdır. İkinci pencere öğesi, kullanıcı pencere öğesinde görüntülenen bir düğmeye her tıkladığınızda artan bir sayaç tutarak kullanıcı eylemlerini ve özel pencere öğesi durumu özelliğini gösterir.

Basit bir hava durumu pencere öğesinin ekran görüntüsü. Pencere öğesi, hava durumuyla ilgili bazı grafik ve verilerin yanı sıra orta büyüklükteki pencere öğesinin şablonunun görüntülendiğini gösteren bazı tanılama metinlerini gösterir.

Basit bir sayma pencere öğesinin ekran görüntüsü. Pencere öğesi, artırılacak sayısal değeri ve Artım etiketli bir düğmeyi içeren bir dizenin yanı sıra küçük boyutlu pencere öğesi şablonunun görüntülendiğini gösteren bazı tanılama metinlerini gösterir.

Bu makaledeki bu örnek kod, Windows Uygulama SDK'sı Pencere Öğeleri Örneği uyarlanmıştır. C# kullanarak pencere öğesi sağlayıcısı uygulamak için bkz. Win32 uygulamasında pencere öğesi sağlayıcısı uygulama (C#).

Önkoşullar

  • Test için kullanılabilen Widget Panosu'na sahip bir Windows 11 cihazı.
  • Cihazınızda geliştirici modu etkinleştirilmiş olmalıdır. Daha fazla bilgi için bkz. Geliştiriciler için ayarlar.
  • WinUI uygulama geliştirme iş yüküyle Visual Studio 2022 veya üzeri. İsteğe bağlı açılan listeden C++ (v143) bileşenini eklediğinizden emin olun.

Yeni bir C++/WinRT win32 konsol uygulaması oluşturma

Visual Studio'da yeni bir proje oluşturun. Yeni proje oluştur iletişim kutusunda dil filtresini "C++" ve platform filtresini Windows olarak ayarlayın, ardından Windows Konsol Uygulaması (C++/WinRT) proje şablonunu seçin. Yeni projeyi "ExampleWidgetProvider" olarak adlandırın. İstendiğinde, uygulamanın hedef Windows sürümünü 1809 veya sonraki bir sürüme ayarlayın.

Windows Uygulama SDK'sı ve Windows Uygulama Kitaplığı NuGet paketlerine başvuru ekleme

Bu kılavuzda Microsoft.WindowsAppSDK sürüm 2.5.1 kullanılır. Çözüm Gezgini'da References sağ tıklayın ve NuGet paketlerini yönet... öğesini seçin. . NuGet paket yöneticisinde Browse sekmesini seçin ve "Microsoft. WindowsAppSDK". Sürüm açılır listesinden 2.5.1 sürümünü veya daha sonraki desteklenen kararlı bir sürümü seçin ve ardından Yükle düğmesine tıklayın. Geçerli destek bilgileri için bkz. Windows Uygulama SDK'sı yayın kanalları.

Bu örnek, Windows Uygulama Kitaplığı NuGet paketini de kullanır. Çözüm Gezgini'da References sağ tıklayın ve NuGet paketlerini yönet... öğesini seçin. . NuGet paket yöneticisinde Browse sekmesini seçin ve "Microsoft.Windows için arama yapın. ImplementationLibrary". Sürüm açılan listesinden en son sürümü seçin ve Yükleöğesine tıklayın.

Önceden derlenmiş başlık dosyası pch.h'ye aşağıdaki ekleme yönergelerini ekleyin.

//pch.h 
#pragma once
#include <wil/cppwinrt.h>
#include <wil/resource.h>
...
#include <winrt/Microsoft.Windows.Widgets.Providers.h>

Uyarı

WinRT üst bilgilerinden önce wil/cppwinrt.h üst bilgisini eklemeniz gerekir.

Pencere öğesi sağlayıcı uygulamasının doğru şekilde kapatılmasını yönetmek için winrt::get_module_lockadlı modül kilidinin özel bir sürümüne ihtiyacımız vardır. main.cpp dosyamızda tanımlanacak signalLocalServerShutdown yöntemini önceden bildiririz ve uygulamanın çıkışını belirten bir olay ayarlarız. Aşağıdaki kodu, diğer dahil etmelerden önce, #pragma once yönergesinin hemen altına pch.h dosyanıza ekleyin.

//pch.h
#include <stdint.h>
#include <combaseapi.h>

// In .exe local servers the class object must not contribute to the module ref count, and use
// winrt::no_module_lock, the other objects must and this is the hook into the C++ WinRT ref counting system
// that enables this.
void SignalLocalServerShutdown();

namespace winrt
{
    inline auto get_module_lock() noexcept
    {
        struct service_lock
        {
            uint32_t operator++() noexcept
            {
                return ::CoAddRefServerProcess();
            }

            uint32_t operator--() noexcept
            {
                const auto ref = ::CoReleaseServerProcess();

                if (ref == 0)
                {
                    SignalLocalServerShutdown();
                }
                return ref;
            }
        };

        return service_lock{};
    }
}


#define WINRT_CUSTOM_MODULE_LOCK

Pencere öğesi işlemlerini yönetmek için bir WidgetProvider sınıfı ekleyin.

Visual Studio'da ExampleWidgetProvider projesine sağ tıklayın ve Add->Class öğesini seçin. Sınıf Ekle iletişim kutusunda sınıfı "WidgetProvider" olarak adlandırın ve Ekledüğmesine tıklayın.

IWidgetProvider arabirimini uygulayan bir sınıf bildirme

IWidgetProvider arabirimi, pencere öğesi sağlayıcısıyla işlemleri başlatmak için pencere öğesi konağı tarafından çağrılacak yöntemleri tanımlar. WidgetProvider.h dosyasındaki boş sınıf tanımını aşağıdaki kodla değiştirin. Bu kod , IWidgetProvider arabirimini uygulayan bir yapı bildirir ve arabirim yöntemleri için prototipler bildirir.

// WidgetProvider.h
struct WidgetProvider : winrt::implements<WidgetProvider, winrt::Microsoft::Windows::Widgets::Providers::IWidgetProvider>
{
    WidgetProvider();

    /* IWidgetProvider required functions that need to be implemented */
    void CreateWidget(winrt::Microsoft::Windows::Widgets::Providers::WidgetContext widgetContext);
    void DeleteWidget(winrt::hstring const& widgetId, winrt::hstring const& customState);
    void OnActionInvoked(winrt::Microsoft::Windows::Widgets::Providers::WidgetActionInvokedArgs actionInvokedArgs);
    void OnWidgetContextChanged(winrt::Microsoft::Windows::Widgets::Providers::WidgetContextChangedArgs contextChangedArgs);
    void Activate(winrt::Microsoft::Windows::Widgets::Providers::WidgetContext widgetContext);
    void Deactivate(winrt::hstring widgetId);
    /* IWidgetProvider required functions that need to be implemented */

    
};

Ayrıca, özel bir yöntem olan UpdateWidget, sağlayıcımızdan pencere öğesi konağına güncelleştirmeler gönderecek bir yardımcı yöntem ekleyin.

// WidgetProvider.h
private: 

void UpdateWidget(CompactWidgetInfo const& localWidgetInfo);

Etkinleştirilmiş pencere öğelerini takip etmeye hazırlan

Pencere öğesi sağlayıcısı tek bir pencere öğesini veya birden çok pencere öğesini destekleyebilir. Pencere öğesi konağı, pencere öğesi sağlayıcısıyla bir işlem başlattığında, işlemle ilişkili pencere öğesini tanımlamak için bir kimlik numarası aktarır. Her pencere öğesinin, özel verileri depolamak için kullanılabilecek ilişkili bir adı ve durum değeri de vardır. Bu örnekte, sabitlenmiş her pencere öğesinin kimliğini, adını ve verilerini depolamak için basit bir yardımcı yapı bildireceğiz. Pencere öğeleri, aşağıdaki Etkinleştir ve Devre Dışı Bırak bölümünde açıklanan etkin bir durumda da olabilir ve boole değeri olan her pencere öğesi için bu durumu takip edeceğiz. Aşağıdaki tanımı WidgetProvider.h dosyasına, WidgetProvider yapısı bildiriminin üzerine ekleyin.

// WidgetProvider.h
struct CompactWidgetInfo
{
    winrt::hstring widgetId;
    winrt::hstring widgetName;
    int customState = 0;
    bool isActive = false;
};

WidgetProvider.h içindeki WidgetProvider bildiriminin içine, her girişin anahtarı olarak pencere öğesi kimliğini kullanarak etkin pencere öğelerinin listesini koruyacak bir harita üyesi ekleyin.

// WidgetProvider.h
#include <mutex>
#include <unordered_map>
struct WidgetProvider : winrt::implements<WidgetProvider, winrt::Microsoft::Windows::Widgets::Providers::IWidgetProvider>
{
...
    private:
        ...
        static std::unordered_map<winrt::hstring, CompactWidgetInfo> RunningWidgets;
        static std::mutex RunningWidgetsMutex;

        

Widget şablonu JSON dizelerini tanımlama

Bu örnekte, her pencere öğesi için JSON şablonlarını tanımlamak üzere bazı statik dizeler bildirilecektir. Kolaylık sağlamak için, bu şablonlar WidgetProvider sınıf tanımının dışında bildirilen yerel değişkenlerde depolanır. Şablonlar için genel bir depolama alanına ihtiyacınız varsa, bunlar uygulama paketinin bir parçası olarak eklenebilir: Paket Dosyalarına Erişme. Pencere öğesi şablonu JSON belgesi oluşturma hakkında bilgi için bkz. Uyarlamalı Kartlar Designer ile pencere öğesi şablonu oluşturma.

Bileşenler Panosu'nda pencere öğesiniz için görüntülenen üst bilgiyi özelleştirerek varsayılan sunuyu geçersiz kılabilirsiniz. Daha fazla bilgi için bkz. Pencere öğesi üst bilgisi alanını özelleştirme.

Uyarı

Web widget desteği Windows Uygulama SDK'sı 1.6.3'te eklendi. Pencere öğesi gövdesini Uyarlamalı Kartlar ile tanımlamak yerine URL'den sunulan HTML'yi görüntüleyebilirsiniz. İçerik URL'sini içeren uyarlamalı Kart JSON yükü sağlamaya devam edersiniz. Sağlayıcınızla web içeriği arasında kaynak engelleme ve mesajlaşma için bkz. Web widget sağlayıcıları. Bu özellikler IWidgetProvider'ı destekler; onun yerine geçmezler.

// WidgetProvider.h
const std::string weatherWidgetTemplate = R"(
{
    "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
    "type": "AdaptiveCard",
    "version": "1.0",
    "speak": "<s>The forecast for Seattle January 20 is mostly clear with a High of 51 degrees and Low of 40 degrees</s>",
    "backgroundImage": "https://raw.githubusercontent.com/microsoft/AdaptiveCards/59402c8d43f7a69a68e0b4fccc08f8a76fc98ccd/source/nodejs/adaptivecards-site/themes/adaptivecards/source/content/Mostly%20Cloudy-Background.jpg",
    "body": [
        {
            "type": "TextBlock",
            "text": "Redmond, WA",
            "size": "large",
            "isSubtle": true,
            "wrap": true
        },
        {
            "type": "TextBlock",
            "text": "Mon, Nov 4, 2019 6:21 PM",
            "spacing": "none",
            "wrap": true
        },
        {
            "type": "ColumnSet",
            "columns": [
                {
                    "type": "Column",
                    "width": "auto",
                    "items": [
                        {
                            "type": "Image",
                            "url": "https://raw.githubusercontent.com/microsoft/AdaptiveCards/59402c8d43f7a69a68e0b4fccc08f8a76fc98ccd/source/nodejs/adaptivecards-site/themes/adaptivecards/source/content/Mostly%20Cloudy-Square.png",
                            "size": "small",
                            "altText": "Mostly cloudy weather"
                        }
                    ]
                },
                {
                    "type": "Column",
                    "width": "auto",
                    "items": [
                        {
                            "type": "TextBlock",
                            "text": "46",
                            "size": "extraLarge",
                            "spacing": "none",
                            "wrap": true
                        }
                    ]
                },
                {
                    "type": "Column",
                    "width": "stretch",
                    "items": [
                        {
                            "type": "TextBlock",
                            "text": "°F",
                            "weight": "bolder",
                            "spacing": "small",
                            "wrap": true
                        }
                    ]
                },
                {
                    "type": "Column",
                    "width": "stretch",
                    "items": [
                        {
                            "type": "TextBlock",
                            "text": "Hi 50",
                            "horizontalAlignment": "left",
                            "wrap": true
                        },
                        {
                            "type": "TextBlock",
                            "text": "Lo 41",
                            "horizontalAlignment": "left",
                            "spacing": "none",
                            "wrap": true
                        }
                    ]
                }
            ]
        }
    ]
})";

const std::string countWidgetTemplate = R"(
{                                                                     
    "type": "AdaptiveCard",                                         
    "body": [                                                         
        {                                                               
            "type": "TextBlock",                                    
            "text": "You have clicked the button ${count} times"    
        },
        {
             "text":"Rendering Only if Medium",
             "type":"TextBlock",
             "$when":"${$host.widgetSize==\"medium\"}"
        },
        {
             "text":"Rendering Only if Small",
             "type":"TextBlock",
             "$when":"${$host.widgetSize==\"small\"}"
        },
        {
         "text":"Rendering Only if Large",
         "type":"TextBlock",
         "$when":"${$host.widgetSize==\"large\"}"
        }                                                                    
    ],                                                                  
    "actions": [                                                      
        {                                                               
            "type": "Action.Execute",                               
            "title": "Increment",                                   
            "verb": "inc"                                           
        }                                                               
    ],                                                                  
    "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
    "version": "1.5"                                                
})";

IWidgetProvider yöntemlerini uygulama

Sonraki birkaç bölümde IWidgetProvider arabiriminin yöntemlerini uygulayacağız. Bu yöntem uygulamalarından birkaçında çağrılan yardımcı UpdateWidget yöntemi bu makalenin devamında gösterilecektir. Arayüz yöntemlerine geçmeden önce, pencere öğesi sağlayıcısı API'lerini WidgetProvider.cpp ad alanına çekmek ve önceki adımda bildirdiğimiz haritaya erişim izni vermek için ekleme yönergelerinden sonra, aşağıdaki satırları öğesine ekleyin.

Uyarı

IWidgetProvider arabiriminin geri çağırma yöntemlerine geçirilen nesnelerin yalnızca geri çağırma içinde geçerli olacağı garanti edilir. Geri çağırma bağlamı dışındaki davranışları tanımsız olduğundan, bu nesnelere yönelik başvuruları depolamamalısınız.

// WidgetProvider.cpp
#include <optional>

namespace winrt
{
    using namespace Microsoft::Windows::Widgets::Providers;
}

std::unordered_map<winrt::hstring, CompactWidgetInfo> WidgetProvider::RunningWidgets{};
std::mutex WidgetProvider::RunningWidgetsMutex{};

Tüm sağlayıcı örnekleri bu haritayı paylaşır. Başlangıç kurtarma ve özelleştirme geri çağırmaları dahil olmak üzere her erişimi RunningWidgetsMutex ile koruyun. Mutex'i tutarken güncelleştirme için gereken durumu kopyalayın, ardından bir widget-manager çağrısının paylaşılan durum kilidini tutmaması için UpdateWidget'i çağırmadan önce mutex'i bırakın.

WidgetOluştur

Kullanıcı, uygulamanızın pencere öğelerinden birini pencere öğesi konağına sabitlediğinde, pencere öğesi konağı CreateWidget çağırır. İlk olarak, bu yöntem ilişkili pencere öğesinin kimliğini ve adını alır ve etkin pencere öğelerinin koleksiyonuna yardımcı yapımız CompactWidgetInfo'nun yeni bir örneğini ekler. Ardından, UpdateWidget yardımcı yönteminde kapsüllenen pencere öğesi için ilk şablonu ve verileri gönderiyoruz.

// WidgetProvider.cpp
void WidgetProvider::CreateWidget(winrt::WidgetContext widgetContext)
{
    auto widgetId = widgetContext.Id();
    auto widgetName = widgetContext.DefinitionId();
    CompactWidgetInfo runningWidgetInfo{ widgetId, widgetName };
    {
        std::scoped_lock lock(RunningWidgetsMutex);
        RunningWidgets[widgetId] = runningWidgetInfo;
    }
    
    // Update the widget
    UpdateWidget(runningWidgetInfo);
}

DeleteWidget

Kullanıcı uygulamanızın widget'larından birini widget konağından kaldırdığında, widget konağı DeleteWidget çağırır. Bu durumda, ilgili pencere öğesini etkin pencere öğeleri listemizden kaldıracağız, böylece bu pencere öğesi için başka güncelleştirme göndermeyeceğiz.

// WidgetProvider.cpp
void WidgetProvider::DeleteWidget(winrt::hstring const& widgetId, winrt::hstring const& customState)
{
    std::scoped_lock lock(RunningWidgetsMutex);
    RunningWidgets.erase(widgetId);
}

OnActionInvoked

Kullanıcı pencere öğesi şablonunuzda tanımladığınız bir eylemle etkileşime geçtiğinde pencere öğesi konağı OnActionInvoked öğesini çağırır. Bu örnekte kullanılan sayaç widget'ı için, widget'ın JSON şablonunda fiili değeri "inc" olan bir eylem tanımlandı. Pencere öğesi sağlayıcısı kodu, kullanıcı etkileşimine yanıt olarak hangi eylemin gerçekleştirileceğini belirlemek için bu fiili değerini kullanır.

...
    "actions": [                                                      
        {                                                               
            "type": "Action.Execute",                               
            "title": "Increment",                                   
            "verb": "inc"                                           
        }                                                               
    ], 
...

OnActionInvoked yönteminde, yönteme geçirilen WidgetActionInvokedArgs'in Fiil özelliğini kontrol ederek fiil değerini alın. Fiil "inc" ise, pencere öğesi için özel durumdaki sayıyı artıracağımızı biliyoruz. WidgetActionInvokedArgs öğesinden, güncelleştirilmekte olan pencere öğesi örneğini tanımlamak için WidgetContext nesnesini ve ardından Id özelliğini alın. Etkinleştirilmiş pencere öğeleri haritamızda belirtilen ID'ye sahip girdiyi bulun ve ardından artış sayısını depolamak için kullanılan özel durum değerini güncelleyin. Son olarak, updateWidget yardımcı işleviyle pencere öğesi içeriğini yeni değerle güncelleştirin.

// WidgetProvider.cpp
void WidgetProvider::OnActionInvoked(winrt::WidgetActionInvokedArgs actionInvokedArgs)
{
    auto verb = actionInvokedArgs.Verb();
    if (verb == L"inc")
    {
        auto widgetId = actionInvokedArgs.WidgetContext().Id();
        // If you need to use some data that was passed in after
        // Action was invoked, you can get it from the args:
        auto data = actionInvokedArgs.Data();
        std::optional<CompactWidgetInfo> widgetToUpdate;
        {
            std::scoped_lock lock(RunningWidgetsMutex);
            if (const auto iter = RunningWidgets.find(widgetId); iter != RunningWidgets.end())
            {
                iter->second.customState++;
                widgetToUpdate = iter->second;
            }
        }
        if (widgetToUpdate)
        {
            UpdateWidget(*widgetToUpdate);
        }
    }
}

Uyarlamalı Kartlar için Action.Execute söz dizimi hakkında bilgi için bkz. Action.Execute. Pencere öğeleri için etkileşim tasarlama hakkında yönergeler için bkz. Pencere öğesi etkileşimi tasarım kılavuzu.

OnWidgetContextChanged

Geçerli sürümde , OnWidgetContextChanged yalnızca kullanıcı sabitlenmiş pencere öğesinin boyutunu değiştirdiğinde çağrılır. İstenen boyuta bağlı olarak pencere öğesi konağına farklı bir JSON şablonu/verisi döndürmeyi seçebilirsiniz. Ayrıca , host.widgetSize değerini temel alan koşullu işlemeyi kullanarak kullanılabilir tüm boyutları desteklemek için JSON şablonunu tasarlayabilirsiniz. Boyut değişikliğini hesaba eklemek için yeni bir şablon veya veri göndermeniz gerekmiyorsa telemetri amacıyla OnWidgetContextChanged kullanabilirsiniz.

// WidgetProvider.cpp
void WidgetProvider::OnWidgetContextChanged(winrt::WidgetContextChangedArgs contextChangedArgs)
{
    auto widgetContext = contextChangedArgs.WidgetContext();
    auto widgetId = widgetContext.Id();
    std::optional<CompactWidgetInfo> widgetToUpdate;
    {
        std::scoped_lock lock(RunningWidgetsMutex);
        if (const auto iter = RunningWidgets.find(widgetId); iter != RunningWidgets.end())
        {
            widgetToUpdate = iter->second;
        }
    }
    if (widgetToUpdate)
    {
        UpdateWidget(*widgetToUpdate);
    }
}
    

Etkinleştirme ve Devre Dışı Bırakma

Activate yöntemi, widget sağlayıcısına widget ana bilgisayarının sağlayıcıdan güncelleştirilmiş içerik almakla ilgilendiğini bildirmek için çağrılır. Örneğin, kullanıcının şu anda widget barındırıcısını aktif bir şekilde görüntülediği anlamına gelebilir. Deactivate yöntemi, widget sağlayıcısına artık widget barındırıcısının içerik güncelleştirmeleri istemediğini bildirmek için çağrılır. Bu iki yöntem, pencere öğesi ana bilgisayarının en çok up-totarih içeriğini göstermekle ilgilendiği bir pencere tanımlar. Pencere öğesi sağlayıcıları, herhangi bir zamanda, örneğin bir push bildirimine yanıt olarak pencere öğesine güncelleme gönderebilir, ancak herhangi bir arka plan görevinde olduğu gibi, pil ömrü gibi kaynak endişeleriyle güncel içerik sağlamayı dengelemek önemlidir.

etkinleştir ve devre dışı bırak widget başına çağrılır. Bu örnek , CompactWidgetInfo yardımcı yapısındaki her pencere öğesinin etkin durumunu izler. Activate yönteminde, pencere öğesimizi güncelleştirmek için UpdateWidget yardımcı yöntemini çağırırız. Etkinleştir ve Devre Dışı Bırak arasındaki zaman penceresinin küçük olabileceğini unutmayın; bu nedenle pencere öğesinizin kod yolunu olabildiğince hızlı güncelleştirmesini sağlamanız önerilir.

void WidgetProvider::Activate(winrt::Microsoft::Windows::Widgets::Providers::WidgetContext widgetContext)
{
    auto widgetId = widgetContext.Id();

    std::optional<CompactWidgetInfo> widgetToUpdate;
    {
        std::scoped_lock lock(RunningWidgetsMutex);
        if (const auto iter = RunningWidgets.find(widgetId); iter != RunningWidgets.end())
        {
            iter->second.isActive = true;
            widgetToUpdate = iter->second;
        }
    }
    if (widgetToUpdate)
    {
        UpdateWidget(*widgetToUpdate);
    }
}

void WidgetProvider::Deactivate(winrt::hstring widgetId)
{
    std::scoped_lock lock(RunningWidgetsMutex);
    if (const auto iter = RunningWidgets.find(widgetId); iter != RunningWidgets.end())
    {
        auto& localWidgetInfo = iter->second;
        localWidgetInfo.isActive = false;
    }
}

Widget güncelle

Etkin bir pencere öğesini güncelleştirmek için UpdateWidget yardımcı yöntemini tanımlayın. Bu örnekte, yöntemine geçirilen CompactWidgetInfo yardımcı yapısındaki pencere öğesinin adını denetleyeceğiz ve ardından hangi pencere öğesinin güncelleştirildiği temelinde uygun şablonu ve veri JSON'unu ayarlayacağız. WidgetUpdateRequestOptions, güncellenmekte olan widget için şablon, veri ve özel durumu ile başlatılır. WidgetManager sınıfının bir örneğini almak için WidgetManager::GetDefault öğesini çağırın ve ardından güncelleştirilmiş pencere öğesi verilerini pencere öğesi konağına göndermek için UpdateWidget'i çağırın.

// WidgetProvider.cpp
void WidgetProvider::UpdateWidget(CompactWidgetInfo const& localWidgetInfo)
{
    winrt::WidgetUpdateRequestOptions updateOptions{ localWidgetInfo.widgetId };

    winrt::hstring templateJson;
    if (localWidgetInfo.widgetName == L"Weather_Widget")
    {
        templateJson = winrt::to_hstring(weatherWidgetTemplate);
    }
    else if (localWidgetInfo.widgetName == L"Counting_Widget")
    {
        templateJson = winrt::to_hstring(countWidgetTemplate);
    }

    winrt::hstring dataJson;
    if (localWidgetInfo.widgetName == L"Weather_Widget")
    {
        dataJson = L"{}";
    }
    else if (localWidgetInfo.widgetName == L"Counting_Widget")
    {
        dataJson = L"{ \"count\": " + winrt::to_hstring(localWidgetInfo.customState) + L" }";
    }

    updateOptions.Template(templateJson);
    updateOptions.Data(dataJson);
    updateOptions.IsPlaceholderContent(localWidgetInfo.widgetName == L"Weather_Widget");
    // You can store some custom state in the widget service that you will be able to query at any time.
    updateOptions.CustomState(winrt::to_hstring(localWidgetInfo.customState));
    winrt::WidgetManager::GetDefault().UpdateWidget(updateOptions);
}

İçerik henüz kişiselleştirilmemiş veya eyleme dönüştürülebilir olmadığında WidgetUpdateRequestOptions.IsPlaceholderContent özelliğini true olarak ayarlayın. Örneğin, bu kılavuzdaki sahte hava durumu pencere öğesi, kullanıcının seçtiği konum yerine sabit bir konum görüntüler. Ana makine, bu örnek hava durumu pencere öğesi yerine özelleştirilmiş içeriğe sahip pencere öğelerine öncelik verebilir. Kişiselleştirilmiş içerik sağladığınızda false özelliğini olarak ayarlayın. Bu özellik Windows Uygulama SDK'sı 1.6.6'ya eklendi.

Uygulama başlangıcında etkin pencere öğelerinin listesini başlatın.

Pencere öğesi sağlayıcımız ilk başlatıldığında, sağlayıcımızın şu anda hizmet vermekte olduğu çalışan pencere öğeleri olup olmadığını WidgetManager'a sormak iyi bir fikirdir. Bilgisayarın yeniden başlatılması veya sağlayıcının çökmesi durumunda uygulamayı önceki durumuna geri getirmeye yardımcı olacaktır. Uygulamanın varsayılan pencere öğesi yöneticisi örneğini almak için WidgetManager::GetDefault öğesini çağırın. Ardından, bir dizi WidgetInfo nesnesi döndüren GetWidgetInfosçağrısını yapın. Pencere öğesi kimliklerini, adlarını ve özel durumunu CompactWidgetInfo yardımcı yapısına kopyalayın ve RunningWidgets üye değişkenine kaydedin. Aşağıdaki kodu WidgetProvider sınıfının oluşturucusuna yapıştırın.

Kaydedilen durum dönüştürmesi için #include <charconv> ve #include <system_error> öğesini WidgetProvider.cpp dosyasına ekleyin.

// WidgetProvider.cpp
WidgetProvider::WidgetProvider()
{
    auto runningWidgets = winrt::WidgetManager::GetDefault().GetWidgetInfos();
    for (auto widgetInfo : runningWidgets )
    {
        auto widgetContext = widgetInfo.WidgetContext();
        auto widgetId = widgetContext.Id();
        auto widgetName = widgetContext.DefinitionId();
        auto customState = widgetInfo.CustomState();
        CompactWidgetInfo runningWidgetInfo{ widgetId, widgetName };
        if (!customState.empty())
        {
            auto savedState = winrt::to_string(customState);
            auto [end, error] = std::from_chars(
                savedState.data(), savedState.data() + savedState.size(),
                runningWidgetInfo.customState);
            if (error != std::errc{} || end != savedState.data() + savedState.size())
            {
                runningWidgetInfo.customState = 0;
                OutputDebugStringW(L"Invalid saved widget count; resetting to 0.\n");
            }
        }
        std::scoped_lock lock(RunningWidgetsMutex);
        RunningWidgets.try_emplace(widgetId, runningWidgetInfo);
    }
}

Talep edildiğinde WidgetProvider örneği oluşturacak bir sınıf fabrikasını kaydedin.

WidgetProvider sınıfını tanımlayan üst bilgiyi uygulama main.cpp dosyanızın üst kısmındaki eklemelere ekleyin.

// main.cpp
...
#include "WidgetProvider.h"

Uygulamamızın çıkışını tetikleyecek olayı ve olayı ayarlayacak SignalLocalServerShutdown işlevini bildirin. Aşağıdaki kodu main.cpp yapıştırın.

// main.cpp
wil::unique_event g_shudownEvent(wil::EventOptions::None);

void SignalLocalServerShutdown()
{
    g_shudownEvent.SetEvent();
}

Ardından, COM etkinleştirmesi için pencere öğesi sağlayıcınızı tanımlamak için kullanılacak bir CLSID oluşturmanız gerekir. Tools-> GUID oluştur'a giderek Visual Studio'da GUID oluşturun. "static const GUID =" seçeneğini seçin ve Kopyala'a tıklayın, ardından bunu main.cpp'ye yapıştırın. GUID tanımını aşağıdaki C++/WinRT söz dizimi ile güncelleştirin ve GUID değişken adını widget_provider_clsid ayarlayın. Daha sonra uygulamanızı paketlerken bu biçime ihtiyacınız olacağı için GUID'nin açıklamalı sürümünü bırakın.

// main.cpp
...
// {80F4CB41-5758-4493-9180-4FB8D480E3F5}
static constexpr GUID widget_provider_clsid
{
    0x80f4cb41, 0x5758, 0x4493, { 0x91, 0x80, 0x4f, 0xb8, 0xd4, 0x80, 0xe3, 0xf5 }
};

Aşağıdaki sınıf fabrikası tanımını main.cpp'a ekleyin. Fabrika winrt::no_module_lock kullanır ve sağlayıcı örneğini tutmaz, bu nedenle fabrikanın kaydedilmesi sağlayıcının modül kilidinin tutulmasına neden olmaz. Her etkinleştirme bir sağlayıcı oluşturur ve COM tarafından istenen arabirimi döndürür. Fabrika muteksi oluşturmayı serileştirir; RunningWidgetsMutex, eş zamanlı oluşturuculardan ve geri çağırmalardan haritayı ayrı olarak korur. İstisnalar COM sınırındaki HRESULT'lere çevrilir. CoWaitForMultipleObjects, uygulama çıkmadan önce kapatma olayını bekler.

// main.cpp
template <typename T>
struct WidgetProviderClassFactory : winrt::implements<WidgetProviderClassFactory<T>, IClassFactory, winrt::no_module_lock>
{
    STDMETHODIMP CreateInstance(
        ::IUnknown* outer,
        GUID const& iid,
        void** result) noexcept final
    {
        if (!result)
        {
            return E_POINTER;
        }
        *result = nullptr;

        if (outer)
        {
            return CLASS_E_NOAGGREGATION;
        }

        try
        {
            std::scoped_lock lock(m_creationMutex);
            return winrt::make<T>().as(iid, result);
        }
        catch (...)
        {
            return winrt::to_hresult();
        }
    }

    STDMETHODIMP LockServer(BOOL) noexcept final
    {
        return S_OK;
    }

private:
    std::mutex m_creationMutex;
};

int main()
{
    winrt::init_apartment();
    wil::unique_com_class_object_cookie widgetProviderFactory;
    auto factory = winrt::make<WidgetProviderClassFactory<WidgetProvider>>();

    winrt::check_hresult(CoRegisterClassObject(
        widget_provider_clsid,
        factory.get(),
        CLSCTX_LOCAL_SERVER,
        REGCLS_MULTIPLEUSE,
        widgetProviderFactory.put()));

    DWORD index{};
    HANDLE events[] = { g_shudownEvent.get() };
    winrt::check_hresult(CoWaitForMultipleObjects(CWMO_DISPATCH_CALLS | CWMO_DISPATCH_WINDOW_MESSAGES,
        INFINITE,
        static_cast<ULONG>(std::size(events)), events, &index));

    return 0;
}

Ana bilgisayar, pencere öğeleri sabitlenmiş durumdayken sağlayıcınızı etkinleştirebilir ve serbest bırakabilir. Sağlayıcı başlatıldığında kaydedilen durumu geri yükleyin ve geri çağırmalar eşzamanlı olarak çalıştırılırsa paylaşılan pencere öğesi durumuna erişimi eşitleyin. Daha fazla bilgi için bkz. IWidgetProvider açıklamaları.

Pencere öğesi sağlayıcı uygulamanızı paketleyin

Bu izlenecek yol, bir MSIX paket bildirimi aracılığıyla pencere öğesi sağlayıcısını kaydeder. Aşağıdaki adımlarda uygulamanızı paketleyin ve pencere öğesi sağlayıcısı olarak Windows’a kaydedin. Örnek, Windows Uygulama SDK'sı çalışma zamanının çerçeveye bağımlı dağıtımını kullanır. Bunun yerine bağımsız dağıtımı seçerseniz, Windows Application Packaging Project kullanırken her iki projenin ayarları da dahil olmak üzere bağımsız uygulamaları dağıtma bölümünü izleyin.

MSIX paketleme projesi oluşturma

Çözüm Gezgini'da çözümünüze sağ tıklayın ve Add-Yeni Project... öğesini seçin. . Yeni bir project iletişim kutusunda "Windows Uygulama Paketleme Project" şablonunu seçin ve İleri tıklayın. Proje adını "ExampleWidgetProviderPackage" olarak ayarlayın ve Oluştur'a tıklayın. İstendiğinde hedef sürümü 1809 veya sonraki bir sürüme ayarlayın ve Tamam'a tıklayın. Ardından ExampleWidgetProviderPackage projesine sağ tıklayın ve > öğesini seçin. ExampleWidgetProvider projesini seçin ve Tamam'a tıklayın.

Paketleme projesine Windows Uygulama SDK'sı paket referansı ekleyin

MSIX paketleme projesine Windows Uygulama SDK'sı nuget paketine bir başvuru eklemeniz gerekir. Çözüm Gezgini'da ExampleWidgetProviderPackage projesine çift tıklayarak ExampleWidgetProviderPackage.wapproj dosyasını açın. aşağıdaki xml dosyasını Project öğesinin içine ekleyin.

<!--ExampleWidgetProviderPackage.wapproj-->
<ItemGroup>
    <PackageReference Include="Microsoft.WindowsAppSDK" Version="2.5.1">
        <IncludeAssets>build</IncludeAssets>
    </PackageReference>  
</ItemGroup>

Uyarı

Kod parçacığı, pencere öğesi API'lerini tanıtan asgari sürümü değil, 2.5.1 sürümünü kullanır. Önceki adımda desteklenen daha sonraki bir kararlı sürümü seçtiyseniz, Sürüm değerini bu sürümle eşleşecek şekilde güncelleştirin.

Paket bildirimini güncelleştirme

Çözüm Gezgini'da dosyasına sağ tıklayın ve manifest xml dosyasını açmak için Kodu Görüntüle öğesini seçin. Ardından, kullanacağımız uygulama paketi uzantıları için bazı ad alanı bildirimleri eklemeniz gerekir. Üst düzey Package öğesine aşağıdaki ad alanı tanımlarını ekleyin.

<!-- Package.appmanifest -->
<Package
  ...
  xmlns:uap3="http://schemas.microsoft.com/appx/manifest/uap/windows10/3"
  xmlns:com="http://schemas.microsoft.com/appx/manifest/com/windows10"

Application öğesinin içinde Extensions adlı yeni bir boş öğe oluşturun. Bunun uap:VisualElementskapanış etiketinden sonra geldiğinden emin olun.

<!-- Package.appxmanifest -->
<Application>
...
    <Extensions>

    </Extensions>
</Application>

Eklememiz gereken ilk uzantı ComServer uzantısıdır. Bu işlem yürütülebilir dosyanın giriş noktasını işletim sistemine kaydeder. Bu uzantı, bir kayıt defteri anahtarı ayarlayarak COM sunucusunu kaydetmenin paketlenmiş uygulama eşdeğeridir ve pencere öğesi sağlayıcılarına özgü değildir. Aşağıdaki com:Extension öğesini Extensions öğesinin alt öğesi olarak ekleyin. com:Class öğesinin Id özniteliğindeki GUID değerini önceki adımda oluşturduğunuz GUID ile değiştirin.

<!-- Package.appxmanifest -->
<Extensions>
    <com:Extension Category="windows.comServer">
        <com:ComServer>
            <com:ExeServer Executable="ExampleWidgetProvider\ExampleWidgetProvider.exe" DisplayName="ExampleWidgetProvider">
                <com:Class Id="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" DisplayName="ExampleWidgetProvider" />
            </com:ExeServer>
        </com:ComServer>
    </com:Extension>
</Extensions>

Ardından, uygulamayı pencere öğesi sağlayıcısı olarak kaydeden uzantıyı ekleyin. uap3:Extension öğesini aşağıdaki kod parçacığına Extensions öğesinin alt öğesi olarak yapıştırın. COM öğesinin ClassId özniteliğini önceki adımlarda kullandığınız GUID ile değiştirdiğinizden emin olun.

<!-- Package.appxmanifest -->
<Extensions>
    ...
    <uap3:Extension Category="windows.appExtension">
        <uap3:AppExtension Name="com.microsoft.windows.widgets" DisplayName="WidgetTestApp" Id="ContosoWidgetApp" PublicFolder="Public">
            <uap3:Properties>
                <WidgetProvider>
                    <ProviderIcons>
                        <Icon Path="Images\StoreLogo.png" />
                    </ProviderIcons>
                    <Activation>
                        <!-- Apps exports COM interface which implements IWidgetProvider -->
                        <CreateInstance ClassId="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" />
                    </Activation>

                    <TrustedPackageFamilyNames>
                        <TrustedPackageFamilyName>Microsoft.MicrosoftEdge.Stable_8wekyb3d8bbwe</TrustedPackageFamilyName>
                    </TrustedPackageFamilyNames>

                    <Definitions>
                        <Definition Id="Weather_Widget"
                            DisplayName="Weather Widget"
                            Description="Weather Widget Description"
                            AllowMultiple="true">
                            <Capabilities>
                                <Capability>
                                    <Size Name="small" />
                                </Capability>
                                <Capability>
                                    <Size Name="medium" />
                                </Capability>
                                <Capability>
                                    <Size Name="large" />
                                </Capability>
                            </Capabilities>
                            <ThemeResources>
                                <Icons>
                                    <Icon Path="ProviderAssets\Weather_Icon.png" />
                                </Icons>
                                <Screenshots>
                                    <Screenshot Path="ProviderAssets\Weather_Screenshot.png" DisplayAltText="For accessibility" />
                                </Screenshots>
                                <!-- DarkMode and LightMode are optional -->
                                <DarkMode />
                                <LightMode />
                            </ThemeResources>
                        </Definition>
                        <Definition Id="Counting_Widget"
                                DisplayName="Microsoft Counting Widget"
                                Description="Couting Widget Description">
                            <Capabilities>
                                <Capability>
                                    <Size Name="small" />
                                </Capability>
                            </Capabilities>
                            <ThemeResources>
                                <Icons>
                                    <Icon Path="ProviderAssets\Counting_Icon.png" />
                                </Icons>
                                <Screenshots>
                                    <Screenshot Path="ProviderAssets\Counting_Screenshot.png" DisplayAltText="For accessibility" />
                                </Screenshots>
                                <!-- DarkMode and LightMode are optional -->
                                <DarkMode>

                                </DarkMode>
                                <LightMode />
                            </ThemeResources>
                        </Definition>
                    </Definitions>
                </WidgetProvider>
            </uap3:Properties>
        </uap3:AppExtension>
    </uap3:Extension>
</Extensions>

Bu öğelerin tümüne ilişkin ayrıntılı açıklamalar ve biçim bilgileri için Pencere Öğesi Sağlayıcı Paketi Manifesto XML Biçimi'ne bakın.

Paketleme projenize simgeler ve diğer görüntüler ekleme

Çözüm Gezgini'da ExampleWidgetProviderPackage öğesine sağ tıklayın ve Add->Yeni Klasör öğesini seçin. Bu klasöre, önceki adımda Package.appxmanifest'de kullanılan ad olan ProviderAssets adını verin. Pencere öğelerimiz için Simgeleri ve Ekran Görüntüleri burada depolayacağız. İstediğiniz Simgeleri ve Ekran Görüntülerini ekledikten sonra, görüntü adlarının Path=ProviderAssets\ sonra gelen adlarla eşleştiğinden emin olun; aksi takdirde pencere öğeleri pencere öğesi ana bilgisayarında gösterilmez.

Ekran görüntülerinin tasarım gereksinimleri ve yerelleştirilmiş ekran görüntüleri için adlandırma kuralları hakkında bilgi için bkz. Pencere öğesi seçiciyle entegrasyon.

Widget sağlayıcınızı test etme

Geliştirme makinenizle uyumlu olan mimariyi, örneğin "x64", Çözüm Platformları açılır listesinden seçtiğinizden emin olun. Çözüm Gezgini'da çözümünüze sağ tıklayın ve Çözümü Derle'yi seçin, ardından ExampleWidgetProviderPackage öğesine sağ tıklayın ve Dağıt'ı seçin. Windows tuşu + W ile Widget panosunu açın ve panonuzun sürümüne bağlı olarak Pencere öğesi ekle veya + düğmesi olarak gösterilen pencere öğesi seçiciyi açın. Hava Durumu Widget'ını bulun ve Microsoft Counting Widget'ını panoya sabitleyin ve işlevlerini test edin.

Widget sağlayıcınızda hata ayıklama

Pencere öğelerinizi sabitledikten sonra Pencere Öğesi Platformu, pencere öğesi hakkında ilgili bilgileri almak ve göndermek için pencere öğesi sağlayıcısı uygulamanızı başlatır. Çalışan pencere öğesinde hata ayıklamak için, çalışan pencere öğesi sağlayıcısı uygulamasına bir hata ayıklayıcı ekleyebilir veya Visual Studio pencere öğesi sağlayıcısı işlemi başlatıldıktan sonra hata ayıklamayı otomatik olarak başlatacak şekilde ayarlayabilirsiniz.

Çalışan işleme eklemek için:

  1. Visual Studio'da Debug -> İşleme bağlan öğesine tıklayın.
  2. İşlemleri filtreleyin ve istediğiniz pencere öğesi sağlayıcısı uygulamasını bulun.
  3. Hata ayıklayıcısını ekleyin.

Hata ayıklayıcıyı başlangıçta başlatıldığında işleme otomatik olarak eklemek için:

  1. Visual Studio Debug -> Diğer Hata Ayıklama Hedefleri -> Yüklü Uygulama Paketinde Hata Ayıklama öğesine tıklayın.
  2. Paketleri filtreleyin ve istediğiniz pencere öğesi sağlayıcısı paketini bulun.
  3. Bunu seçin ve "Başlatmayın, ancak başlatıldığında kodumu hata ayıklayın" diyen kutuyu işaretleyin.
  4. Ekle'e tıklayın.

Konsol uygulamanızı Windows uygulamasına dönüştürme

Bu kılavuzda oluşturulan konsol uygulamasını bir Windows uygulamasına dönüştürmek için:

  1. Çözüm Gezgini'da ExampleWidgetProvider projesine sağ tıklayın ve Properties öğesini seçin. Linker -> System adresine gidin ve SubSystem 'Konsol'dan "Windows" olarak değiştirin. Bu, .vcxproj dosyasının <Link> bölümü içerisine <SubSystem>Windows</SubSystem> eklenerek yapılabilir.
  2. main.cpp'de int main() ile int WINAPI wWinMain(_In_ HINSTANCE /*hInstance*/, _In_opt_ HINSTANCE /*hPrevInstance*/, _In_ PWSTR pCmdLine, _In_ int /*nCmdShow*/)'i değiştirin.

Ekran görüntüsü, çıktısı türü Windows Uygulaması olarak ayarlanmış C++ widget sağlayıcısı proje özelliklerini gösteriyor

Widget'i yayımlama

Pencere öğesinizi geliştirip test ettikten sonra, kullanıcıların pencere öğelerinizi cihazlarına yükleyebilmesi için uygulamanızı Microsoft Store yayımlayabilirsiniz. Bir uygulamayı yayımlamaya yönelik adım adım yönergeler için bkz. Uyunu Microsoft Store yayımlama.

Widget Mağaza Koleksiyonu

Uygulamanız Microsoft Store yayımlandıktan sonra, uygulamanızın kullanıcıların Windows Pencere Öğeleri özelliğine sahip uygulamaları bulmasına yardımcı olan Pencere Öğeleri Mağaza Koleksiyonu'na eklenmesini isteyebilirsiniz. İsteğinizi göndermek için bkz. Mağaza Koleksiyonueklemek için Pencere Öğesi bilgilerinizi gönderme.

Microsoft Store'un, Windows Widget'lar içeren uygulamaları keşfetmeye olanak tanıyan pencere öğesi koleksiyonunu gösteren ekran görüntüsü

Pencere öğesi özelleştirmesini uygulamak

Windows Uygulama SDK'sı 1.4 sürümünden itibaren pencere öğeleri kullanıcı özelleştirmesini destekleyebilir. Bu özellik uygulandığında, "Özelleştir pencere öğesi" seçeneği, "Pencere öğesini kaldır" seçeneğinin üstünde yer alan üç nokta menüsüne eklenir.

Özelleştirme iletişim kutusunun görüntülendiği bir pencere öğesini gösteren ekran görüntüsü.

Aşağıdaki adımlarda pencere öğesi özelleştirme işlemi özetlenmiştir.

  1. Normal işlemde pencere öğesi sağlayıcısı, normal pencere öğesi deneyimi için görsel ve veri JSON şablonlarıyla pencere öğesi konağından gelen isteklere yanıt verir.
  2. Kullanıcı üç noktalı menüdeki Özelleştir pencere öğesi düğmesine tıklar.
  3. Pencere öğesi, kullanıcının pencere öğesi özelleştirme deneyimini istediği belirtmek için pencere öğesi sağlayıcısında OnCustomizationRequested olayını tetikler.
  4. Pencere öğesi sağlayıcısı, pencere öğesinin özelleştirme modunda olduğunu belirtmek için bir iç bayrak ayarlar. Özelleştirme modundayken, pencere öğesi sağlayıcısı normal pencere öğesi kullanıcı arabirimi yerine pencere öğesi özelleştirme kullanıcı arabirimi için JSON şablonlarını gönderir.
  5. Özelleştirme modundayken, kullanıcı özelleştirme kullanıcı arabirimiyle etkileşim kurarken pencere öğesi sağlayıcısı OnActionInvoked olaylarını alır ve kullanıcının eylemlerine göre iç yapılandırmasını ve davranışını ayarlar.
  6. OnActionInvoked olayıyla ilişkili eylem uygulama tanımlı "özelleştirmeden çık" eylemi olduğunda, pencere öğesi sağlayıcısı artık özelleştirme modunda olmadığını belirtmek için iç bayrağını sıfırlar ve normal pencere öğesi deneyimi için görsel ve veri JSON şablonlarını göndermeye devam eder ve özelleştirme sırasında istenen değişiklikleri yansıtır.
  7. Pencere öğesi sağlayıcısı, değişikliklerin pencere öğesi sağlayıcısı çağrıları arasında korunması için özelleştirme seçeneklerini diskte veya bulutta korur.

Uyarı

Windows Uygulama SDK'sı kullanılarak oluşturulan pencere öğeleri için Windows Pencere Öğesi Panosunda, özelleştirme kartı gösterildikten sonra üç nokta menüsünün yanıt vermemeye başlamasına neden olan bilinen bir hata vardır.

Tipik Pencere Öğesi özelleştirme senaryolarında kullanıcı, pencere öğesinde hangi verilerin görüntüleneceğini seçer veya pencere öğesinin görsel sunumunu ayarlar. Kolaylık olması için, bu bölümdeki örnekte kullanıcının önceki adımlarda uygulanan sayım pencere öğesinin sayacını sıfırlamasına olanak tanıyan özelleştirme davranışı eklenecektir.

Uyarı

Pencere öğesi özelleştirmesi için Windows Uygulama SDK'sı 1.4 veya üzeri gerekir. Bu kılavuzda Microsoft.WindowsAppSDK sürüm 2.5.1 kullanılır.

Özelleştirme desteği bildirmek için paket bildirimini güncelleştirin

Pencere öğesi konağına pencere öğesinin özelleştirmeyi desteklediğini bildirmek için, pencere öğesinin Tanım eleent'ine IsCustomizable özniteliğini ekleyin ve true olarak ayarlayın.

...
<Definition Id="Counting_Widget"
    DisplayName="Microsoft Counting Widget"
    Description="CONFIG counting widget description"
    IsCustomizable="true">
...

WidgetProvider.h dosyasını güncelleştirme

Bu makaledeki önceki adımlarda oluşturulan pencere öğesine özelleştirme desteği eklemek için pencere öğesi sağlayıcımız WidgetProvider.h için üst bilgi dosyasını güncelleştirmemiz gerekir.

İlk olarak CompactWidgetInfo tanımını güncelleştirin. Bu yardımcı yapı, etkin pencere öğelerimizin geçerli durumunu izlememize yardımcı olur. Pencere öğesi sunucusunun normal pencere öğesi şablonu yerine özelleştirme JSON şablonumuzu göndermemizi beklediği zamanları izlemek için kullanılacak inCustomization alanını ekleyin.

// WidgetProvider.h
struct CompactWidgetInfo
{
    winrt::hstring widgetId;
    winrt::hstring widgetName;
    int customState = 0;
    bool isActive = false;
    bool inCustomization = false;
};

IWidgetProvider2 arabirimini uygulamak için WidgetProvider bildirimini güncelleştirin.

// WidgetProvider.h

struct WidgetProvider : winrt::implements<WidgetProvider, winrt::Microsoft::Windows::Widgets::Providers::IWidgetProvider, winrt::Microsoft::Windows::Widgets::Providers::IWidgetProvider2>

IWidgetProvider2 arabiriminin OnCustomizationRequested geri çağırması için bir bildirim ekleyin.

// WidgetProvider.h

void OnCustomizationRequested(winrt::Microsoft::Windows::Widgets::Providers::WidgetCustomizationRequestedArgs args);

Son olarak, pencere öğesi özelleştirme kullanıcı arabirimi için JSON şablonunu tanımlayan bir dize değişkeni bildirin. Bu örnekte, sağlayıcımızın normal pencere öğesi davranışına dönmesi için sinyal veren bir "Sayacı sıfırla" düğmesi ve "Özelleştirmeden çık" düğmemiz vardır.

// WidgetProvider.h
const std::string countWidgetCustomizationTemplate = R"(
{
    "type": "AdaptiveCard",
    "actions" : [
        {
            "type": "Action.Execute",
            "title" : "Reset counter",
            "verb": "reset"
            },
            {
            "type": "Action.Execute",
            "title": "Exit customization",
            "verb": "exitCustomization"
            }
    ],
    "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
    "version": "1.5"
})";

Güncelleştirme WidgetProvider.cpp

Şimdi pencere öğesi özelleştirme davranışını uygulamak için WidgetProvider.cpp dosyasını güncelleştirin. Bu yöntem, kullandığımız diğer geri çağırmalarla aynı deseni kullanır. WidgetContext'ten özelleştirilecek pencere öğesinin kimliğini alır ve bu pencere öğesiyle ilişkili CompactWidgetInfo yardımcı yapısını bulur ve inCustomization alanını true olarak ayarlarız.

//WidgetProvider.cpp
void WidgetProvider::OnCustomizationRequested(winrt::WidgetCustomizationRequestedArgs args)
{
    auto widgetId = args.WidgetContext().Id();

    std::optional<CompactWidgetInfo> widgetToUpdate;
    {
        std::scoped_lock lock(RunningWidgetsMutex);
        if (const auto iter = RunningWidgets.find(widgetId); iter != RunningWidgets.end())
        {
            iter->second.inCustomization = true;
            widgetToUpdate = iter->second;
        }
    }
    if (widgetToUpdate)
    {
        UpdateWidget(*widgetToUpdate);
    }
}

Ardından, verilerimizi ve görsel JSON şablonlarımızı pencere öğesi konağına gönderen UpdateWidget yardımcı yöntemimizi güncelleştireceğiz. Sayım pencere öğesini güncelleştirirken inCustomization alanının değerine bağlı olarak normal pencere öğesi şablonunu veya özelleştirme şablonunu göndeririz. Kısa olması için bu kod parçacığında özelleştirmeyle ilgili olmayan kod atlanır.

//WidgetProvider.cpp
void WidgetProvider::UpdateWidget(CompactWidgetInfo const& localWidgetInfo)
{
    ...
    else if (localWidgetInfo.widgetName == L"Counting_Widget")
    {
        if (!localWidgetInfo.inCustomization)
        {
            std::wcout << L" - not in customization " << std::endl;
            templateJson = winrt::to_hstring(countWidgetTemplate);
		}
        else
        {
            std::wcout << L" - in customization " << std::endl;
            templateJson = winrt::to_hstring(countWidgetCustomizationTemplate);
		}
    }
    ...
    
    updateOptions.Template(templateJson);
    updateOptions.Data(dataJson);
    // !!  You can store some custom state in the widget service that you will be able to query at any time.
    updateOptions.CustomState(winrt::to_hstring(localWidgetInfo.customState));
    winrt::WidgetManager::GetDefault().UpdateWidget(updateOptions);
}

Kullanıcılar özelleştirme şablonumuzdaki girişlerle etkileşime geçtiğinde, kullanıcı normal pencere öğesi deneyimiyle etkileşime geçtiğinde olduğu gibi Aynı OnActionInvoked işleyicisini çağırır. Özelleştirmeyi desteklemek için özelleştirme JSON şablonumuzda "reset" ve "exitCustomization" fiillerini ararız. Eylem "Sayacı sıfırla" düğmesine yönelikse, yardımcı yapımızın customState alanında tutulan sayacı 0 olarak sıfırlarız. Eylem "Özelleştirmeden çık" düğmesi içinse inCustomization alanını false olarak ayarlarız, böylece UpdateWidget'i çağırdığımızda yardımcı yöntemimiz özelleştirme şablonunu değil normal JSON şablonlarını gönderir.

//WidgetProvider.cpp
void WidgetProvider::OnActionInvoked(winrt::WidgetActionInvokedArgs actionInvokedArgs)
{
    auto verb = actionInvokedArgs.Verb();
    if (verb != L"inc" && verb != L"reset" && verb != L"exitCustomization")
    {
        return;
    }

    auto widgetId = actionInvokedArgs.WidgetContext().Id();
    std::optional<CompactWidgetInfo> widgetToUpdate;
    {
        std::scoped_lock lock(RunningWidgetsMutex);
        if (const auto iter = RunningWidgets.find(widgetId); iter != RunningWidgets.end())
        {
            auto& localWidgetInfo = iter->second;
            if (verb == L"inc")
            {
                localWidgetInfo.customState++;
            }
            else
            {
                if (verb == L"reset")
                {
                    localWidgetInfo.customState = 0;
                }
                localWidgetInfo.inCustomization = false;
            }
            widgetToUpdate = localWidgetInfo;
        }
    }
    if (widgetToUpdate)
    {
        UpdateWidget(*widgetToUpdate);
    }
}

Şimdi pencere öğesinizi dağıtırken üç nokta menüsünde Pencere öğesini özelleştir düğmesini görmeniz gerekir. Özelleştir düğmesine tıkladığınızda özelleştirme şablonunuz görüntülenir.

Pencere öğelerini özelleştirme kullanıcı arabirimini gösteren ekran görüntüsü.

Sayacı sıfırla düğmesine tıklayarak sayacı 0'a sıfırlayın. Pencere öğesinizin normal davranışına dönmek için Özelleştirmeden çık düğmesine tıklayın.