Delen via


Een widgetprovider implementeren in een win32-app (C++/WinRT)

Dit artikel begeleidt u bij het maken van een eenvoudige widgetprovider waarmee de IWidgetProvider-interface wordt geïmplementeerd. De methoden van deze interface worden aangeroepen door de widgethost om de gegevens aan te vragen die een widget definiëren of om de widgetprovider te laten reageren op een gebruikersactie op een widget. Widgetproviders kunnen één widget of meerdere widgets ondersteunen. In dit voorbeeld definiëren we twee verschillende widgets. Een widget is een gesimuleerde weerwidget die enkele van de opmaakopties illustreert die door het Adaptive Cards-framework worden geboden. De tweede widget demonstreert gebruikersacties en de aangepaste widgetstatusfunctie door een teller te onderhouden die wordt verhoogd wanneer de gebruiker op een knop klikt die wordt weergegeven in de widget.

Een schermopname van een eenvoudige weerwidget. De widget toont een aantal weergerelateerde afbeeldingen en gegevens, evenals een aantal diagnostische tekst die illustreert dat de sjabloon voor de middelgrote widget wordt weergegeven.

Een schermopname van een eenvoudige telwidget. De widget toont een tekenreeks met de numerieke waarde die moet worden verhoogd en een knop met het label 'Verhogen', evenals enkele diagnostische teksten die illustreren dat het sjabloon voor de kleine widget wordt weergegeven.

Deze voorbeeldcode in dit artikel is aangepast aan het windows App SDK-widgetsvoorbeeld. Zie Een widgetprovider implementeren in een win32-app (C#) als u een widgetprovider wilt implementeren met C#.

Vereiste voorwaarden

  • Op uw apparaat moet de ontwikkelaarsmodus zijn ingeschakeld. Zie Instellingen voor ontwikkelaars voor meer informatie.
  • Visual Studio 2022 of hoger met de Universal Windows Platform-ontwikkeling workload. Zorg ervoor dat u het onderdeel voor C++ (v143) toevoegt vanuit de optionele vervolgkeuzelijst.

Een nieuwe C++/WinRT win32-console-app maken

Maak in Visual Studio een nieuw project. Stel in het dialoogvenster Een nieuw project maken het taalfilter in op 'C++' en het platformfilter op Windows en selecteer vervolgens de projectsjabloon Windows-consoletoepassing (C++/WinRT). Geef het nieuwe project de naam ExampleWidgetProvider. Stel de windows-doelversie voor de app in op versie 1809 of hoger wanneer u hierom wordt gevraagd.

Verwijzingen toevoegen aan de NuGet-pakketten van de Windows App SDK en De Windows-implementatiebibliotheek

In dit voorbeeld wordt het meest recente stabiele NuGet-pakket voor Windows App SDK gebruikt. Klik in Solution Explorer met de rechtermuisknop op Verwijzingen en selecteer NuGet-pakketten beheren.... Selecteer in NuGet Package Manager het tabblad Bladeren en zoek naar Microsoft.WindowsAppSDK. Selecteer de meest recente stabiele versie in de vervolgkeuzelijst Versie en klik vervolgens op Installeren.

In dit voorbeeld wordt ook het NuGet-pakket Windows Implementatiebibliotheek gebruikt. Klik in Solution Explorer met de rechtermuisknop op Verwijzingen en selecteer NuGet-pakketten beheren.... Selecteer in NuGet Package Manager het tabblad Bladeren en zoek naar Microsoft.Windows.ImplementationLibrary. Selecteer de nieuwste versie in het Versie uitklapmenu en klik vervolgens op Installeren.

Voeg in het vooraf gecompileerde headerbestand pch.h de volgende include-richtlijnen toe.

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

Opmerking

U moet eerst de header wil/cppwinrt.h opnemen voordat u WINRT-headers opneemt.

Om het afsluiten van de widgetprovider-app correct te behandelen, moeten we een aangepaste implementatie van winrt::get_module_lockhebben. We declareren vooraf de SignalLocalServerShutdown-methode die wordt gedefinieerd in ons main.cpp-bestand en stellen een gebeurtenis in die de app aangeeft om af te sluiten. Voeg de volgende code toe aan uw pch.h-bestand, net onder de #pragma once-richtlijn, voordat de andere zijn opgenomen.

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

Een WidgetProvider-klasse toevoegen om widgetbewerkingen te verwerken

Klik in Visual Studio met de rechtermuisknop op het ExampleWidgetProvider-project in de Solution Explorer en selecteer Toevoegen->Klasse. Geef in het dialoogvenster Klasse toevoegen de klasse WidgetProvider een naam en klik op Toevoegen.

Een klasse declareren die de interface IWidgetProvider implementeert

De interface IWidgetProvider definieert methoden die door de widgethost worden aangeroepen om bewerkingen te starten met de widgetprovider. Vervang de lege klassedefinitie in het bestand WidgetProvider.h door de volgende code. Deze code declareert een structuur die de IWidgetProvider-interface implementeert en prototypen declareert voor de interfacemethoden.

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

    
};

Voeg ook een privémethode , UpdateWidget, toe. Dit is een helpermethode waarmee updates van onze provider naar de widgethost worden verzonden.

// WidgetProvider.h
private: 

void UpdateWidget(CompactWidgetInfo const& localWidgetInfo);

Maak gereed voor het bijhouden van ingeschakelde widgets

Een widgetprovider kan één widget of meerdere widgets ondersteunen. Wanneer de widgethost een bewerking start met de widgetprovider, wordt er een id doorgegeven om de widget te identificeren die aan de bewerking is gekoppeld. Elke widget heeft ook een gekoppelde naam en een statuswaarde die kan worden gebruikt om aangepaste gegevens op te slaan. In dit voorbeeld declareren we een eenvoudige helperstructuur voor het opslaan van de id, naam en gegevens voor elke vastgemaakte widget. Widgets kunnen zich ook in een actieve status bevinden, die wordt besproken in de sectie Activeren en Deactiveren hieronder. Deze status wordt voor elke widget bijgehouden met een Booleaanse waarde. Voeg de volgende definitie toe aan het bestand WidgetProvider.h, boven de struct-declaratie WidgetProvider .

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

Voeg in de declaratie WidgetProvider in WidgetProvider.h een lid toe voor de kaart die de lijst met ingeschakelde widgets onderhoudt, met behulp van de widget-id als sleutel voor elk item.

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

        

JSON-tekenreeksen voor widgetsjabloon declareren

In dit voorbeeld worden enkele statische tekenreeksen gede declareerd om de JSON-sjablonen voor elke widget te definiëren. Voor het gemak worden deze sjablonen opgeslagen in de lokale variabelen die buiten de widgetproviderklassedefinitie zijn gedeclareerd. Als u een algemene opslag voor de sjablonen nodig hebt, kunnen deze worden opgenomen als onderdeel van het toepassingspakket: Toegang tot pakketbestanden. Zie Een widgetsjabloon maken met adaptieve kaartontwerper voor meer informatie over het maken van het JSON-document voor widgetsjablonen.

In de nieuwste release kunnen apps die Windows-widgets implementeren, de koptekst aanpassen die wordt weergegeven voor hun widget in het widgetsbord, waardoor de standaardpresentatie wordt overschreven. Zie Het koptekstgebied van de widget aanpassen voor meer informatie.

Opmerking

In de nieuwste release kunnen apps die Windows-widgets implementeren ervoor kiezen om de widgetinhoud te vullen met HTML die wordt geleverd vanaf een opgegeven URL in plaats van inhoud op te geven in de schema-indeling van adaptieve kaart in de JSON-nettolading die van de provider aan het widgetsbord wordt doorgegeven. Widget providers moeten nog steeds een JSON-payload van een Adaptive Card opgeven, dus de implementatiestappen in deze instructie zijn geschikt voor webwidgets. Zie Webwidgetproviders voor meer informatie.

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

De IWidgetProvider-methoden implementeren

In de volgende secties implementeren we de methoden van de IWidgetProvider-interface . De helpermethode UpdateWidget die in verschillende van deze methodeimplementaties wordt aangeroepen, wordt verderop in dit artikel weergegeven. Voordat u de interfacemethoden bekijkt, voegt u de volgende regels toe aan WidgetProvider.cpp, na de insluitingsrichtlijnen, om de WIDGET-provider-API's op te halen in de winrt-naamruimte en toegang te verlenen tot de kaart die we in de vorige stap hebben gedeclareerd.

Opmerking

Objecten die worden doorgegeven aan de callback-methoden van de IWidgetProvider-interface , zijn alleen gegarandeerd geldig in de callback. U mag geen verwijzingen naar deze objecten opslaan omdat hun gedrag buiten de context van de callback niet is gedefinieerd.

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

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

CreateWidget

De widget host roept CreateWidget aan wanneer de gebruiker een van de widgets van uw app in de widget host heeft geplaatst. Eerst haalt deze methode de id en naam van de gekoppelde widget op en voegt een nieuw exemplaar van onze helperstructuur, CompactWidgetInfo, toe aan de verzameling ingeschakelde widgets. Vervolgens verzenden we de eerste sjabloon en gegevens voor de widget, die is ingekapseld in de Helper-methode 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);
}

Verwijder Widget

De widgethost roept DeleteWidget aan wanneer de gebruiker een van de widgets van je app heeft losgemaakt van de widgethost. Wanneer dit gebeurt, verwijderen we de bijbehorende widget uit onze lijst met ingeschakelde widgets, zodat we geen verdere updates voor die widget verzenden.

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

OnActionInvoked

De widgethost roept OnActionInvoked aan wanneer de gebruiker communiceert met een actie die u hebt gedefinieerd in uw widgetsjabloon. Voor de tellerwidget die in dit voorbeeld wordt gebruikt, is een actie gedeclareerd met een werkwoord en een waarde van "inc" in de JSON-sjabloon voor de widget. De code van de widgetprovider gebruikt deze werkwoordwaarde om te bepalen welke actie moet worden ondernomen als reactie op de interactie van de gebruiker.

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

Haal in de methode OnActionInvoked de waarde van het werkwoord op door de Verb-eigenschap van de WidgetActionInvokedArgs, die aan de methode is doorgegeven, te controleren. Als het werkwoord 'inc' is, weten we dat we het aantal in de aangepaste status voor de widget gaan verhogen. Haal in de WidgetActionInvokedArgshet WidgetContext-object en vervolgens de WidgetId om de ID van de widget die wordt bijgewerkt op te halen. Zoek de vermelding in de kaart met ingeschakelde widgets met de opgegeven id en werk vervolgens de aangepaste statuswaarde bij die wordt gebruikt om het aantal stappen op te slaan. Werk ten slotte de widgetinhoud bij met de nieuwe waarde met de helperfunctie 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);
        }
    }
}

Zie Action.Execute voor meer informatie over de syntaxis action.execute voor adaptieve kaarten. Zie de richtlijnen voor het ontwerpen van widgetinteractie voor aanwijzingen over het ontwerpen van interactie voor widgets.

OnWidgetContextChanged

In de huidige release wordt OnWidgetContextChanged alleen aangeroepen wanneer de gebruiker de grootte van een vastgemaakte widget wijzigt. U kunt ervoor kiezen om een andere JSON-sjabloon/-gegevens te retourneren aan de widgethost, afhankelijk van de grootte die wordt aangevraagd. U kunt de sjabloon-JSON ook ontwerpen ter ondersteuning van alle beschikbare grootten met behulp van voorwaardelijke rendering op basis van de waarde van host.widgetSize. Als u geen nieuwe sjabloon of gegevens hoeft te verzenden om rekening te houden met de groottewijziging, kunt u de OnWidgetContextChanged gebruiken voor telemetriedoeleinden.

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

Activeren en deactiveren

De methode Activate wordt aangeroepen om de widgetprovider op de hoogte te stellen dat de widgethost momenteel geïnteresseerd is in het ontvangen van bijgewerkte inhoud van de provider. Het kan bijvoorbeeld betekenen dat de gebruiker momenteel actief de widgethost bekijkt. De methode Deactiveren wordt aangeroepen om de widgetprovider op de hoogte te stellen dat de widgethost geen inhoudsupdates meer aanvraagt. Met deze twee methoden wordt een venster gedefinieerd waarin de widget-host vooral geïnteresseerd is in het tonen van de meest actuele up-to-gegevensinhoud. Widgetproviders kunnen op elk gewenst moment updates naar de widget verzenden, zoals in reactie op een pushmelding, maar net als bij elke achtergrondtaak is het belangrijk om de balans op te lossen met up-to-datuminhoud met resourceproblemen zoals de levensduur van de batterij.

Activeren en deactiveren worden per widget aangeroepen. In dit voorbeeld wordt de actieve status van elke widget bijgehouden in de Helper-struct CompactWidgetInfo . In de methode Activate roepen we de Helper-methode UpdateWidget aan om onze widget bij te werken. Houd er rekening mee dat het tijdvenster tussen Activeren en Deactiveren klein kan zijn, dus het is raadzaam dat u het codepad voor het bijwerken van de widget zo snel mogelijk probeert te maken.

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

Een widget bijwerken

Definieer de helpermethode UpdateWidget om een ingeschakelde widget bij te werken. In dit voorbeeld controleren we de naam van de widget in de Helper-struct CompactWidgetInfo die is doorgegeven aan de methode en stellen we vervolgens de juiste sjabloon en gegevens-JSON in op basis van welke widget wordt bijgewerkt. Een WidgetUpdateRequestOptions wordt geïnitialiseerd met de sjabloon, gegevens en aangepaste status voor de widget die wordt bijgewerkt. WidgetManager aanroepen::GetDefault om een exemplaar van de WidgetManager-klasse op te halen en vervolgens UpdateWidget aan te roepen om de bijgewerkte widgetgegevens naar de widgethost te verzenden.

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

Initialiseer de lijst met ingeschakelde widgets bij het opstarten

Wanneer onze widgetprovider voor het eerst wordt geïnitialiseerd, is het een goed idee om WidgetManager te vragen of er actieve widgets zijn die momenteel door onze provider worden geleverd. Het helpt bij het herstellen van de app naar de vorige status in het geval van het opnieuw opstarten van de computer of het vastlopen van de provider. Roep WidgetManager::GetDefault aan om het standaardexemplaar van de widgetmanager voor de app op te halen. Roep vervolgens GetWidgetInfos aan, die een matrix met WidgetInfo-objecten retourneert. Kopieer de widget-id's, namen en aangepaste status in de helper struct CompactWidgetInfo en sla deze op in de variabele RunningWidgets-lid . Plak de volgende code in de constructor voor de widgetproviderklasse .

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

Een klassefactory registreren waarmee WidgetProvider op aanvraag wordt geïnstitualiseerd

Voeg de header die de WidgetProvider klasse definieert toe aan de includes bovenaan het main.cpp-bestand van je app. We zullen hier ook mutex opnemen.

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

Declareer de gebeurtenis die onze app activeert om af te sluiten en de SignalLocalServerShutdown-functie waarmee de gebeurtenis wordt ingesteld. Plak de volgende code in main.cpp.

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

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

Vervolgens moet u een CLSID maken die wordt gebruikt om uw widgetprovider te identificeren voor COM-activering. Genereer een GUID in Visual Studio door naar Hulpmiddelen te gaan en>GUID makente selecteren. Selecteer de optie 'statische const GUID =' en klik op Kopiëren en plak deze in main.cpp. Werk de GUID-definitie bij met de volgende C++/WinRT-syntaxis en stel de naam van de GUID-variabele in widget_provider_clsid. Laat de becommentarieerde versie van de GUID staan, omdat u deze indeling later nodig hebt bij het verpakken van uw app.

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

Voeg de volgende klassefactorydefinitie toe aan main.cpp. Dit is meestal standaardcode die niet specifiek is voor implementaties van widgetproviders. Houd er rekening mee dat CoWaitForMultipleObjects wacht totdat ons afsluitevenement wordt geactiveerd voordat de app wordt afgesloten.

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

Verpak uw widgetprovider-app

In de huidige release kunnen alleen verpakte apps worden geregistreerd als widgetproviders. Met de volgende stappen wordt u begeleid bij het verpakken van uw app en het bijwerken van het app-manifest om uw app te registreren bij het besturingssysteem als widgetprovider.

Een MSIX-pakketproject maken

Klik in Solution Explorer met de rechtermuisknop op uw oplossing en selecteer Add-New> Project.... Selecteer in het dialoogvenster Een nieuw project toevoegen de sjabloon Windows Application Packaging Project en klik op Volgende. Stel de projectnaam in op "ExampleWidgetProviderPackage" en klik op Creëren. Wanneer u hierom wordt gevraagd, stelt u de doelversie in op versie 1809 of hoger en klikt u op OK. Klik vervolgens met de rechtermuisknop op het project ExampleWidgetProviderPackage en selecteer >een projectverwijzingtoevoegen. Selecteer het project ExampleWidgetProvider en klik op OK.

Windows App SDK-pakketreferentie toevoegen aan het pakketproject

U moet een verwijzing toevoegen naar het NuGet-pakket van de Windows App SDK aan het MSIX-verpakkingsproject. Dubbelklik in Solution Explorer op het project ExampleWidgetProviderPackage om het bestand ExampleWidgetProviderPackage.wapproj te openen. Voeg de volgende XML toe in het Project-element .

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

Opmerking

Zorg ervoor dat de versie die is opgegeven in het element PackageReference overeenkomt met de meest recente stabiele versie waarnaar u in de vorige stap hebt verwezen.

Als de juiste versie van de Windows App SDK al op de computer is geïnstalleerd en u de SDK-runtime niet in uw pakket wilt bundelen, kunt u de pakketafhankelijkheid opgeven in het bestand Package.appxmanifest voor het project 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>
...

Het pakketmanifest bijwerken

Klik in Solution Explorer met de rechtermuisknop op het Package.appxmanifest bestand en selecteer Code weergeven om het XML-manifestbestand te openen. Vervolgens moet u enkele naamruimtedeclaraties toevoegen voor de app-pakketextensies die we gaan gebruiken. Voeg de volgende naamruimtedefinities toe aan het element Package op het hoogste niveau.

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

Maak in het toepassingselement een nieuw leeg element met de naam Extensies. Zorg ervoor dat dit komt na de afsluitende tag voor uap:VisualElements.

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

    </Extensions>
</Application>

De eerste extensie die we moeten toevoegen, is de ComServer-extensie . Hiermee wordt het toegangspunt van het uitvoerbare bestand geregistreerd bij het besturingssysteem. Deze extensie is het pakket-app-equivalent van het registreren van een COM-server door een registersleutel in te stellen en is niet specifiek voor widgetproviders. Voeg het volgende com:Extension-element toe als onderliggend element van het element Extensies . Wijzig de GUID in het kenmerk -id van het element com:Class naar de GUID die u hebt gegenereerd in een vorige stap.

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

Voeg vervolgens de extensie toe die de app registreert als widgetprovider. Plak het element uap3:Extension in het volgende codefragment als een onderliggend element van het element Extensions . Vervang het kenmerk ClassId van het COM-element door de GUID die u in de vorige stappen hebt gebruikt.

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

Zie de widgetprovider pakketmanifest XML-indeling voor gedetailleerde beschrijvingen en indelingen voor al deze elementen.

Pictogrammen en andere afbeeldingen toevoegen aan uw verpakkingsproject

Klik in Solution Explorer-met de rechtermuisknop op uw ExampleWidgetProviderPackage en selecteer Add->New Folder. Noem deze map ProviderAssets, omdat dit is wat is gebruikt in de Package.appxmanifest vorige stap. Hier slaan we onze pictogrammen en schermopnamen op voor onze widgets. Nadat u de gewenste pictogrammen en schermopnamen hebt toegevoegd, moet u ervoor zorgen dat de namen van de afbeeldingen overeenkomen met wat er na Path=ProviderAssets\ komt in uw specifieke bestand, anders zullen de widgets niet worden getoond in de widgethost.

Zie Integreren met de widgetkiezer voor informatie over de ontwerpvereisten voor schermafbeeldingen en de naamconventies voor gelokaliseerde schermopnamen.

Uw widgetprovider testen

Zorg ervoor dat u de architectuur hebt geselecteerd die overeenkomt met uw ontwikkelcomputer in de vervolgkeuzelijst Oplossingsplatformen , bijvoorbeeld 'x64'. Klik in Solution Explorer met de rechtermuisknop op uw oplossing en selecteer Oplossing bouwen. Zodra dit is gebeurd, klikt u met de rechtermuisknop op uw ExampleWidgetProviderPackage en selecteert u Implementeren. In de huidige release is de enige ondersteunde widgethost het widgetsbord. Als u de widgets wilt zien, moet u het widgetsbord openen en widgets in de rechterbovenhoek selecteren. Scroll naar de onderkant van de beschikbare widgets-lijst en u zou de mockup Weather Widget en Microsoft Counting Widget moeten zien die in deze tutorial zijn gemaakt. Klik op de widgets om ze aan het widgetsbord vast te maken en hun functionaliteit te testen.

Foutopsporing voor uw widgetprovider

Nadat u uw widgets hebt vastgemaakt, start het widgetplatform uw widgetprovidertoepassing om relevante informatie over de widget te ontvangen en te verzenden. Als u fouten wilt opsporen in de actieve widget, kunt u een foutopsporingsprogramma toevoegen aan de toepassing van de actieve widgetprovider of u kunt Visual Studio instellen om automatisch de foutopsporing van het widgetproviderproces te starten zodra deze is gestart.

Om u aan het lopende proces te koppelen:

  1. Klik in Visual Studio op Debuggen -> Koppelen aan proces.
  2. Filter de processen en zoek de gewenste widgetprovidertoepassing.
  3. Voeg het foutopsporingsprogramma toe.

Als u het foutopsporingsprogramma automatisch wilt koppelen aan het proces wanneer het in eerste instantie wordt gestart:

  1. Klik in Visual Studio op Foutopsporing -> Andere foutopsporingsdoelen -> Gedebugged app-pakket.
  2. Filter de pakketten en zoek het gewenste widgetproviderpakket.
  3. Selecteer het en vink het vakje aan met de tekst 'Niet starten, maar debug mijn code wanneer het start.'
  4. Klik op ombij te voegen.

Uw console-app converteren naar een Windows-app

De console-app die in deze walkthrough is gemaakt omzetten naar een Windows-app:

  1. Klik met de rechtermuisknop op het project ExampleWidgetProvider in Solution Explorer en selecteer Eigenschappen. Navigeer naar Linker -> Systeem en wijzig SubSystem van 'Console' in 'Windows'. U kunt dit ook doen door <SubSystem>Windows</SubSystem> toe te voegen aan de <Link>..<sectie /Koppeling> van de .vcxproj.
  2. Wijzig in main.cpp int main() naar int WINAPI wWinMain(_In_ HINSTANCE /*hInstance*/, _In_opt_ HINSTANCE /*hPrevInstance*/, _In_ PWSTR pCmdLine, _In_ int /*nCmdShow*/).

Een schermopname van de projecteigenschappen van de C++-widgetprovider met het uitvoertype ingesteld op Windows-toepassing

Uw widget publiceren

Nadat u uw widget hebt ontwikkeld en getest, kunt u uw app publiceren in de Microsoft Store, zodat gebruikers uw widgets op hun apparaten kunnen installeren. Zie Uw app publiceren in de Microsoft Store voor stapsgewijze instructies voor het publiceren van een app.

De widget winkelcollectie

Nadat uw app is gepubliceerd in de Microsoft Store, kunt u aanvragen dat uw app wordt opgenomen in de Store-verzameling widgets waarmee gebruikers apps met Windows Widgets kunnen detecteren. Als u uw aanvraag wilt indienen, raadpleegt u Uw widgetgegevens indienen voor toevoeging aan dewinkelcollectie.

Schermopname van de Microsoft Store met de widgetsverzameling waarmee gebruikers apps kunnen detecteren die Windows Widgets bevatten.

Het aanpassen van widgets implementeren

Vanaf Windows App SDK 1.4 kunnen widgets gebruikersaanpassing ondersteunen. Wanneer deze functie is geïmplementeerd, wordt er een Aanpassen widget optie toegevoegd aan het beletseltekenmenu boven de Losmaken widget optie.

Een schermopname van een widget waarin het aanpassingsdialoogvenster wordt weergegeven.

De volgende stappen geven een overzicht van het proces voor het aanpassen van widget's.

  1. In normale werking reageert de widgetprovider op aanvragen van de widgethost met de JSON-sjablonen voor visuals en gegevens voor de reguliere widgetervaring.
  2. De gebruiker klikt op de knop Widget aanpassen in het menu met het beletselteken.
  3. De widget verhoogt de gebeurtenis OnCustomizationRequested op de widgetprovider om aan te geven dat de gebruiker de aanpassingservaring van de widget heeft aangevraagd.
  4. De widgetprovider stelt een interne vlag in om aan te geven dat de widget zich in de aanpassingsmodus bevindt. In de aanpassingsmodus verzendt de widgetprovider de JSON-sjablonen voor de gebruikersinterface voor widgetaanpassing in plaats van de reguliere widgetgebruikersinterface.
  5. In de aanpassingsmodus ontvangt de widgetprovider OnActionInvoked-gebeurtenissen wanneer de gebruiker communiceert met de aanpassingsgebruikersinterface en de interne configuratie en het gedrag ervan aanpast op basis van de acties van de gebruiker.
  6. Wanneer de actie die is gekoppeld aan de gebeurtenis OnActionInvoked de door de app gedefinieerde actie 'aanpassing afsluiten' is, stelt de widgetprovider de interne vlag opnieuw in om aan te geven dat deze zich niet meer in de aanpassingsmodus bevindt en het verzenden van de JSON-sjablonen voor visuals en gegevens hervat voor de normale widgetervaring, waarbij de wijzigingen worden weergegeven die tijdens de aanpassing zijn aangevraagd.
  7. De widgetprovider behoudt de aanpassingsopties op schijf of de cloud, zodat de wijzigingen behouden blijven tussen aanroepen van de widgetprovider.

Opmerking

Er is een bekende fout met het Windows Widget Board, voor widgets die zijn gebouwd met de Windows App SDK, waardoor het beletseltekenmenu niet meer reageert nadat de aanpassingskaart is weergegeven.

In typische scenario's voor het aanpassen van widget kiest de gebruiker welke gegevens worden weergegeven in de widget of past de visuele presentatie van de widget aan. Ter vereenvoudiging voegt het voorbeeld in deze sectie aanpassingsgedrag toe waarmee de gebruiker de teller van de telwidget die in de vorige stappen is geïmplementeerd, opnieuw kan instellen.

Opmerking

Widgetaanpassing wordt alleen ondersteund in Windows App SDK 1.4 en hoger. Zorg ervoor dat u de verwijzingen in uw project bijwerkt naar de nieuwste versie van het Nuget-pakket.

Het pakketmanifest bijwerken om aanpassingsondersteuning te declareren

Als u de widgethost wilt laten weten dat de widget aanpassing ondersteunt, voegt u het kenmerk IsCustomizable toe aan de definitie-eleent voor de widget en stelt u deze in op waar.

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

WidgetProvider.h bijwerken

Als u aanpassingsondersteuning wilt toevoegen aan de widget die in de vorige stappen in dit artikel is gemaakt, moet u het headerbestand voor onze widgetprovider WidgetProvider.h bijwerken.

Werk eerst de definitie van CompactWidgetInfo bij. Deze helper-struct helpt ons bij het bijhouden van de huidige status van onze actieve widgets. Voeg het veld inCustomisatie toe, dat wordt gebruikt om bij te houden wanneer de widgethost verwacht dat we onze JSON-sjabloon voor aanpassing verzenden in plaats van de reguliere widgetsjabloon.

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

Werk de WidgetProvider-declaratie bij om de IWidgetProvider2-interface te implementeren.

// WidgetProvider.h

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

Voeg een declaratie toe voor de OnCustomizationRequested callback van de IWidgetProvider2-interface .

// WidgetProvider.h

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

Declareer ten slotte een tekenreeksvariabele die de JSON-sjabloon definieert voor de ui voor het aanpassen van de widget. In dit voorbeeld hebben we de knop 'Teller opnieuw instellen' en de knop 'Aanpassing afsluiten,' waarmee onze provider wordt gesignaleerd om terug te keren naar het normale widgetgedrag.

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

WidgetProvider.cpp bijwerken

Werk nu het WidgetProvider.cpp-bestand bij om het aanpassingsgedrag van de widget te implementeren. Deze methode maakt gebruik van hetzelfde patroon als de andere callbacks die we hebben gebruikt. We krijgen de id voor de widget die moet worden aangepast vanuit widgetContext en zoeken de Helper-struct compactWidgetInfo die aan die widget is gekoppeld en stel het veld inCustomisatie in op true.

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

Vervolgens werken we de Helpermethode UpdateWidget bij waarmee onze gegevens en visuele JSON-sjablonen naar de widgethost worden verzonden. Wanneer we de widget tellen bijwerken, verzenden we de reguliere widgetsjabloon of de aanpassingssjabloon, afhankelijk van de waarde van het veld InCustomisatie . Voor kortheid wordt code die niet relevant is voor aanpassing weggelaten in dit codefragment.

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

Wanneer gebruikers interactie hebben met invoer in onze aanpassingssjabloon, wordt dezelfde OnActionInvoked-handler aanroepen als wanneer de gebruiker communiceert met de reguliere widgetervaring. Ter ondersteuning van aanpassingen zoeken we naar de werkwoorden 'reset' en 'exitCustomization' in onze JSON-sjabloon voor aanpassing. Als de actie voor de knop 'Teller opnieuw instellen' is, stellen we de teller in het veld customState van onze helper-struct opnieuw in op 0. Als de actie voor de knop Aangepaste modus afsluiten is, stellen we het inCustomization-veld in op onwaar, zodat wanneer we UpdateWidget aanroepen, onze helpermethode de reguliere JSON-sjablonen verzendt en niet de aanpassingssjabloon.

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

Wanneer u uw widget implementeert, ziet u nu de knop Widget aanpassen in het menu met weglatingstekens. Als u op de knop Aanpassen klikt, wordt uw aanpassingssjabloon weergegeven.

Een schermopname van de ui voor het aanpassen van widgets.

Klik op de knop Teller opnieuw instellen om de teller opnieuw in te stellen op 0. Klik op de knop Aanpassing afsluiten om terug te keren naar het normale gedrag van uw widget.