Teilen über


Implementieren eines Widgetanbieters in einer Win32-App (C++/WinRT)

Dieser Artikel führt Sie durch die Erstellung eines einfachen Widgetanbieters, der die Schnittstelle IWidgetProvider implementiert. Die Methoden dieser Schnittstelle werden vom Widgethost aufgerufen, um die Daten anzufordern, die ein Widget definieren, oder um den Widgetanbieter auf eine Benutzeraktion für ein Widget reagieren zu lassen. Widgetanbieter können ein einzelnes Widget oder mehrere Widgets unterstützen. In diesem Beispiel werden zwei verschiedene Widgets definiert. Ein Widget ist ein simuliertes Wetterwidget, das einige der vom Framework für adaptive Karten bereitgestellten Formatierungsoptionen veranschaulicht. Das zweite Widget veranschaulicht Benutzeraktionen und das Feature für den benutzerdefinierten Widgetzustand durch Verwalten eines Leistungsindikators, der sich immer dann erhöht, wenn der Benutzer auf eine Schaltfläche klickt, die im Widget angezeigt wird.

Screenshot eines einfachen Wetter-Widgets. Das Widget zeigt einige wetterbezogene Grafiken und Daten sowie einige Diagnosetexte an, die zeigen, dass die Vorlage für das mittlere Widget angezeigt wird.

Screenshot eines einfachen Zählwidgets. Das Widget zeigt eine Zeichenkette mit dem numerischen Wert, der erhöht wird, und eine Schaltfläche mit der Beschriftung „Inkrement“ sowie einen Diagnosetext, der zeigt, dass die Vorlage für das kleine Widget angezeigt wird.

Der Beispielcode in diesem Artikel basiert auf dem Beispiel für Windows App SDK-Widgets. Informationen zum Implementieren eines Widgetanbieters mithilfe von C# finden Sie unter Implementieren eines Widgetanbieters in einer Win32-App (C#).

Voraussetzungen

  • Auf Ihrem Gerät muss der Entwicklermodus aktiviert sein. Weitere Informationen finden Sie unter Aktivieren von Geräten für die Entwicklung.
  • Visual Studio 2022 oder höher, mit der Workload für die UWP-Entwicklung. Stellen Sie sicher, dass Sie die Komponente für C++ (v143) aus der optionalen Dropdownliste hinzufügen.

Erstellen einer neuen Win32-Konsolen-App in C++/WinRT

Erstellen Sie in Visual Studio ein neues Projekt. Legen Sie im Dialogfeld Neues Projekt erstellen den Sprachfilter auf „C++“ und den Plattformfilter auf „Windows“ fest, und wählen Sie dann die Projektvorlage „Windows-Konsolenanwendung (C++/WinRT)“ aus. Nennen Sie das neue Projekt „ExampleWidgetProvider“. Wenn Sie dazu aufgefordert werden, legen Sie die Windows-Version für die App auf Version 1809 oder höher fest.

Hinzufügen von Verweisen auf die NuGet-Pakete des Windows App SDK und der Windows-Implementierungsbibliothek

In diesem Beispiel wird das neueste stabile NuGet-Paket der Windows App SDK verwendet. Klicken Sie in Projektmappen-Explorer mit der rechten Maustaste auf Verweise, und wählen Sie NuGet-Pakete verwalten... aus. Wählen Sie im NuGet-Paket-Manager die Registerkarte Durchsuchen aus, und suchen Sie nach „Microsoft.WindowsAppSDK“. Wählen Sie in der Dropdownliste Version die neueste stabile Version aus, und klicken Sie dann auf Installieren.

In diesem Beispiel wird auch das NuGet-Paket der Windows-Implementierungsbibliothek verwendet. Klicken Sie in Projektmappen-Explorer mit der rechten Maustaste auf Verweise, und wählen Sie NuGet-Pakete verwalten... aus. Wählen Sie im NuGet-Paket-Manager die Registerkarte Durchsuchen aus, und suchen Sie nach „Microsoft.Windows.ImplementationLibrary“. Wählen Sie in der Dropdownliste Version die neueste Version aus, und klicken Sie dann auf Installieren.

Fügen Sie in der vorkompilierten Headerdatei „pch.h“ die folgenden include-Anweisungen hinzu.

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

Hinweis

Sie müssen zuerst den wil/cppwinrt.h-Header vor allen WinRT-Headern angeben.

Um das Herunterfahren der Widgetanbieter-App ordnungsgemäß zu behandeln, wird eine benutzerdefinierte Implementierung von winrt::get_module_lock benötigt. Die in der Datei „main.cpp“ definierte SignalLocalServerShutdown-Methode wird vorab deklariert, und ein Ereignis wird festgelegt, das signalisiert, dass die App beendet werden soll. Fügen Sie der Datei „pch.h“ direkt unterhalb der #pragma once-Direktive vor den anderen include-Anweisungen den folgenden Code hinzu.

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

Hinzufügen einer WidgetProvider-Klasse zum Verarbeiten von Widgetvorgängen

Klicken Sie in Visual Studio im Projektmappen-Explorer mit der rechten Maustaste auf das Projekt ExampleWidgetProvider, und wählen Sie Hinzufügen -> Klasse aus. Nennen Sie die Klasse im Dialogfeld Klasse hinzufügen „WidgetProvider“, und klicke dann auf Hinzufügen.

Deklarieren einer Klasse, die die IWidgetProvider-Schnittstelle implementiert

Die IWidgetProvider-Schnittstelle definiert Methoden, die der Widgethost aufruft, um Vorgänge mit dem Widgetanbieter zu initiieren. Ersetzen Sie die leere Klassendefinition in der Datei „WidgetProvider.h“ durch den folgenden Code. Dieser Code deklariert eine Struktur, die die IWidgetProvider-Schnittstelle implementiert, sowie Prototypen für die Schnittstellenmethoden.

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

    
};

Fügen Sie außerdem die private Methode UpdateWidget hinzu, bei der es sich um eine Hilfsmethode handelt, die Updates vom Anbieter an den Widgethost sendet.

// WidgetProvider.h
private: 

void UpdateWidget(CompactWidgetInfo const& localWidgetInfo);

Vorbereiten der Nachverfolgung aktivierter Widgets

Ein Widgetanbieter kann ein einzelnes Widget oder mehrere Widgets unterstützen. Wann immer der Widgethost einen Vorgang mit dem Widgetanbieter initiiert, übergibt er eine ID, um das dem Vorgang zugeordnete Widget zu identifizieren. Jedes Widget verfügt außerdem über einen zugeordneten Namen und einen Zustandswert, der zum Speichern benutzerdefinierter Daten verwendet werden kann. In diesem Beispiel wird eine einfache Hilfsstruktur zum Speichern von ID, Namen und Daten für jedes angeheftete Widget deklariert. Widgets können sich auch im aktiven Zustand befinden. Dies wird im Abschnitt Aktivieren und Deaktivieren weiter unten erläutert. Dieser Zustand wird für jedes Widget mit einem booleschen Wert nachverfolgt. Fügen Sie der Datei „WidgetProvider.h“ oberhalb der WidgetProvider-Strukturdeklaration die folgende Definition hinzu.

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

Fügen Sie in der WidgetProvider-Klassendeklaration in „WidgetProvider.h“ ein Member für die Zuordnung hinzu, das die Liste der aktivierten Widgets verwaltet, wobei die Widget-ID als Schlüssel für jeden Eintrag verwendet wird.

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

        

Deklarieren von JSON-Zeichenfolgen für Widgetvorlagen

In diesem Beispiel werden einige statische Zeichenfolgen deklariert, um die JSON-Vorlagen für jedes Widget zu definieren. Zur Vereinfachung werden diese Vorlagen in den lokalen Variablen gespeichert, die außerhalb der WidgetProvider-Klassendefinition deklariert wurden. Wenn Sie einen allgemeinen Speicher für die Vorlagen benötigen, können sie als Teil des Anwendungspakets einbezogen werden: Zugreifen auf Paketdateien. Informationen zum Erstellen des JSON-Dokuments für Widgetvorlage finden Sie unter Erstellen einer Widgetvorlage mit dem Designer für adaptive Karten.

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

Implementieren der IWidgetProvider-Methoden

In den nächsten Abschnitten wird die Methoden der IWidgetProvider-Schnittstelle implementiert. Die in mehreren dieser Methodenimplementierungen aufgerufene Hilfsmethode UpdateWidget wird weiter unten in diesem Artikel gezeigt. Bevor Sie sich mit den Schnittstellenmethoden vertraut machen, fügen Sie die folgenden Zeilen nach den include-Anweisungen zu WidgetProvider.cpp hinzu, um die Widgetanbieter-APIs in den winrt-Namespace zu pullen und den Zugriff auf die Karte zuzulassen, die im vorherigen Schritt deklariert wurden.

Hinweis

Objekte, die an die Rückrufmethoden der IWidgetProvider-Schnittstelle übergeben werden, sind nur innerhalb des Rückrufs gültig. Sie sollten keine Verweise auf diese Objekte speichern, da ihr Verhalten außerhalb des Kontexts des Rückrufs nicht definiert ist.

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

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

CreateWidget

Der Widgethost ruft CreateWidget auf, wenn der Benutzer eines der Widgets Ihrer App im Widgethost angeheftet hat. Zunächst ruft diese Methode die ID und den Namen des zugeordneten Widgets ab und fügt der Auflistung aktivierter Widgets eine neue Instanz der Hilfsstruktur CompactWidgetInfo hinzu. Als Nächstes werden die anfängliche Vorlage und die Daten für das Widget gesendet, das in der UpdateWidget-Hilfsmethode gekapselt ist.

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

DeleteWidget

Der Widgethost ruft DeleteWidget auf, wenn der Benutzer eines der angehefteten Widgets Ihrer App aus dem Widgethost löst. In diesem Fall wird das zugeordnete Widget aus der Liste der aktivierten Widgets entfernt, sodass keine weiteren Updates für dieses Widget gesendet werden.

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

OnActionInvoked

Der Widgethost ruft OnActionInvoked auf, wenn der Benutzer mit einer Aktion interagiert, die Sie in Ihrer Widgetvorlage definiert haben. Für das in diesem Beispiel verwendete Leistungsindikatorwidget wurde in der JSON-Vorlage für das Widget eine Aktion mit dem Verb-Wert „inc“ deklariert. Der Code des Widgetanbieters verwendet diesen Verb-Wert, um zu bestimmen, welche Aktion als Reaktion auf die Benutzerinteraktion ausgeführt werden soll.

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

Rufen Sie in der Methode OnActionInvoked den Verbwert ab, indem Sie die Eigenschaft Verb der an die Methode übergebenen WidgetActionInvokedArgs-Werte überprüfen. Wenn das Verb „inc“ ist, wissen wir, dass die Zahl im benutzerdefinierten Zustand für das Widget erhöht wird. Rufen Sie aus WidgetActionInvokedArgs das WidgetContext-Objekt und dann die WidgetId ab, um die ID für das Widget abzurufen, das aktualisiert wird. Suchen Sie in der Zuordnung aktivierter Widgets den Eintrag mit der angegebenen ID, und aktualisieren Sie dann den benutzerdefinierten Zustandswert, der zum Speichern der Anzahl von Inkrementen verwendet wird. Aktualisieren Sie schließlich den Widgetinhalt mit dem neuen Wert unter Verwendung der Hilfsfunktion 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);
        }
    }
}

Informationen zur Action.Execute-Syntax für adaptive Karten finden Sie unter Action.Execute. Anleitungen zum Entwerfen von Interaktionen für Widgets finden Sie unter Entwurfsleitfaden für Widgetinteraktionen.

OnWidgetContextChanged

Im aktuellen Release wird OnWidgetContextChanged nur aufgerufen, wenn der Benutzer die Größe eines angehefteten Widgets ändert. Sie können je nach angeforderter Größe eine andere JSON-Vorlage bzw. andere JSON-Daten an den Widgethost zurückgeben. Sie können die JSON-Vorlage auch so entwerfen, dass alle verfügbaren Größen unterstützt werden, indem Sie das bedingte Rendering basierend auf dem Wert von host.widgetSize verwenden. Wenn Sie keine neue Vorlage und keine neuen Daten senden müssen, um die Größenänderung zu berücksichtigen, können Sie OnWidgetContextChanged für Telemetriezwecke verwenden.

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

Aktivieren und Deaktivieren

Die Activate-Methode wird aufgerufen, um den Widgetanbieter darüber zu informieren, dass der Widgethost derzeit aktualisierte Inhalte vom Anbieter erhalten möchte. Dies kann beispielsweise bedeuten, dass der Benutzer den Widgethost gerade aktiv anzeigt. Die Deactivate-Methode wird aufgerufen, um den Widgetanbieter darüber zu informieren, dass der Widgethost keine Inhaltsupdates mehr anfordert. Diese beiden Methoden definieren ein Fenster, bei dem das größte Interesse des Widgethosts darin besteht, den aktuellsten Inhalt anzuzeigen. Widgetanbieter können jederzeit Updates an das Widget senden, z. B. als Reaktion auf eine Pushbenachrichtigung. Wie bei jeder Hintergrundaufgabe ist es jedoch wichtig, die Bereitstellung aktueller Inhalte gegen Ressourcenbedenken wie Akkulaufzeit abzuwägen.

Aktivieren und Deaktivieren werden pro Widget aufgerufen. In diesem Beispiel wird der aktive Status jedes Widgets in der CompactWidgetInfo-Hilfsstruktur nachverfolgt. In der Activate-Methode wird die UpdateWidget-Hilfsmethode aufgerufen, um das Widget zu aktualisieren. Beachten Sie, dass das Zeitfenster zwischen Aktivieren und Deaktivieren klein sein kann. Daher wird empfohlen, den Codepfad für die Widgetaktualisierung so schnell wie möglich festzulegen.

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

Aktualisieren eines Widgets

Definieren Sie die UpdateWidget-Hilfsmethode zum Aktualisieren eines aktivierten Widgets. In diesem Beispiel wird der Name des Widgets in der an die Methode übergebenen CompactWidgetInfo-Hilfsstruktur überprüft und dann die entsprechende Vorlage und der JSON-Datencode basierend auf dem aktualisierten Widget festgelegt. Ein WidgetUpdateRequestOptions-Objekt wird mit der Vorlage, den Daten und dem benutzerdefinierten Zustand für das zu aktualisierende Widget initialisiert. Rufen Sie WidgetManager::GetDefault auf, um eine Instanz der WidgetManager-Klasse abzurufen, und rufen Sie dann UpdateWidget auf, um die aktualisierten Widgetdaten an den Widgethost zu senden.

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

Initialisieren der Liste der aktivierten Widgets beim Start

Wenn der Widgetanbieter zum ersten Mal initialisiert wird, empfiehlt es sich, WidgetManager zu fragen, ob aktive Widgets vorhanden sind, die aktuell vom Anbieter bedient werden. Dies hilft dabei, die App im Falle eines Neustarts des Computers oder eines Absturzes des Anbieters wieder in den vorherigen Zustand zu versetzen. Rufen Sie WidgetManager::GetDefault auf, um die Widget-Manager-Standardinstanz für die App abzurufen. Rufen Sie dann GetWidgetInfos auf, wodurch ein Array von WidgetInfo-Objekten zurückgegeben wird. Kopieren Sie die Widget-IDs, die Namen und den benutzerdefinierten Zustand in die Hilfsstruktur CompactWidgetInfo, und speichern Sie sie in der Membervariablen RunningWidgets. Fügen Sie den folgenden Code in den Konstruktor für die WidgetProvider-Klasse ein.

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

Registrieren einer Klassenfactory, die WidgetProvider bei Bedarf instanziiert

Fügen Sie den Header, der die WidgetProvider-Klasse definiert, zu den include-Anweisungen am Anfang der main.cpp-Datei Ihrer App hinzu. Hier wird auch mutex eingefügt.

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

Deklarieren Sie das Ereignis, das das Beenden der App auslöst, und die SignalLocalServerShutdown-Funktion, die das Ereignis festlegt. Fügen Sie den folgenden Code in die Datei „main.cpp“ ein.

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

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

Als Nächstes müssen Sie eine CLSID erstellen, die verwendet wird, um Ihren Widgetanbieter für die COM-Aktivierung zu identifizieren. Generieren Sie eine GUID in Visual Studio, indem Sie zu Tools -> GUID erstellen wechseln. Wählen Sie die Option „static const GUID =“ aus, klicken Sie auf Kopieren, und fügen Sie sie dann in main.cpp ein. Aktualisieren Sie die GUID-Definition mit der folgenden C++/WinRT-Syntax, und legen Sie den GUID-Variablennamen auf „widget_provider_clsid“ fest. Behalten Sie die kommentierte Version der GUID bei, da Sie dieses Format später beim Packen Ihrer App benötigen.

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

Fügen Sie die folgende Klassenfactorydefinition zu main.cpp hinzu. Dabei handelt es sich hauptsächlich um Bausteincode, der nicht spezifisch für Widgetanbieterimplementierungen bestimmt ist. Beachten Sie, dass CoWaitForMultipleObjects wartet, bis das Herunterfahren-Ereignis ausgelöst wird, bevor die App beendet wird.

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

Packen Ihrer Widgetanbieter-App

Im aktuellen Release können nur gepackte Apps als Widgetanbieter registriert werden. Die folgenden Schritte führen Sie durch den Vorgang zum Packen Ihrer App und zum Aktualisieren des App-Manifests, um Ihre App beim Betriebssystem als Widgetanbieter zu registrieren.

Erstellen eines MSIX-Paketerstellungsprojekts

Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf Ihre Projektmappe, und wählen Sie Hinzufügen -> Neues Projekt... aus. Wählen Sie im Dialogfeld Neues Projekt hinzufügen die Vorlage „Paketerstellungsprojekt für Windows-Anwendungen“ aus, und klicken Sie auf Weiter. Legen Sie den Projektnamen auf „ExampleWidgetProviderPackage“ fest, und klicken Sie auf Erstellen. Wenn Sie dazu aufgefordert werden, legen Sie die Zielversion auf Version 1809 oder höher fest, und klicken Sie auf OK. Klicken Sie als Nächstes mit der rechten Maustaste auf das Projekt „ExampleWidgetProviderPackage“, und wählen Sie Hinzufügen -> Projektverweis aus. Wählen Sie das Projekt ExampleWidgetProvider aus, und klicken Sie auf „OK“.

Hinzufügen eines Windows App SDK-Paketverweises zum Paketprojekt

Sie müssen dem MSIX-Paketprojekt einen Verweis auf das NuGet-Paket des Windows App SDK hinzufügen. Doppelklicken Sie im Projektmappen-Explorer auf das Projekt „ExampleWidgetProviderPackage“, um die Datei „ExampleWidgetProviderPackage.wapproj“ zu öffnen. Fügen Sie im Project-Element den folgenden XML-Code hinzu.

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

Hinweis

Stellen Sie sicher, dass die im PackageReference-Element angegebene Version mit der neuesten stabilen Version übereinstimmt, auf die Sie im vorherigen Schritt verwiesen haben.

Wenn die richtige Version des Windows App SDK bereits auf dem Computer installiert ist und Sie die SDK-Runtime nicht in Ihrem Paket bündeln möchten, können Sie die Paketabhängigkeit in der Datei „Package.appxmanifest“ für das Projekt „ExampleWidgetProviderPackage“ angeben.

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

Aktualisieren des Paketmanifests

Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf die Datei Package.appxmanifest, und wählen Sie Code anzeigen aus, um die XML-Datei des Manifests zu öffnen. Als Nächstes müssen Sie einige Namespacedeklarationen für die verwendeten App-Paketerweiterungen hinzufügen. Fügen Sie dem Package-Element der obersten Ebene die folgenden Namespacedefinitionen hinzu.

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

Erstellen Sie im Application-Element ein neues leeres Element namens Extensions. Stellen Sie sicher, dass Sie dieses Element nach dem schließenden Tag für uap:VisualElements angeben.

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

    </Extensions>
</Application>

Die erste Erweiterung, die hinzugefügt werden muss, ist die ComServer-Erweiterung. Dadurch wird der Einstiegspunkt der ausführbaren Datei beim Betriebssystem registriert. Diese Erweiterung ist das Paket-App-Äquivalent zur Registrierung eines COM-Servers durch Festlegen eines Registrierungsschlüssels und nicht spezifisch für Widget-Anbieter. Fügen Sie das folgende com:Extension-Element als untergeordnetes Element des Elements Extensions hinzu. Ändern Sie die GUID im Id-Attribut des com:Class-Elements in die GUID, die Sie in einem vorherigen Schritt generiert haben.

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

Fügen Sie als Nächstes die Erweiterung hinzu, die die App als Widgetanbieter registriert. Fügen Sie das uap3:Extension-Element als untergeordnetes Element des Extensions-Elements in den folgenden Codeausschnitt ein. Ersetzen Sie das ClassId-Attribut des COM-Elements durch die GUID, die Sie in den vorherigen Schritten verwendet haben.

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

Ausführliche Beschreibungen und Formatinformationen für all diese Elemente finden Sie unter XML-Format des Widgetanbieter-Paketmanifests.

Hinzufügen von Symbolen und anderen Bildern zu Ihrem Paketerstellungsprojekt

Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf Ihr ExampleWidgetProviderPackage, und wählen Sie Hinzufügen -> Neuer Ordner aus. Nennen Sie diesen Ordner „ProviderAssets“, da dies das ist, was im Package.appxmanifest im vorherigen Schritt verwendet wurde. Hier werden Symbole und Screenshots für die Widgets gespeichert. Nachdem Sie die gewünschten Symbole und Screenshots hinzugefügt haben, stellen Sie sicher, dass die Bildnamen mit dem übereinstimmen, was nach Path=ProviderAssets\ in Ihrem Package.appxmanifest steht, oder die Widgets werden nicht im Widgethost angezeigt.

Informationen zu den Entwurfsanforderungen für Screenshots und den Benennungskonventionen für lokalisierte Screenshots finden Sie unter Integrieren in die Widgetauswahl.

Testen Ihres Widgetanbieters

Stellen Sie sicher, dass Sie in der Dropdownliste Projektmappenplattformen die Architektur ausgewählt haben, die Ihrem Entwicklungscomputer entspricht, z. B. „x64“. Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf die Projektmappe, und wählen Sie Projektmappe erstellen aus. Klicken Sie anschließend mit der rechten Maustaste auf Ihr ExampleWidgetProviderPackage, und wählen Sie Bereitstellen aus. Im aktuellen Release ist der einzige unterstützte Widgethost das Widgets Board. Um die Widgets anzuzeigen, müssen Sie das Widgets Board öffnen und oben rechts Widgets hinzufügen auswählen. Scrollen Sie in den verfügbaren Widgets nach unten. Sie sollten das simulierte Wetterwidget und das Microsoft-Zählwidget sehen, die in diesem Tutorial erstellt wurden. Klicken Sie auf die Widgets, um sie an Ihr Widgets Board anzuheften und ihre Funktionalität zu testen.

Debuggen Ihres Widgetanbieters

Nachdem Sie Ihre Widgets angeheftet haben, startet die Widgetplattform Ihre Widgetanbieteranwendung, um relevante Informationen über das Widget zu empfangen und zu senden. Zum Debuggen des ausgeführten Widgets können Sie entweder einen Debugger an die ausgeführte Widgetanbieteranwendung anfügen oder Visual Studio so einrichten, dass das Debuggen des Widgetanbieterprozesses nach dem Start automatisch gestartet wird.

Zum Anfügen an den ausgeführten Prozess:

  1. Klicken Sie in Visual Studio auf Debuggen -> An den Prozess anhängen.
  2. Filtern Sie die Prozesse, und suchen Sie ihre gewünschte Widgetanbieteranwendung.
  3. Fügen Sie den Debugger an.

Zum Anfügen des Debuggers an den Prozess beim erstmaligen Start:

  1. Klicken Sie in Visual Studio auf Debuggen -> Andere Debugziele -> Installiertes App-Paket debuggen aus.
  2. Filtern Sie die Pakete, und suchen Sie ihr gewünschtes Widgetanbieterpaket.
  3. Wählen Sie es aus, und aktivieren Sie das Kontrollkästchen „Eigenen Code zunächst nicht starten, sondern debuggen“.
  4. Klicken Sie auf Anfügenaus.

Konvertieren Sie Ihre Konsolen-App in eine Windows-App

So konvertieren Sie die in dieser exemplarischen Vorgehensweise erstellte Konsolen-App in eine Windows-App:

  1. Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf das Projekt „ExampleWidgetProvider“, und wählen Sie Eigenschaften aus. Navigieren Sie zu Linker -> System, und ändern Sie SubSystem von „Konsole“ in „Windows“. Dies kann auch durch Hinzufügen von <SubSystem>Windows</SubSystem> zum Abschnitt <Link>..</Link> der VCXPROJ-Datei erfolgen.
  2. Ändern Sie in der Datei „main.cpp“ int main() in int WINAPI wWinMain(_In_ HINSTANCE /*hInstance*/, _In_opt_ HINSTANCE /*hPrevInstance*/, _In_ PWSTR pCmdLine, _In_ int /*nCmdShow*/).

Screenshot der Eigenschaften des C++-Widgetanbieterprojekts, wobei der Ausgabetyp auf „Windows-Anwendung“ festgelegt ist

Veröffentlichen Ihres Widgets

Nachdem Sie Ihr Widget entwickelt und getestet haben, können Sie Ihre App im Microsoft Store veröffentlichen, damit Benutzer Ihre Widgets auf ihren Geräten installieren können. Eine schrittweise Anleitung zum Veröffentlichen einer App finden Sie unter Veröffentlichen Ihrer App im Microsoft Store.

Die Widgetssammlung im Store

Nachdem Ihre App im Microsoft Store veröffentlicht wurde, können Sie anfordern, dass Ihre App in die Widgetssammlung im Store aufgenommen wird, die Benutzern hilft, Apps mit Windows-Widgets zu entdecken. Informationen zum Übermitteln Ihrer Anforderung finden Sie unter Übermitteln Ihrer Widgetinformationen, um Sie zur Store-Sammlung hinzuzufügen.

Screenshot des Microsoft Store mit der Widgetssammlung, die Benutzern eine Auswahl von Apps mit Windows-Widgets bereitstellt.

Implementieren der Widgetanpassung

Ab Windows App SDK 1.4 unterstützen Widgets die Benutzeranpassung. Wenn dieses Feature implementiert ist, wird dem Menü mit den Auslassungspunkten oberhalb der Option Widget loslösen eine Option Widget anpassen hinzugefügt.

Screenshot eines Widgets mit angezeigtem Anpassungsdialogfeld.

Die folgenden Schritte fassen den Prozess für die Widgetanpassung zusammen.

  1. Im normalen Betrieb antwortet der Widgetanbieter auf Anforderungen vom Widgethost mit den visuellen und Daten-JSON-Vorlagen für die normale Widgeterfahrung.
  2. Der Benutzer klickt im Menü mit den Auslassungspunkten auf die Schaltfläche Widget anpassen.
  3. Das Widget löst das OnCustomizationRequested-Ereignis beim Widgetanbieter aus, um anzuzeigen, dass der Benutzer die Widgetanpassung angefordert hat.
  4. Der Widgetanbieter legt ein internes Flag fest, um anzuzeigen, dass sich das Widget im Anpassungsmodus befindet. Im Anpassungsmodus sendet der Widgetanbieter die JSON-Vorlagen für die Widgetanpassungs-Benutzeroberfläche anstelle der normalen Widget-Benutzeroberfläche.
  5. Im Anpassungsmodus empfängt der Widgetanbieter OnActionInvoked-Ereignisse, wenn der Benutzer mit der Anpassungsbenutzeroberfläche interagiert, und passt seine interne Konfiguration und sein Verhalten auf der Grundlage der Aktionen des Benutzers an.
  6. Wenn die Aktion, die mit dem OnActionInvoked-Ereignis verknüpft ist, die von der App definierte Aktion „Anpassung beenden“ ist, setzt der Widgetanbieter sein internes Flag zurück, um anzuzeigen, dass er sich nicht mehr im Anpassungsmodus befindet, und nimmt das Senden der visuellen und Daten-JSON-Vorlagen für die normale Widgeterfahrung wieder auf, wobei die während der Anpassung angeforderten Änderungen berücksichtigt werden.
  7. Der Widgetanbieter behält die Anpassungsoptionen auf dem Datenträger oder in der Cloud bei, sodass die Änderungen zwischen Aufrufen des Widgetanbieters erhalten bleiben.

Hinweis

Es gibt einen bekannten Fehler im Windows Widget Board für Widgets, die mit dem Windows App SDK erstellt wurden, der dazu führt, dass das Menü mit den Auslassungspunkten nicht mehr reagiert, nachdem die Anpassungskarte angezeigt wurde.

In typischen Widget-Anpassungsszenarien wählt der Benutzer aus, welche Daten auf dem Widget angezeigt werden, oder er passt die visuelle Darstellung des Widgets an. Der Einfachheit halber wird im Beispiel in diesem Abschnitt ein Anpassungsverhalten hinzugefügt, das es dem Benutzer ermöglicht, den Zähler des in den vorherigen Schritten implementierten Zählwidgets zurückzusetzen.

Hinweis

Die Widgetanpassung wird nur in Windows App SDK 1.4 und höher unterstützt. Stellen Sie sicher, dass Sie die Verweise in Ihrem Projekt auf die neueste Version des Nuget-Pakets aktualisieren.

Aktualisieren des Paketmanifests zum Deklarieren der Anpassungsunterstützung

Um dem Widgethost mitzuteilen, dass das Widget die Anpassung unterstützt, fügen Sie das Attribut IsCustomizable zum Definitionselement für das Widget hinzu, und legen Sie es auf true fest.

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

Aktualisieren von WidgetProvider.h

Um dem Widget, das in den vorherigen Schritten in diesem Artikel erstellt wurde, Anpassungssupport hinzuzufügen, müssen wir die Headerdatei für unseren Widgetanbieter, WidgetProvider.h, aktualisieren.

Aktualisieren Sie zunächst die Definition CompactWidgetInfo. Diese Hilfsstruktur hilft uns, den aktuellen Zustand unserer aktiven Widgets nachzuverfolgen. Fügen Sie das Feld inCustomization hinzu, mit dem nachverfolgt wird, wann der Widgethost erwartet, dass wir unsere JSON-Anpassungsvorlage anstelle der normalen Widgetvorlage senden.

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

Aktualisieren Sie die WidgetProvider-Deklaration, um die IWidgetProvider2-Schnittstelle zu implementieren.

// WidgetProvider.h

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

Fügen Sie eine Deklaration für den OnCustomizationRequested-Rückruf der IWidgetProvider2-Schnittstelle hinzu.

// WidgetProvider.h

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

Deklarieren Sie schließlich eine Zeichenfolgenvariable, die die JSON-Vorlage für die Widgetanpassungs-Benutzeroberfläche definiert. In diesem Beispiel gibt es eine Schaltfläche „Zähler zurücksetzen“ und eine Schaltfläche „Anpassung beenden“, die unserem Anbieter signalisiert, dass er zum normalen Widgetverhalten zurückkehren soll.

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

Aktualisieren von WidgetProvider.cpp

Aktualisieren Sie nun die Datei WidgetProvider.cpp, um das Verhalten der Widgetanpassung zu implementieren. Bei dieser Methode wird dasselbe Muster verwendet wie bei den anderen verwendeten Rückrufen. Die ID des anzupassenden Widgets rufen wir aus dem WidgetContext ab, anschließend suchen wir die mit dem Widget verknüpfte Hilfsstruktur CompactWidgetInfo und legen das Feld inCustomization auf true fest.

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

Als Nächstes aktualisieren wir unsere Hilfsmethode UpdateWidget, die unsere Daten- und visuellen JSON-Vorlagen an den Widgethost sendet. Beim Aktualisieren des Zählwidgets senden wir abhängig vom Wert des Felds InCustomization entweder die normale Widgetvorlage oder die Anpassungsvorlage. Aus Platzgründen wird Code, der nicht für die Anpassung relevant ist, in diesem Codeausschnitt weggelassen.

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

Wenn Benutzer mit Eingaben in unserer Anpassungsvorlage interagieren, ruft sie denselben OnActionInvoked-Handler auf, als wenn der Benutzer mit der normalen Widgeterfahrung interagieren würde. Zur Unterstützung der Anpassung suchen wir nach den Verben „reset“ und „exitCustomization“ aus unserer JSON-Anpassungsvorlage. Wenn die Aktion die Schaltfläche „Zähler zurücksetzen“ betrifft, setzen wir den Zähler im Feld customState unserer Hilfsstruktur auf 0 zurück. Wenn die Aktion die Schaltfläche „Anpassung beenden“ betrifft, legen wir das Feld inCustomization auf false fest, sodass beim Aufrufen von UpdateWidget unsere Hilfsmethode die normalen JSON-Vorlagen und nicht die Anpassungsvorlage sendet.

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

Wenn Sie nun Ihr Widget bereitstellen, sollte die Schaltfläche Widget anpassen im Menü mit den Auslassungspunkten angezeigt werden. Wenn Sie auf die Schaltfläche „Anpassen“ klicken, wird Ihre Anpassungsvorlage angezeigt.

Ein Screenshot, der die Benutzeroberfläche zur Widget-Anpassung zeigt.

Klicken Sie auf die Schaltfläche Zähler zurücksetzen, um den Zähler auf 0 zurückzusetzen. Klicken Sie auf die Schaltfläche Anpassung beenden, um zum normalen Verhalten Ihres Widgets zurückzukehren.