Megosztás:


Widget-szolgáltató implementálása win32-alkalmazásban (C++/WinRT)

Ez a cikk bemutatja, hogyan hozhat létre egy egyszerű widget-szolgáltatót, amely megvalósítja az IWidgetProvider felületet. Ennek a felületnek a metódusait a widget-gazdagép meghívja, hogy kérje le a widgetet meghatározó adatokat, vagy hogy a widgetszolgáltató válaszoljon egy widget felhasználói műveletére. A widgetszolgáltatók egyetlen vagy több widgetet is támogathatnak. Ebben a példában két különböző widgetet határozunk meg. Az egyik widget egy szimulált időjárási widget, amely az Adaptív kártyák keretrendszer által biztosított formázási lehetőségek némelyikét mutatja be. A második widget bemutatja a felhasználói műveleteket és az egyéni widgetállapot-funkciót egy olyan számláló fenntartásával, amely növekszik, amikor a felhasználó a widgeten megjelenő gombra kattint.

Képernyőkép egy egyszerű időjárási widgetről. A widget az időjárással kapcsolatos ábrákat és adatokat, valamint néhány diagnosztikai szöveget jelenít meg, amelyek azt szemléltetik, hogy a közepes méretű widget sablonja jelenik meg.

Képernyőkép egy egyszerű számláló widgetről. A widget egy karakterláncot jelenít meg, amely tartalmazza a növelendő számértéket, egy

A cikkben szereplő mintakód a Windows App SDK widgetek mintáiból lett átdolgozva. A widget-szolgáltató C# használatával történő implementálásához lásd: Widget-szolgáltató implementálása win32-alkalmazásokban (C#).

Előfeltételek

  • Az eszköznek engedélyeznie kell a fejlesztői módot. További információ: Beállítások fejlesztőknek.
  • Visual Studio 2022 vagy újabb verzió a Univerzális Windows Platform fejlesztési munkafolyamattal. Mindenképpen adja hozzá a C++ (v143) összetevőt az opcionális legördülő listából.

Új C++/WinRT win32-konzolalkalmazás létrehozása

Hozzon létre egy új projektet a Visual Studióban. Az Új projekt létrehozása párbeszédpanelen állítsa a nyelvi szűrőt "C++" értékre, a platformszűrőt pedig Windowsra, majd válassza ki a Windows Konzolalkalmazás (C++/WinRT) projektsablont. Nevezze el az új projektet "ExampleWidgetProvider" néven. Amikor a rendszer kéri, állítsa az alkalmazás cél Windows-verzióját az 1809-es vagy újabb verzióra.

Hivatkozások hozzáadása a Windows App SDK-hoz és a Windows implementációs könyvtár nuGet-csomagjaihoz

Ez a minta a legújabb stabil Windows App SDK NuGet-csomagot használja. A Megoldáskezelőben kattintson a jobb gombbal a Hivatkozások elemre , és válassza a NuGet-csomagok kezelése... lehetőséget. A NuGet-csomagkezelőben válassza a Tallózás lapot, és keressen rá a "Microsoft.WindowsAppSDK" kifejezésre. Válassza ki a legújabb stabil verziót a Verzió legördülő listában, majd kattintson a Telepítés gombra.

Ez a minta a Windows implementációs kódtár NuGet-csomagjának használatát is használja. A Megoldáskezelőben kattintson a jobb gombbal a Hivatkozások elemre , és válassza a NuGet-csomagok kezelése... lehetőséget. A NuGet-csomagkezelőben válassza a Tallózás lapot, és keressen rá a "Microsoft.Windows.ImplementationLibrary" kifejezésre. Válassza ki a legújabb verziót a Verzió legördülő listában, majd kattintson a Telepítés gombra.

Az előre összeállított fejlécfájlban (pch.h) adja hozzá az alábbi include utasításokat.

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

Megjegyzés:

Először a wil/cppwinrt.h fejlécet kell tartalmaznia a WinRT-fejlécek előtt.

A widget-szolgáltató alkalmazás megfelelő leállításának kezeléséhez a winrt::get_module_lock egyéni implementációjára van szükség. Előre deklaráljuk a SignalLocalServerShutdown metódust, amely a main.cpp fájlban lesz definiálva, és beállítunk egy eseményt, amely az alkalmazás kilépését jelzi. Adja hozzá a következő kódot a pch.h fájlhoz, közvetlenül a #pragma once irányelv alá, a többi beillesztés előtt.

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

WidgetProvider-osztály hozzáadása a widgetműveletek kezeléséhez

A Visual Studio-ban kattintson a jobb gombbal a ExampleWidgetProvider projektre a Megoldáskezelőben, és válassza a Hozzáadás ->Osztálylehetőséget. Az Osztály hozzáadása párbeszédpanelen nevezze el a "WidgetProvider" osztályt, majd kattintson a hozzáadása elemre.

Az IWidgetProvider interfészt implementáló osztály deklarálása

Az IWidgetProvider felület olyan metódusokat határoz meg, amelyeket a widget-gazdagép meghív a vezérlőszolgáltatóval való műveletek kezdeményezéséhez. Cserélje le a WidgetProvider.h fájl üres osztálydefinícióját a következő kódra. Ez a kód deklarál egy struktúrát, amely implementálja az IWidgetProvider interfészt, és deklarálja a prototípusokat az interfész módszereihez.

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

    
};

Emellett adjon hozzá egy privát metódust, az UpdateWidgetet, amely egy segédmetódus, amely frissítéseket küld a szolgáltatótól a widget-gazdagépnek.

// WidgetProvider.h
private: 

void UpdateWidget(CompactWidgetInfo const& localWidgetInfo);

Felkészülés az engedélyezett widgetek nyomon követésére

A widget-szolgáltatók egyetlen vagy több widgetet is támogathatnak. Amikor a widget-gazdagép műveletet kezdeményez a widget-szolgáltatónál, egy azonosítót ad át a művelethez társított widget azonosításához. Minden vezérlőhöz tartozik egy társított név és egy állapotérték is, amely egyéni adatok tárolására használható. Ebben a példában egy egyszerű segédstruktúrát deklarálunk az egyes rögzített vezérlők azonosítójának, nevének és adatainak tárolásához. A widgetek aktív állapotban is lehetnek, amelyet az alábbi Aktiválás és inaktiválás szakaszban ismertetünk, és ezt az állapotot minden widget esetében logikai értékkel követjük nyomon. Adja hozzá a következő definíciót a WidgetProvider.h fájlhoz a WidgetProvider struct deklarációja fölött.

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

A WidgetProvider.h widgetProvider deklarációjában adjon hozzá egy tagot a térképhez, amely megőrzi az engedélyezett widgetek listáját, és minden bejegyzéshez kulcsként használja a widget azonosítóját.

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

        

Widgetsablon JSON-sztringjeinek deklarálása

Ez a példa néhány statikus sztringet deklarál az egyes vezérlők JSON-sablonjainak meghatározásához. A kényelem érdekében ezek a sablonok a WidgetProvider osztálydefiníción kívül deklarált helyi változókban vannak tárolva. Ha általános tárterületre van szüksége a sablonokhoz , az alkalmazáscsomag részeként is felvehetők: Csomagfájlok elérése. A widgetsablon JSON-dokumentumának létrehozásáról további információt az Adaptív kártyatervezővel rendelkező widgetsablon létrehozása című témakörben talál.

A legújabb kiadásban a Windows-vezérlőket implementáló alkalmazások testre szabhatják a widgethez megjelenített fejlécet a Widgets Boardban, felülírva az alapértelmezett bemutatót. További információ: A widget fejlécterületének testreszabása.

Megjegyzés:

A legújabb kiadásban a Windows-widgeteket implementáló alkalmazások dönthetnek úgy, hogy a widget tartalmát egy megadott URL-címről kiszolgált HTML-lel töltik fel ahelyett, hogy a szolgáltatótól a Widgets Boardnak átadott JSON-hasznos adatban adja meg az Adaptív kártya sémaformátumában lévő tartalmat. A vezérlőszolgáltatóknak továbbra is rendelkezniük kell egy adaptív kártya JSON-hasznos adatával, így az útmutató implementálási lépései a webes widgetekre vonatkoznak. További információ: Webes widget-szolgáltatók.

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

Az IWidgetProvider metódusok implementálása

A következő néhány szakaszban implementáljuk az IWidgetProvider felület módszereit. A cikk későbbi részében megjelenik az a UpdateWidget segítő metódus, amelyet több ilyen metódusmegvalósításban is meghívnak. Mielőtt belemerülne az interfész metódusokba, adja hozzá a következő sorokat a WidgetProvider.cpp-hoz, az include irányelvek után, hogy beolvassa a widget-szolgáltató API-kat a winrt névtérbe, és hozzáférést biztosítson az előző lépésben deklarált térképhez.

Megjegyzés:

Az IWidgetProvider felület visszahívási metódusaiba átadott objektumok csak a visszahíváson belül lesznek érvényesek. Ne tároljon hivatkozásokat ezekre az objektumokra, mert a visszahívás környezetén kívüli viselkedésük nincs meghatározva.

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

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

CreateWidget

A widget-gazdagép meghívja a CreateWidgetet , amikor a felhasználó az alkalmazás egyik widgetjét rögzítette a widget-gazdagépen. Először is ez a módszer lekéri a társított widget azonosítóját és nevét, és hozzáad egy új példányt a compactWidgetInfo segédstruktúránkhoz az engedélyezett widgetek gyűjteményéhez. Ezután elküldjük a widget kezdeti sablonját és adatait, amely az UpdateWidget segédmetódusban van beágyazva.

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

Widget törlése

A widget-gazdagép akkor hívja meg a DeleteWidgetet , ha a felhasználó törölte az alkalmazás egyik widgetét a widget-gazdagépről. Ha ez történik, eltávolítjuk a társított widgetet az engedélyezett widgetek listájából, hogy ne küldhessünk további frissítéseket a widgethez.

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

OnActionInvoked

A widget-gazdagép meghívja az OnActionInvoked parancsot , amikor a felhasználó egy, a widgetsablonban meghatározott művelettel lép kapcsolatba. Az ebben a példában használt számláló widget esetében a widget JSON-sablonjában egy "inc" értékű műveletet deklaráltak. A widget-szolgáltató kódja ezt az igeértéket használja annak meghatározására, hogy milyen műveletet kell elvégezni a felhasználói beavatkozásra válaszul.

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

Az OnActionInvoked metódusban szerezze meg az ige értékét a WidgetActionInvokedArgs objektum Verb tulajdonságának ellenőrzésével. Ha az ige "inc", akkor tudjuk, hogy a widget testreszabott állapotában növelni fogjuk a számlálót. A WidgetActionInvokedArgs fájlból kérje le a WidgetContext objektumot, majd a WidgetId azonosítót a frissített widget azonosítójának lekéréséhez. Keresse meg a bejegyzést az engedélyezett widgetek térképén a megadott azonosítóval, majd frissítse a növekmények tárolásához használt egyéni állapotértéket. Végül frissítse a widget tartalmát az új értékkel az UpdateWidget segédfüggvénnyel.

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

Az Adaptív kártyák Action.Execute szintaxisával kapcsolatos információkért lásd: Action.Execute. A widgetek interakciójának tervezésével kapcsolatos útmutatásért tekintse meg vezérlők interakciójának tervezési útmutatóját

OnWidgetContextChanged

Az aktuális kiadásban az OnWidgetContextChanged csak akkor lesz meghívva, ha a felhasználó módosítja a rögzített widget méretét. A kért mérettől függően másik JSON-sablont/adatot adhat vissza a widget-gazdagépnek. A JSON sablont úgy is megtervezheti, hogy a host.widgetSize értékétől függően feltételes rendereléssel támogassa az összes elérhető méretet. Ha nem kell új sablont vagy adatokat küldenie a méretváltozáshoz, az OnWidgetContextChanged telemetriai célokra használható.

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

Aktiválás és inaktiválás

Az Activate metódust meghívva értesítheti a widget-szolgáltatót arról, hogy a widget-gazdagép jelenleg szeretne frissített tartalmat kapni a szolgáltatótól. Ez például azt jelentheti, hogy a felhasználó jelenleg aktívan tekinti meg a widget-gazdagépet. A rendszer meghívja az Inaktiválás metódust, amely értesíti a widget-szolgáltatót arról, hogy a widget-gazdagép már nem kér tartalomfrissítéseket. Ez a két módszer határoz meg egy ablakot, amelyben a widget-gazdagép leginkább a up-to-date tartalmat szeretné ábrázolni. A widget-szolgáltatók bármikor küldhetnek frissítéseket a widgetnek, például leküldéses értesítésre válaszolva, de mint minden háttérfeladat esetében, fontos, hogy up-todátumtartalmat biztosítson az erőforrásokkal kapcsolatos problémákkal, például az akkumulátor üzemidejével.

aktiválása és Inaktiválás meghívása widgetenként történik. Ez a példa a CompactWidgetInfo segédstruktúra egyes vezérlőinek aktív állapotát követi nyomon. Az Aktiválás metódusban meghívjuk az UpdateWidget segédmetódust a widget frissítéséhez. Vegye figyelembe, hogy az aktiválás és az inaktiválás közötti időablak kicsi lehet, ezért javasoljuk, hogy próbálja meg a lehető leggyorsabban frissíteni a widget kódját.

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

Widget frissítése

Adja meg az UpdateWidget segédmetódusát egy engedélyezett widget frissítéséhez. Ebben a példában ellenőrizzük a CompactWidgetInfo segédstruktúrában átadott widget nevét, majd beállítjuk a megfelelő sablont és annak adat JSON-ját, annak függvényében, hogy melyik widgetet frissítjük. A WidgetUpdateRequestOptions inicializálva van a frissített widget sablonjával, adataival és egyéni állapotával. Hívja meg a WidgetManager::GetDefault parancsot a WidgetManager osztály egy példányának lekéréséhez, majd az UpdateWidget hívásával küldje el a frissített widgetadatokat a widget-gazdagépnek.

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

Az engedélyezett widgetek listájának inicializálása indításkor

A widget-szolgáltató első inicializálásakor érdemes megkérdezni a WidgetManagert , hogy vannak-e futó widgetek, amelyeket a szolgáltató jelenleg kiszolgál. Segít helyreállítani az alkalmazást az előző állapotba a számítógép újraindítása vagy a szolgáltató összeomlása esetén. Hívja meg a WidgetManager::GetDefault parancsot az alkalmazás alapértelmezett widget-kezelő példányának lekéréséhez. Ezután hívja meg a GetWidgetInfos parancsot, amely WidgetInfo-objektumok tömbét adja vissza. Másolja át a widget ID-ket, neveket és egyéni állapotokat a segítő struktúrába CompactWidgetInfo, és mentse el a RunningWidgets tagváltozóba. Illessze be a következő kódot a WidgetProvider osztályának konstruktorába.

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

Regisztráljon egy osztály-előállítót, amely kérésre példányosítani fogja a WidgetProvidert

Adja hozzá a WidgetProvider osztályt meghatározó fejlécet az alkalmazás main.cpp fájljának tetején található belek közé. Itt is be fogjuk vonni a mutex-et.

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

Deklarálja azt az eseményt, amely az alkalmazás kilépését eredményezi, valamint a SignalLocalServerShutdown függvényt, amely beállítja az eseményt. Illessze be a következő kódot a main.cpp.

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

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

Ezután létre kell hoznia egy CLSID- kódot, amely a COM-aktiválás widgetszolgáltatójának azonosítására szolgál. Visual Studióban generáljon GUID-azonosítót úgy, hogy lép a Eszközök ->GUID létrehozásamenüpontra. Válassza a "static const GUID =" lehetőséget, kattintson a Másolás gombra, majd illessze be a main.cpp. Frissítse a GUID-definíciót a következő C++/WinRT szintaxissal, és állítsa be a GUID változó nevét widget_provider_clsid. Hagyja meg a GUID megjegyzésként megadott verzióját, mert később szüksége lesz erre a formátumra az alkalmazás csomagolásakor.

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

Adja hozzá a következő osztálygyári meghatározást a main.cpp-hoz. Ez többnyire olyan sablonkód, amely nem a widget-szolgáltató implementációira vonatkozik. Vegye figyelembe, hogy CoWaitForMultipleObjects megvárja a leállítási esemény aktiválását, mielőtt az alkalmazás kilépne.

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

Csomagolja a widget-szolgáltató alkalmazást

Az aktuális kiadásban csak a csomagolt alkalmazások regisztrálhatók widget-szolgáltatókként. Az alábbi lépések végigvezetik az alkalmazás csomagolásának és az alkalmazásjegyzék frissítésének folyamatán, hogy regisztrálja az alkalmazást az operációs rendszerben widget-szolgáltatóként.

MSIX csomagolási projekt létrehozása

A Megoldáskezelőben kattintson a jobb gombbal a megoldásra, és válassza az Add-New> Project(Új projekt hozzáadása) lehetőséget. Az Új projekt hozzáadása párbeszédpanelen válassza a "Windows Alkalmazáscsomagoló projekt" sablont, és kattintson a Tovább gombra. Állítsa a projekt nevét "ExampleWidgetProviderPackage" értékre, majd kattintson a létrehozása gombra. Amikor a rendszer kéri, állítsa a célverziót az 1809-es vagy újabb verzióra, és kattintson az OK gombra. Ezután kattintson a jobb egérgombbal az ExampleWidgetProviderPackage projektre, és válassza a Hozzáadás>Projekthivatkozáslehetőséget. Válassza ki a ExampleWidgetProvider projektet, és kattintson az OK gombra.

Windows App SDK-csomaghivatkozás hozzáadása a csomagolási projekthez

Az MSIX csomagolási projekthez hozzá kell adnia egy hivatkozást a Windows App SDK nuget-csomaghoz. A Megoldáskezelőablakban kattintson duplán az ExampleWidgetProviderPackage projektre az ExampleWidgetProviderPackage.wapproj fájl megnyitásához. Adja hozzá a következő xml-t a Project elemhez.

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

Megjegyzés:

Győződjön meg arról, hogy a PackageReference elemben megadott verzió megegyezik az előző lépésben hivatkozott legújabb stabil verzióval.

Ha a Windows App SDK megfelelő verziója már telepítve van a számítógépen, és nem szeretné csomagolni az SDK-futtatókörnyezetet a csomagban, a csomagfüggőséget a ExampleWidgetProviderPackage projekt Package.appxmanifest fájljában adhatja meg.

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

A csomagjegyzék frissítése

A Megoldáskezelőben kattintson a jobb gombbal a fájlra, Package.appxmanifest és válassza a Kód megtekintése lehetőséget a jegyzék xml-fájljának megnyitásához. Ezután hozzá kell adnia néhány névtér-deklarációt a használni kívánt alkalmazáscsomag-bővítményekhez. Adja hozzá a következő névtérdefiníciókat a legfelső szintű csomagelemhez .

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

Az Alkalmazáselemen belül hozzon létre egy új üres, Bővítmények nevű elemet. Győződjön meg arról, hogy ez a uap:VisualElementszáró címkéje után jelenik meg.

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

    </Extensions>
</Application>

Az első bővítmény, amelyet hozzá kell adnunk, a ComServer bővítmény. Ez regisztrálja a végrehajtható fájl belépési pontját az operációs rendszeren. Ez a bővítmény a COM-kiszolgáló regisztrációs kulcs beállításával történő regisztrálásával egyenértékű csomagolt alkalmazás, és nem a widget-szolgáltatókra vonatkozik. Adja hozzá a következő com:Extension elemet a Bővítmények elem gyermekelemeként. Módosítsa a Id attribútum GUID-ját a com:Class elemben arra a GUID-ra, amelyet az előző lépésben generált.

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

Ezután adja hozzá az alkalmazást widget-szolgáltatóként regisztráló bővítményt. Illessze be a uap3:Extension elemet a következő kódrészletbe, a Bővítmények elem gyermekeként. Mindenképpen cserélje le a COM-elemClassId attribútumát az előző lépésekben használt GUID azonosítóra.

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

Az összes elem részletes leírását és formátuminformációit lásd itt: Widget provider package manifest XML format.

Ikonok és egyéb képek hozzáadása a csomagolási projekthez

A Megoldáskezelőbenkattintson a jobb gombbal az ExampleWidgetProviderPackage elemre, majd válassza a Hozzáadás –>Új mappaparancsot. Nevezze el ezt a mappát ProviderAssets néven, mivel ez volt az Package.appxmanifest előző lépésben használt mappa. Itt fogjuk tárolni a widgetek ikonjait és képernyőképeit . Miután hozzáadta a kívánt ikonokat és képernyőképeket, győződjön meg arról, hogy a képnevek megegyeznek a Path=ProviderAssets\ után található névvel a Package.appxmanifest-ben, különben a widgetek nem fognak megjelenni a widgetgazdagépen.

A képernyőképképek tervezési követelményeivel és a honosított képernyőképek elnevezési konvencióival kapcsolatos információkért lásd: Integrálás a vezérlőválasztóval.

A widget-szolgáltató tesztelése

Győződjön meg arról, hogy kiválasztotta a fejlesztői gépnek megfelelő architektúrát a Megoldásplatformok legördülő listából, például "x64". A Megoldáskezelőben kattintson a jobb gombbal a megoldásra, és válassza a Megoldás létrehozása lehetőséget. Ha ez megtörtént, kattintson jobb gombbal az ExampleWidgetProviderPackage elemre, és válassza a Üzembe helyezéslehetőséget. Az aktuális kiadásban az egyetlen támogatott widget-gazdagép a Widgets Board. A widgetek megtekintéséhez meg kell nyitnia a widgetek tábláját, és a jobb felső sarokban válassza a Widgetek hozzáadása lehetőséget. Görgessen az elérhető widgetek aljára, és látnia kell az oktatóanyagban létrehozott, a példaként szolgáló Weather Widget és Microsoft Counting Widget widgeteket. A widgetekre kattintva rögzítheti őket a widgetek tábláján, és tesztelheti a funkciójukat.

A widget-szolgáltató hibakeresése

Miután rögzítette a widgeteket, a widgetplatform elindítja a widget-szolgáltató alkalmazást, hogy releváns információkat kapjon és küldjön a widgetről. A futó widget hibakereséséhez csatolhat egy hibakeresőt a futó widget-szolgáltató alkalmazáshoz, vagy beállíthatja, hogy a Visual Studio automatikusan elindítsa a widget-szolgáltatói folyamat hibakeresését az indítás után.

A futó folyamathoz való csatoláshoz:

  1. A Visual Studióban kattintson a(z) Hibakeresés -> Csatlakozásfolyamathoz.
  2. Szűrje a folyamatokat, és keresse meg a kívánt widgetszolgáltatói alkalmazást.
  3. Csatold a hibakeresőt.

Annak érdekében, hogy a hibakereső automatikusan csatolva legyen a folyamathoz az első indításkor:

  1. A Visual Studióban kattintson a Hibakeresés –> Egyéb hibakeresési célok –> Telepített alkalmazáscsomag hibakeresése elemre.
  2. Szűrje a csomagokat, és keresse meg a kívánt widget-szolgáltatói csomagot.
  3. Válassza ki, és jelölje be a jelölőnégyzetet, amelynek szövege: "Ne induljon el, de végezzen hibakeresést, amikor a kód elindul".
  4. Kattintson az ésCsatolás gombra.

A konzolalkalmazás átalakítása Windows-alkalmazássá

Az ebben az útmutatóban létrehozott konzolalkalmazás windowsos alkalmazássá alakításához:

  1. Kattintson a jobb gombbal a ExampleWidgetProvider projektre a Megoldáskezelőben , és válassza a Tulajdonságok lehetőséget. Lépjen a Linker -> Rendszer részre, és módosítsa a AlsóRendszert "Konzol" értékről "Windows" értékre. Ez úgy is elvégezhető, hogy hozzáadjuk a <SubSystem>Windows</SubSystem>-t a <Link>..</Link> szakaszába a .vcxproj fájlban.
  2. A(z) main.cpp-ban módosítsa a int main() értéket int WINAPI wWinMain(_In_ HINSTANCE /*hInstance*/, _In_opt_ HINSTANCE /*hPrevInstance*/, _In_ PWSTR pCmdLine, _In_ int /*nCmdShow*/)-re.

Képernyőkép a C++ widgetszolgáltató projekttulajdonságáról a Windows-alkalmazásra beállított kimeneti típussal

A widget közzététele

Miután kifejlesztette és tesztelte a widgetet, közzéteheti az alkalmazást a Microsoft Store-ban, hogy a felhasználók telepíthessék a widgeteket az eszközeikre. Az alkalmazások közzétételével kapcsolatos részletes útmutatásért lásd : Alkalmazás közzététele a Microsoft Store-ban.

A widgetek áruházgyűjteménye

Miután az alkalmazást közzétette a Microsoft Store-ban, kérheti, hogy az alkalmazás szerepeljen a widgetek Áruház gyűjteményében, amely segít a felhasználóknak felderíteni a Windows widgeteket tartalmazó alkalmazásokat. A kérés elküldéséhez lásd: a widget adatainak benyújtása a Store gyűjteményhez való hozzáadáshoz.

Képernyőkép a Microsoft Store-ról, amelyen a widgetek gyűjteménye látható, amely lehetővé teszi a felhasználók számára a Windows widgeteket tartalmazó alkalmazások felderítését.

Widget testreszabásának megvalósítása

A Windows App SDK 1.4-től kezdve a widgetek támogatják a felhasználók testreszabását. A funkció megvalósításakor a rendszer hozzáad egy testreszabási widget-beállítást a vezérlő rögzítésének feloldása lehetőség feletti három pont menühöz.

Képernyőkép egy widgetről, amelyen megjelenik a testreszabási párbeszédpanel.

Az alábbi lépések összefoglalják a widget testreszabásának folyamatát.

  1. Normál működés esetén a widget-szolgáltató válaszol a widget-gazdagéptől érkező kérésekre a rendszeres widgetélményhez tartozó vizualizációs és adat JSON-sablonokkal.
  2. A felhasználó az ellipszis menüben a Widget testreszabása gombra kattint.
  3. A widget az OnCustomizationRequested eseményt a widget-szolgáltatón jelzi, hogy a felhasználó kérte a widget testreszabási felületét.
  4. A widget-szolgáltató beállít egy belső jelzőt, amely jelzi, hogy a widget testreszabási módban van. Testreszabási módban a widget-szolgáltató a szokásos widget-felhasználói felület helyett elküldi a widget testreszabási felhasználói felületéhez tartozó JSON-sablonokat.
  5. Testreszabási módban a widget-szolgáltató onActionInvoked eseményeket fogad, miközben a felhasználó interakcióba lép a testreszabási felhasználói felülettel, és a felhasználó műveletei alapján módosítja annak belső konfigurációját és viselkedését.
  6. Ha az OnActionInvoked eseményhez társított művelet az alkalmazás által definiált "kilépés testreszabása" művelet, a widget-szolgáltató visszaállítja a belső jelzőt, hogy jelezze, hogy már nincs testreszabási módban, és folytatja a vizualizációs és adat JSON-sablonok küldését a szokásos widget-élmény érdekében, a testreszabás során kért módosításokat tükrözve.
  7. A widget-szolgáltató megőrzi a lemez vagy a felhő testreszabási beállításait, hogy a módosítások megmaradjanak a widget-szolgáltató meghívásai között.

Megjegyzés:

A Windows Widget Board ismert hibája a Windows App SDK-val létrehozott vezérlők esetében, amely miatt a három pont menü nem válaszol a testreszabási kártya megjelenítése után.

A widgetek tipikus testreszabási forgatókönyveiben a felhasználó kiválaszthatja, hogy milyen adatok jelenjenek meg a widgeten, vagy módosíthatja a widget vizuális megjelenítését. Az egyszerűség kedvéért az ebben a szakaszban szereplő példa olyan testreszabási viselkedést ad hozzá, amely lehetővé teszi, hogy a felhasználó visszaállítsa az előző lépésekben implementált számláló widget számlálóját.

Megjegyzés:

A widget testreszabása csak a Windows App SDK 1.4-ben és újabb verzióiban támogatott. Győződjön meg arról, hogy a projekt hivatkozásait a Nuget-csomag legújabb verziójára frissíti.

A csomagjegyzék frissítése a testreszabási támogatás deklarálásához

Ha tudatni szeretné a widget-gazdagéppel, hogy a widget támogatja a testreszabást, adja hozzá az IsCustomizable attribútumot a widget definícióelőtagjához, és állítsa igaz értékre.

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

WidgetProvider.h frissítése

Ha testre szabási támogatást szeretne adni a jelen cikk előző lépéseiben létrehozott widgethez, frissítenie kell a widgetszolgáltató, a WidgetProvider.h fejlécfájlt.

Először frissítse a CompactWidgetInfo definíciót. Ez a segédstruktúra segít nyomon követni az aktív widgetek aktuális állapotát. Adja hozzá az inCustomization mezőt, amely nyomon követi, hogy a widget-gazdagép mikor várja, hogy elküldjük a testreszabási JSON-sablont a szokásos widgetsablon helyett.

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

Frissítse a WidgetProvider deklarációt az IWidgetProvider2 felület implementálásához.

// WidgetProvider.h

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

Adjon hozzá egy deklarációt az IWidgetProvider2 felület OnCustomizationRequested visszahívásához.

// WidgetProvider.h

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

Végül deklaráljon egy sztringváltozót, amely meghatározza a widget testreszabási felhasználói felületéhez tartozó JSON-sablont. Ebben a példában az "Alaphelyzetbe állítás számláló" gomb és a "Kilépés a testreszabásból" gomb jelzi a szolgáltatónknak, hogy térjen vissza a szokásos widget-működéshez.

// 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 frissítése

Most frissítse a WidgetProvider.cpp fájlt a widget testreszabási viselkedésének implementálásához. Ez a módszer ugyanazt a mintát használja, mint a többi használt visszahívás. Lekérjük a widget testreszabásához szükséges azonosítót a WidgetContextből , és megkeresjük a widgethez társított CompactWidgetInfo segédszerkezetet, és igaz értékre állítjuk az inCustomization mezőt.

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

Ezután frissítjük az UpdateWidget segédmetódusunkat, amely elküldi az adatokat és a vizuális JSON-sablonokat a widget-gazdagépnek. A számláló widget frissítésekor a szokásos widgetsablont vagy a testreszabási sablont küldjük el az inCustomization mező értékétől függően. A rövidség kedvéért a testreszabás szempontjából nem releváns kód hiányzik ebben a kódrészletben.

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

Amikor a felhasználók a testreszabási sablonban lévő bemenetekkel dolgoznak, ugyanazt az OnActionInvoked kezelőt hívja meg, mint amikor a felhasználó a szokásos vezérlőélményt használja. A testreszabás támogatása érdekében a testreszabási JSON-sablonban az "reset" és "exitCustomization" igéket keressük. Ha a művelet a "Számláló alaphelyzetbe állítása" gombra van állítva, a segédstruktúra customState mezőjében tárolt számlálót 0 értékre állítjuk vissza. Ha a művelet a "Kilépés a testreszabásból" gombra van állítva, az InCustomization mezőt hamisra állítjuk, hogy az UpdateWidget hívásakor a segédmetódus a szokásos JSON-sablonokat küldje el, és ne a testreszabási sablont.

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

Most, amikor üzembe helyezi a widgetet, látnia kell a Widget testreszabása gombot a három pont menüben. A testreszabás gombra kattintva megjelenik a testreszabási sablon.

Képernyőkép a widgetek testreszabási felhasználói felületével.

A Számláló alaphelyzetbe állítása gombra kattintva állítsa vissza a számlálót 0-ra. Kattintson a Kilépés a testreszabásból gombra a widget szokásos viselkedéséhez való visszatéréshez.