Udostępnij przez


Implementowanie dostawcy widżetu w aplikacji win32 (C++/WinRT)

W tym artykule opisano proces tworzenia prostego dostawcy widżetów, który implementuje interfejs IWidgetProvider . Metody tego interfejsu są wywoływane przez hosta widżetu w celu żądania danych definiujących widżet lub zezwalania dostawcy widżetu na akcję użytkownika w widżecie. Dostawcy widżetów mogą obsługiwać jeden widżet lub wiele widżetów. W tym przykładzie zdefiniujemy dwa różne widżety. Jednym z widżetów jest przykładowy widżet pogody, który ilustruje niektóre opcje formatowania udostępniane przez framework Kart adaptacyjnych. Drugi widżet pokaże akcje użytkownika i funkcję stanu niestandardowego widżetu, zachowując licznik, który jest zwiększany za każdym razem, gdy użytkownik kliknie przycisk wyświetlany w widżecie.

Zrzut ekranu przedstawiający prosty widżet pogody. Widżet zawiera niektóre grafiki i dane związane z pogodą, a także tekst diagnostyczny ilustrujący wyświetlanie szablonu dla widżetu o średnim rozmiarze.

Zrzut ekranu przedstawiający prosty widżet zliczania. Widżet pokazuje ciąg zawierający wartość liczbową, która ma być zwiększana, oraz przycisk z etykietą Przyrost, a także tekst diagnostyczny ilustrujący wyświetlanie szablonu dla widżetu o małym rozmiarze.

Ten kod przykładowy w tym artykule został dostosowany z przykładu widżetów z Windows App SDK . Aby zaimplementować dostawcę widżetu przy użyciu języka C#, zobacz Implementowanie dostawcy widżetów w aplikacji win32 (C#).

Wymagania wstępne

  • Urządzenie musi mieć włączony tryb dewelopera. Aby uzyskać więcej informacji, zobacz Ustawienia dla deweloperów.
  • Visual Studio 2022 lub nowsze z modułem rozwoju Platformy Uniwersalnej Windows . Pamiętaj, aby dodać składnik dla języka C++ (wersja 143) z opcjonalnej listy rozwijanej.

Tworzenie nowej aplikacji konsolowej win32 C++/WinRT

W programie Visual Studio utwórz nowy projekt. W oknie dialogowym Tworzenie nowego projektu ustaw filtr języka na "C++" i filtr platformy na Windows, a następnie wybierz szablon projektu Aplikacja konsolowa systemu Windows (C++/WinRT). Nadaj nowej nazwie nowy projekt "ExampleWidgetProvider". Po wyświetleniu monitu ustaw docelową wersję systemu Windows dla aplikacji na wersję 1809 lub nowszą.

Dodaj odwołania do pakietów NuGet Windows App SDK i Windows Implementation Library.

W tym przykładzie użyto najnowszego stabilnego pakietu NuGet zestawu SDK aplikacji systemu Windows. W Eksploratorze rozwiązań kliknij prawym przyciskiem myszy Odwołania i wybierz pozycję Zarządzaj pakietami NuGet.... W menedżerze pakietów NuGet wybierz kartę Przeglądaj i wyszukaj "Microsoft.WindowsAppSDK". Wybierz najnowszą stabilną wersję na liście rozwijanej Wersja , a następnie kliknij pozycję Zainstaluj.

W tym przykładzie użyto również pakietu NuGet biblioteki implementacji systemu Windows. W Eksploratorze rozwiązań kliknij prawym przyciskiem myszy odwołania i wybierz opcję Zarządzaj pakietami NuGet.... W menedżerze pakietów NuGet wybierz kartę Przeglądaj i wyszukaj „Microsoft.Windows.ImplementationLibrary”. Wybierz najnowszą wersję z listy rozwijanej Wersja, a następnie kliknij pozycję Zainstaluj.

W pliku prekompilowanego nagłówka pch.h dodaj następujące dyrektywy include.

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

Uwaga / Notatka

Należy dołączyć nagłówek wil/cppwinrt.h przed wszystkimi nagłówkami WinRT.

Aby poprawnie obsłużyć zamykanie aplikacji dostawcy widżetów, musimy wykonać niestandardową implementację winrt::get_module_lock. Wstępnie deklarujemy metodę SignalLocalServerShutdown , która zostanie zdefiniowana w naszym pliku main.cpp i ustawi zdarzenie, które sygnalizuje zakończenie działania aplikacji. Dodaj następujący kod do pliku pch.h, tuż poniżej dyrektywy #pragma once, przed innymi dołączeniami.

//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

Dodawanie klasy WidgetProvider do obsługi operacji widżetu

W programie Visual Studio kliknij prawym przyciskiem myszy projekt ExampleWidgetProvider w eksploratorze rozwiązań i wybierz pozycję Dodaj>Klasa. W oknie dialogowym Dodaj klasę nadaj klasie nazwę "WidgetProvider" i kliknij przycisk Dodaj.

Deklarowanie klasy implementujące interfejs IWidgetProvider

Interfejs IWidgetProvider definiuje metody, które host widżetu wywoła w celu zainicjowania operacji z dostawcą widżetu. Zastąp pustą definicję klasy w pliku WidgetProvider.h następującym kodem. Ten kod deklaruje strukturę, która implementuje interfejs IWidgetProvider i deklaruje prototypy dla metod interfejsu.

// 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 */

    
};

Ponadto dodaj metodę prywatną UpdateWidget, która jest metodą pomocnika, która będzie wysyłać aktualizacje od naszego dostawcy do hosta widżetu.

// WidgetProvider.h
private: 

void UpdateWidget(CompactWidgetInfo const& localWidgetInfo);

Przygotowanie do śledzenia włączonych widżetów

Dostawca widżetu może obsługiwać jeden widżet lub wiele widżetów. Za każdym razem, gdy host widżetu inicjuje operację z dostawcą widżetu, przekazuje identyfikator w celu zidentyfikowania widżetu skojarzonego z operacją. Każdy widżet ma również skojarzoną nazwę i stan, które mogą służyć do przechowywania danych niestandardowych. W tym przykładzie zadeklarujemy prostą strukturę pomocnika do przechowywania identyfikatora, nazwy i danych dla każdego przypiętego widżetu. Widżety mogą być również w stanie aktywnym, który jest omówiony w sekcji Aktywuj i Dezaktywuj poniżej, a następnie prześledzimy ten stan dla każdego widżetu z wartością logiczną. Dodaj następującą definicję do pliku WidgetProvider.h powyżej deklaracji struktury WidgetProvider .

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

Wewnątrz deklaracji WidgetProvider w WidgetProvider.h dodaj członka dla mapy, która będzie prowadzić listę włączonych widżetów, używając identyfikatora widżetu jako klucza dla każdego wpisu.

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

        

Deklarowanie ciągów JSON szablonu widżetu

W tym przykładzie zostaną zadeklarowane niektóre ciągi statyczne w celu zdefiniowania szablonów JSON dla każdego widżetu. Dla wygody te szablony są przechowywane w zmiennych lokalnych zadeklarowanych poza definicją klasy WidgetProvider . Jeśli potrzebujesz ogólnego miejsca przechowywania dla szablonów — mogą one zostać dołączone jako część pakietu aplikacji: Uzyskiwanie dostępu do plików pakietu. Aby uzyskać informacje na temat tworzenia dokumentu JSON szablonu widżetu, zobacz Tworzenie szablonu widżetu za pomocą projektanta kart adaptacyjnych.

W najnowszej wersji aplikacje, które implementują widżety systemu Windows, mogą dostosowywać nagłówek, który jest wyświetlany dla ich widżetów na Tablicy Widżetów, zastępując domyślną prezentację. Aby uzyskać więcej informacji, zobacz Dostosowywanie obszaru nagłówka widżetu.

Uwaga / Notatka

W najnowszej wersji aplikacje implementujące widżety systemu Windows mogą zdecydować się na wypełnienie zawartości widżetu kodem HTML obsługiwanym z określonego adresu URL zamiast podawania zawartości w formacie schematu karty adaptacyjnej w ładunku JSON przekazanym od dostawcy do tablicy widżetów. Dostawcy widżetów muszą nadal dostarczać ładunek JSON karty adaptacyjnej, więc kroki implementacji w tym przewodniku mają zastosowanie do widżetów internetowych. Aby uzyskać więcej informacji, skontaktuj się z dostawcami widżetów internetowych.

// 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://messagecardplayground.azurewebsites.net/assets/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://messagecardplayground.azurewebsites.net/assets/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"                                                
})";

Implementowanie metod IWidgetProvider

W kilku następnych sekcjach zaimplementujemy metody interfejsu IWidgetProvider. Metoda pomocnika UpdateWidget wywoływana w kilku z tych implementacji metod zostanie pokazana w dalszej części tego artykułu. Przed rozpoczęciem pracy z metodami interfejsu, po dyrektywach dołączania, dodaj następujące wiersze do WidgetProvider.cpp, aby wczytać interfejsy API dostarczane przez widżet do przestrzeni nazw winrt i zezwolić na dostęp do mapy zadeklarowanej w poprzednim kroku.

Uwaga / Notatka

Obiekty przekazywane do metod wywołań zwrotnych interfejsu IWidgetProvider są gwarantowane jako prawidłowe tylko w trakcie ich wywołania. Nie należy przechowywać odwołań do tych obiektów, ponieważ ich zachowanie poza kontekstem wywołania zwrotnego jest niezdefiniowane.

// WidgetProvider.cpp
namespace winrt
{
    using namespace Microsoft::Windows::Widgets::Providers;
}

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

UtwórzWidżet

Gdy użytkownik przypnie jeden z widżetów Twojej aplikacji w hoście widżetów, host wywołuje CreateWidget. Najpierw metoda pobiera identyfikator i nazwę skojarzonego widżetu, a następnie dodaje nowe wystąpienie naszej struktury pomocniczej CompactWidgetInfodo zbioru aktywnych widżetów. Następnie wysyłamy początkowy szablon i dane dla widżetu, który jest hermetyzowany w metodzie pomocnika UpdateWidget.

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

UsuńWidżet

Host widżetu wywołuje DeleteWidget, gdy użytkownik odpiął jeden z widżetów Twojej aplikacji z hosta widżetu. W takim przypadku usuniemy skojarzony widżet z naszej listy z włączonymi widżetami, abyśmy nie wysyłali żadnych dalszych aktualizacji tego widżetu.

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

OnActionInvoked

Host widżetu wywołuje OnActionInvoked, gdy użytkownik wchodzi w interakcję z akcją, którą zdefiniowałeś w szablonie widżetu. W widżecie licznika użytym w tym przykładzie zadeklarowano akcję z czasownikiem i wartością "inc" w szablonie JSON dla widżetu. Kod dostawcy widżetu użyje tego czasownika i wartości, aby określić działanie w odpowiedzi na interakcję użytkownika.

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

W metodzie OnActionInvoked pobierz wartość czasownika, sprawdzając właściwość Verb obiektu WidgetActionInvokedArgs przekazanego do metody. Jeśli czasownik ma wartość "inc", wiemy, że będziemy zwiększać liczbę w stanie niestandardowym dla widżetu. Z WidgetActionInvokedArgspobierz obiekt WidgetContext, a następnie WidgetId, aby uzyskać identyfikator zaktualizowanego widżetu. Znajdź wpis na naszej mapie włączonych widżetów z określonym identyfikatorem, a następnie zaktualizuj niestandardową wartość stanu, która jest wykorzystywana do przechowywania liczby przyrostów. Na koniec zaktualizuj zawartość widżetu przy użyciu nowej wartości za pomocą funkcji pomocnika UpdateWidget.

// 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();
        if (const auto iter = RunningWidgets.find(widgetId); iter != RunningWidgets.end())
        {
            auto& localWidgetInfo = iter->second;
            // Increment the count
            localWidgetInfo.customState++;
            UpdateWidget(localWidgetInfo);
        }
    }
}

Aby uzyskać informacje o składni Action.Execute dla kart adaptacyjnych, zobacz Action.Execute. Aby uzyskać wskazówki dotyczące projektowania interakcji dla widżetów, zobacz wskazówki dotyczące projektowania interakcji z widżetem

PoZmienieniuKontekstuWidżetu

W bieżącej wersji OnWidgetContextChanged jest wywoływana tylko wtedy, gdy użytkownik zmienia rozmiar przypiętego widżetu. Możesz wybrać zwrócenie innego szablonu/danych JSON do hosta widżetu, w zależności od żądanego rozmiaru. Kod JSON szablonu można również zaprojektować tak, aby obsługiwał wszystkie dostępne rozmiary przy użyciu renderowania warunkowego na podstawie wartości host.widgetSize. Jeśli nie musisz wysyłać nowego szablonu lub danych w celu uwzględnienia zmiany rozmiaru, możesz użyć OnWidgetContextChanged dla celów telemetrii.

// WidgetProvider.cpp
void WidgetProvider::OnWidgetContextChanged(winrt::WidgetContextChangedArgs contextChangedArgs)
{
    auto widgetContext = contextChangedArgs.WidgetContext();
    auto widgetId = widgetContext.Id();
    auto widgetSize = widgetContext.Size();
    if (const auto iter = RunningWidgets.find(widgetId); iter != RunningWidgets.end())
    {
        auto localWidgetInfo = iter->second;

        UpdateWidget(localWidgetInfo);
    }
}
    

Aktywowanie i dezaktywowanie

Metoda Activate jest wywoływana w celu powiadomienia dostawcy widżetu, że host widżetu jest obecnie zainteresowany otrzymywaniem zaktualizowanej zawartości od dostawcy. Na przykład może to oznaczać, że użytkownik aktualnie aktywnie ogląda hosta widżetu. Metoda Dezaktywuj jest wywoływana w celu powiadomienia dostawcy widżetu o tym, że host widżetu nie żąda już aktualizacji zawartości. Te dwie metody definiują okno, w którym host widżetu jest najbardziej zainteresowany wyświetlaniem najbardziej aktualnej zawartości up-to. Dostawcy widżetów mogą wysyłać aktualizacje do widżetu w dowolnym momencie, na przykład w odpowiedzi na powiadomienie wypychane, ale podobnie jak w przypadku dowolnego zadania w tle, ważne jest, aby zrównoważyć dostarczanie up-to-date zawartości z problemami dotyczącymi zasobów, takimi jak żywotność baterii.

Aktywowanie i Dezaktywowanie są wywoływane dla poszczególnych widżetów. Ten przykład śledzi aktywny stan każdego widżetu w strukturze pomocniczej CompactWidgetInfo. W metodzie Activate wywołujemy metodę pomocnika UpdateWidget w celu zaktualizowania widżetu. Należy pamiętać, że okno czasowe między Aktywacja a Dezaktywacja może być małe, dlatego zaleca się jak najszybsze zaktualizowanie ścieżki aktualizacji kodu widżetu.

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

    if (const auto iter = RunningWidgets.find(widgetId); iter != RunningWidgets.end())
    {
        auto& localWidgetInfo = iter->second;
        localWidgetInfo.isActive = true;

        UpdateWidget(localWidgetInfo);
    }
}

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

Aktualizowanie widżetu

Zdefiniuj metodę pomocnika UpdateWidget w celu zaktualizowania włączonego widżetu. W tym przykładzie sprawdzamy nazwę widżetu w strukturze pomocnika CompactWidgetInfo przekazanej do metody, następnie ustawiamy odpowiedni szablon i dane JSON na podstawie tego, który widżet jest aktualizowany. WidgetUpdateRequestOptions jest inicjowany przy użyciu szablonu, danych i stanu niestandardowego dla aktualizowanego widżetu. Wywołaj WidgetManager::GetDefault, aby uzyskać wystąpienie klasy WidgetManager, a następnie wywołaj UpdateWidget, aby wysłać zaktualizowane dane widżetu do hosta widżetu.

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

Inicjowanie listy włączonych widżetów podczas uruchamiania

Po pierwszym zainicjowaniu dostawcy widżetów warto zapytać WidgetManager, czy istnieją uruchomione widżety, które obecnie obsługuje nasz dostawca. Pomoże to odzyskać aplikację do poprzedniego stanu w przypadku ponownego uruchomienia komputera lub awarii dostawcy. Wywołaj WidgetManager::GetDefault, aby uzyskać domyślne wystąpienie menedżera widżetów dla aplikacji. Następnie wywołaj GetWidgetInfos, która zwraca tablicę obiektów WidgetInfo. Skopiuj identyfikatory, nazwy i stan niestandardowy widżetu do struktury pomocniczej CompactWidgetInfo i zapisz je w zmiennej składowej RunningWidgets. Wklej następujący kod do konstruktora dla klasy WidgetProvider .

// 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();
        if (RunningWidgets.find(widgetId) == RunningWidgets.end())
        {
            CompactWidgetInfo runningWidgetInfo{ widgetId, widgetName };
            try
            {
                // If we had any save state (in this case we might have some state saved for Counting widget)
                // convert string to required type if needed.
                int count = std::stoi(winrt::to_string(customState));
                runningWidgetInfo.customState = count;
            }
            catch (...)
            {

            }
            RunningWidgets[widgetId] = runningWidgetInfo;
        }
    }
}

Zarejestruj fabrykę obiektów, która utworzy element WidgetProvider na żądanie

Dodaj nagłówek definiujący klasę WidgetProvider do elementu dołączanego w górnej części pliku main.cpp aplikacji. Również tutaj będziemy uwzględniać mutex.

// main.cpp
...
#include "WidgetProvider.h"
#include <mutex>

Zadeklaruj zdarzenie, które spowoduje zakończenie działania naszej aplikacji, oraz funkcję SignalLocalServerShutdown, która ustawi to zdarzenie. Wklej następujący kod w main.cpp.

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

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

Następnie należy utworzyć CLSID, która będzie używana do identyfikowania dostawcy widżetu na potrzeby aktywacji modelu COM. Wygeneruj identyfikator GUID w programie Visual Studio, przechodząc do Tools—>Create GUID. Wybierz opcję "static const GUID =" i kliknij przycisk Kopiuj , a następnie wklej ją do main.cpp. Zaktualizuj definicję identyfikatora GUID przy użyciu następującej składni języka C++/WinRT, ustawiając nazwę zmiennej GUID widget_provider_clsid. Pozostaw zakomentowaną wersję identyfikatora GUID, ponieważ ten format będzie potrzebny później podczas pakowania aplikacji.

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

Dodaj następującą definicję fabryki klas do main.cpp. Jest to głównie standardowy kod, który nie jest specyficzny dla implementacji dostawcy widżetów. Należy pamiętać, że CoWaitForMultipleObjects czeka na wyzwolenie zdarzenia zamknięcia przed zamknięciem aplikacji.

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

        std::unique_lock lock(mutex);

        if (outer)
        {
            return CLASS_E_NOAGGREGATION;
        }

        if (!instance)
        {
            instance = winrt::make<WidgetProvider>();
        }

        return instance.as(iid, result);
    }

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

private:
    T instance{ nullptr };
    std::mutex mutex;
};

int main()
{
    winrt::init_apartment();
    wil::unique_com_class_object_cookie widgetProviderFactory;
    auto factory = winrt::make<SingletonClassFactory<winrt::Microsoft::Windows::Widgets::Providers::IWidgetProvider>>();

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

Spakuj aplikację dostawcy widżetów

W bieżącej wersji jako dostawcy widżetów mogą być rejestrowane tylko spakowane aplikacje. Poniższe kroki poprowadzą cię przez proces pakowania aplikacji i aktualizowania manifestu aplikacji, aby zarejestrować ją w systemie operacyjnym jako dostawcę widżetów.

Utwórz projekt pakowania MSIX

W Eksploratorze rozwiązań kliknij swoje rozwiązanie prawym przyciskiem myszy i wybierz >Nowy projekt.... W oknie dialogowym "Dodawanie nowego projektu" wybierz szablon "Projekt tworzenia pakietów aplikacji systemu Windows" i kliknij Dalej. Ustaw nazwę projektu na "ExampleWidgetProviderPackage", a następnie kliknij przycisk Utwórz. Po wyświetleniu monitu ustaw wersję docelową na wersję 1809 lub nowszą, a następnie kliknij przycisk OK. Następnie kliknij prawym przyciskiem myszy projekt ExampleWidgetProviderPackage i wybierz pozycję Add->Project reference. Wybierz projekt ExampleWidgetProvider i kliknij przycisk OK.

Dodaj odwołanie do pakietu Windows App SDK do projektu pakietowania

Należy dodać odwołanie do pakietu NuGet Windows App SDK do projektu pakietowania MSIX. W eksploratorze rozwiązań kliknij dwukrotnie projekt ExampleWidgetProviderPackage, aby otworzyć plik ExampleWidgetProviderPackage.wapproj. Dodaj następujący kod XML wewnątrz elementu Project.

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

Uwaga / Notatka

Upewnij się, że wersja określona w elemencie PackageReference jest zgodna z najnowszą stabilną wersją, do której odwołujesz się w poprzednim kroku.

Jeśli na komputerze jest już zainstalowana poprawna wersja zestawu SDK aplikacji systemu Windows i nie chcesz pakować środowiska uruchomieniowego zestawu SDK w pakiecie, możesz określić zależność pakietu w pliku Package.appxmanifest dla projektu ExampleWidgetProviderPackage.

<!--Package.appxmanifest-->
...
<Dependencies>
...
    <PackageDependency Name="Microsoft.WindowsAppRuntime.1.2-preview2" MinVersion="2000.638.7.0" Publisher="CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US" />
...
</Dependencies>
...

Aktualizowanie manifestu pakietu

W Eksploratorze rozwiązań kliknij prawym przyciskiem myszy plik i wybierz pozycję Wyświetl kod, aby otworzyć plik XML manifestu. Następnie należy dodać deklaracje przestrzeni nazw dla rozszerzeń pakietu aplikacji, których będziemy używać. Dodaj następujące definicje przestrzeni nazw do elementu najwyższego poziomu Package.

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

Wewnątrz elementu Application utwórz nowy pusty element o nazwie Extensions. Upewnij się, że jest to umieszczone po tagu zamykającym dla uap:VisualElements.

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

    </Extensions>
</Application>

Pierwszym rozszerzeniem, które musimy dodać, jest rozszerzenie ComServer. Spowoduje to zarejestrowanie punktu wejścia pliku wykonywalnego w systemie operacyjnym. To rozszerzenie jest odpowiednikiem aplikacji pakietowej umożliwiającej rejestrację serwera COM poprzez ustawienie klucza rejestru i nie jest specyficzne dla dostawców widżetów. Dodaj następujący element com:Extension jako element podrzędny elementu Extensions. Zmień identyfikator GUID w atrybucie Id elementu com:Class na identyfikator GUID wygenerowany w poprzednim kroku.

<!-- 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>

Następnie dodaj rozszerzenie, które rejestruje aplikację jako dostawcę widżetu. Wklej element uap3:Extension w poniższym fragmencie kodu jako element podrzędny elementu Extensions. Pamiętaj, aby zastąpić atrybut ClassId elementu COM identyfikatorem GUID użytym w poprzednich krokach.

<!-- 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>

Aby uzyskać szczegółowe opisy i informacje o formacie dla wszystkich tych elementów, zobacz Format XML manifestu dostawcy widżetów.

Dodawanie ikon i innych obrazów do projektu pakowania

W eksploratorze rozwiązań kliknij prawym przyciskiem myszy ExampleWidgetProviderPackage i wybierz dodaj>nowy folder. Nadaj temu folderowi nazwę ProviderAssets, ponieważ użyto go w Package.appxmanifest z poprzedniego kroku. W tym miejscu będziemy przechowywać nasze ikony i zrzuty ekranu dla naszych widżetów. Gdy już dodasz pożądane ikony i zrzuty ekranu, upewnij się, że nazwy obrazów są zgodne z tym, co znajduje się po Path=ProviderAssets\ w Package.appxmanifest, w przeciwnym razie widżety nie będą wyświetlane na hoście widżetu.

Aby uzyskać informacje o wymaganiach projektowych dotyczących obrazów zrzutów ekranu i konwencji nazewnictwa zlokalizowanych zrzutów ekranu, zobacz Integrowanie z selektorem widżetów.

Testowanie dostawcy widgetu

Upewnij się, że wybrano zgodną architekturę z listy rozwijanej Platformy rozwiązań, na przykład "x64", która odpowiada twojej maszynie deweloperskiej. W eksploratorze rozwiązań kliknij rozwiązanie prawym przyciskiem myszy i wybierz pozycję Build Solution. Po wykonaniu tej czynności kliknij prawym przyciskiem myszy ExampleWidgetProviderPackage i wybierz pozycję Wdróż. W bieżącej wersji jedynym obsługiwanym hostem widżetu jest Tablica widżetów. Aby wyświetlić widżety, musisz otworzyć tablicę widżetów i wybrać pozycję Dodaj widżety w prawym górnym rogu. Przewiń do dołu dostępnych widżetów i powinien zostać wyświetlony fikcyjny Widżet Pogody i Widżet Zliczania Microsoft, które zostały utworzone podczas tego samouczka. Kliknij widżety, aby przypiąć je do tablicy widżetów i przetestować ich funkcjonalność.

Debugowanie twojego dostawcy widżetów

Po przypięciu widżetów Platforma widżetów uruchomi aplikację twojego dostawcy widżetów, aby mogła otrzymywać i wysyłać istotne informacje dotyczące widżetu. Aby debugować uruchomiony widżet, możesz dołączyć debuger do uruchomionej aplikacji dostawcy widżetów lub skonfigurować program Visual Studio, aby automatycznie rozpocząć debugowanie procesu dostawcy widżetu po jego uruchomieniu.

Aby dołączyć do uruchomionego procesu:

  1. W programie Visual Studio kliknij pozycję debugowanie —> Dołącz do procesu.
  2. Przefiltruj procesy i znajdź żądaną aplikację dostawcy widżetów.
  3. Dołącz debuger.

Aby automatycznie dołączyć debuger do procesu po początkowym uruchomieniu:

  1. W programie Visual Studio kliknij pozycję debugowanie —> inne cele debugowania —> Debugowanie zainstalowanego pakietu aplikacji.
  2. Przefiltruj pakiety i znajdź żądany pakiet dostawcy widżetów.
  3. Wybierz go i zaznacz pole wyboru "Nie uruchamiaj, ale debuguj mój kod, gdy zostanie uruchomiony".
  4. Kliknij , aby dołączyć.

Konwertowanie aplikacji konsolowej na aplikację systemu Windows

Aby przekonwertować aplikację konsolową utworzoną w tym przewodniku do aplikacji systemu Windows:

  1. Kliknij prawym przyciskiem myszy projekt ExampleWidgetProvider w Eksploratorze rozwiązań i wybierz Właściwości . Przejdź do konsolidatora —> System i zmień podsystem z "Console" na "Windows". Można to również zrobić, dodając <SubSystem>Windows</SubSystem> do sekcji <Link>..</Link> pliku .vcxproj.
  2. W main.cpp zmień wartość int main() na int WINAPI wWinMain(_In_ HINSTANCE /*hInstance*/, _In_opt_ HINSTANCE /*hPrevInstance*/, _In_ PWSTR pCmdLine, _In_ int /*nCmdShow*/).

Zrzut ekranu przedstawiający właściwości projektu dostawcy widżetów języka C++ z typem wyjściowym ustawionym na aplikację systemu Windows

Publikowanie widżetu

Po opracowaniu i przetestowaniu widżetu możesz opublikować aplikację w sklepie Microsoft Store, aby użytkownicy mogli instalować widżety na swoich urządzeniach. Aby uzyskać szczegółowe wskazówki dotyczące publikowania aplikacji, zobacz Publikowanie aplikacji w sklepie Microsoft Store.

Kolekcja sklepów widżetów

Po opublikowaniu aplikacji w sklepie Microsoft Store możesz poprosić o włączenie aplikacji do kolekcji sklepów widżetów, która ułatwia użytkownikom odnajdywanie aplikacji, które zawierają widżety systemu Windows. Aby przesłać żądanie, zobacz Prześlij informacje o widżecie w celu dodania do Kolekcji Sklepu.

Zrzut ekranu sklepu Microsoft Store przedstawiający kolekcję widżetów, która umożliwia użytkownikom odnajdywanie aplikacji, które zawierają widżety systemu Windows.

Implementowanie dostosowywania widżetu

Począwszy od zestawu Windows App SDK 1.4, widżety mogą obsługiwać dostosowywanie użytkowników. Po zaimplementowaniu tej funkcji do menu z trzema kropkami zostanie dodana opcja Dostosuj widżet powyżej opcji Odepnij widżet.

Zrzut ekranu przedstawiający widżet z wyświetlonym oknom dialogowym dostosowywania.

Poniższe kroki zawierają podsumowanie procesu dostosowywania widżetu.

  1. W normalnej operacji dostawca widżetu odpowiada na żądania od hosta widżetu przy użyciu szablonów wizualizacji i danych JSON dla zwykłego środowiska widżetu.
  2. Użytkownik klika przycisk Dostosuj widżet w menu z trzema kropkami.
  3. Widżet zgłasza zdarzenie OnCustomizationRequested u dostawcy widżetu, aby wskazać, że użytkownik zażądał możliwości dostosowywania widżetu.
  4. Dostawca widżetu ustawia flagę wewnętrzną, aby wskazać, że widżet jest w trybie dostosowywania. W trybie dostosowywania dostawca widżetu wysyła szablony JSON dla interfejsu użytkownika dostosowywania widżetu zamiast zwykłego interfejsu użytkownika widżetu.
  5. W trybie personalizacji dostawca widżetu otrzymuje zdarzenia OnActionInvoked, gdy użytkownik wchodzi w interakcję z interfejsem użytkownika personalizacji i na podstawie tych działań dostosowuje swoją wewnętrzną konfigurację oraz zachowanie.
  6. Gdy akcja skojarzona z zdarzeniem OnActionInvoked to zdefiniowana przez aplikację akcja "zakończ dostosowywanie", dostawca widżetu resetuje flagę wewnętrzną, aby wskazać, że nie jest już w trybie dostosowywania i wznawia wysyłanie szablonów wizualizacji i danych JSON dla zwykłego środowiska widżetu, odzwierciedlając zmiany żądane podczas dostosowywania.
  7. Dostawca widżetu utrzymuje opcje dostosowywania na dysku lub w chmurze, aby zmiany zostały zachowane między wywołaniami dostawcy widżetu.

Uwaga / Notatka

Istnieje znana usterka dotycząca tablicy widżetów systemu Windows dla widżetów utworzonych przy użyciu zestawu SDK aplikacji systemu Windows, która powoduje, że menu wielokropka przestaje odpowiadać po wyświetlaniu karty dostosowywania.

W typowych scenariuszach dostosowywania widżetu użytkownik wybierze dane wyświetlane w widżecie lub dostosuje wizualną prezentację widżetu. Dla uproszczenia przykład w tej sekcji spowoduje dodanie zachowania dostosowywania, które umożliwia użytkownikowi zresetowanie licznika widżetu zliczania zaimplementowanego w poprzednich krokach.

Uwaga / Notatka

Dostosowywanie widżetu jest obsługiwane tylko w zestawie Sdk aplikacji systemu Windows w wersji 1.4 lub nowszej. Pamiętaj, aby zaktualizować odwołania w projekcie do najnowszej wersji pakietu Nuget.

Zaktualizuj manifest pakietu, aby zadeklarować wsparcie dla dostosowań

Aby poinformować hosta widżetu, że widżet obsługuje dostosowywanie, dodaj atrybut IsCustomizable do elementu eleent definicji dla widżetu i ustaw go na wartość true.

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

Zaktualizuj WidgetProvider.h

Aby dodać obsługę dostosowywania do widżetu utworzonego w poprzednich krokach w tym artykule, musimy zaktualizować plik nagłówka dla naszego dostawcy widżetów WidgetProvider.h.

Najpierw zaktualizuj definicję CompactWidgetInfo . Ta struktura pomocnika pomaga nam śledzić bieżący stan naszych aktywnych widżetów. Dodaj pole inCustomization, które będzie używane do śledzenia, kiedy host widżetu oczekuje od nas wysłania szablonu dostosowania json, a nie szablonu zwykłego widżetu.

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

Zaktualizuj deklarację WidgetProvider , aby zaimplementować interfejs IWidgetProvider2 .

// WidgetProvider.h

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

Dodaj deklarację dla wywołania zwrotnego OnCustomizationRequested interfejsu IWidgetProvider2.

// WidgetProvider.h

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

Na koniec zadeklaruj zmienną ciągu, która definiuje szablon JSON dla interfejsu użytkownika dostosowywania widżetu. W tym przykładzie mamy przycisk "Resetuj licznik" i przycisk "Zakończ dostosowywanie", który zasygnalizuje, że nasz dostawca powróci do normalnego zachowania widżetu.

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

Aktualizowanie WidgetProvider.cpp

Teraz zaktualizuj plik WidgetProvider.cpp, aby zaimplementować zachowanie dostosowywania widżetu. Ta metoda używa tego samego wzorca co inne użyte wywołania zwrotne. Otrzymujemy identyfikator widżetu, który ma zostać dostosowany, z WidgetContext, znajdujemy strukturę pomocniczą CompactWidgetInfo skojarzoną z tym widżetem i ustawiamy pole inCustomization na prawda.

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

    if (const auto iter = RunningWidgets.find(widgetId); iter != RunningWidgets.end())
    {
        auto& localWidgetInfo = iter->second;
        localWidgetInfo.inCustomization = true;

        UpdateWidget(localWidgetInfo);
    }
}

Następnie zaktualizujemy metodę pomocnika UpdateWidget, która wysyła nasze dane i wizualne szablony JSON do hosta widżetu. Podczas aktualizowania widżetu zliczania wysyłamy zwykły szablon widżetu lub szablon dostosowywania w zależności od wartości pola inCustomization. Dla zwięzłości, kod, który nie jest istotny dla dostosowywania, został pominięty w tym fragmencie kodu.

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

Gdy użytkownicy wchodzą w interakcję z danymi wejściowymi w naszym szablonie dostosowywania, wywołuje tę samą obsługę OnActionInvoked, jak w przypadku interakcji użytkownika ze zwykłym środowiskiem widżetu. Aby zapewnić obsługę personalizacji, szukamy czasowników „reset” i „exitCustomization” z naszego szablonu JSON. Jeśli akcja dotyczy przycisku "Resetuj licznik", zresetujemy licznik przechowywany w polu customState naszej struktury pomocniczej na wartość 0. Jeśli akcja dotyczy przycisku "Zakończ dostosowywanie", ustawimy pole inCustomization na false, tak aby podczas wywoływania UpdateWidgetnasza metoda pomocnika wysyłała zwykłe szablony JSON, a nie szablon dostosowywania.

//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();
        if (const auto iter = RunningWidgets.find(widgetId); iter != RunningWidgets.end())
        {
            auto& localWidgetInfo = iter->second;
            // Increment the count
            localWidgetInfo.customState++;
            UpdateWidget(localWidgetInfo);
        }
    }
    else if (verb == L"reset") 
    {
        auto widgetId = actionInvokedArgs.WidgetContext().Id();
        auto data = actionInvokedArgs.Data();
        if (const auto iter = RunningWidgets.find(widgetId); iter != RunningWidgets.end())
        {
            auto& localWidgetInfo = iter->second;
            // Reset the count
            localWidgetInfo.customState = 0;
            localWidgetInfo.inCustomization = false;
            UpdateWidget(localWidgetInfo);
        }
    }
    else if (verb == L"exitCustomization")
    {
        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();
        if (const auto iter = RunningWidgets.find(widgetId); iter != RunningWidgets.end())
        {
            auto& localWidgetInfo = iter->second;
            // Stop sending the customization template
            localWidgetInfo.inCustomization = false;
            UpdateWidget(localWidgetInfo);
        }
    }
}

Teraz po wdrożeniu widżetu powinien zostać wyświetlony przycisk Dostosuj widżet w menu wielokropka. Kliknięcie przycisku dostosuj spowoduje wyświetlenie szablonu dostosowywania.

Zrzut ekranu przedstawiający interfejs użytkownika dostosowywania widżetów.

Kliknij przycisk Resetuj licznik, aby zresetować licznik do 0. Kliknij przycisk Zakończ dostosowywanie, aby powrócić do normalnego działania widżetu.