Implementace zprostředkovatele widgetu v aplikaci Windows v jazyce C#

Tento článek vás provede vytvořením jednoduchého zprostředkovatele widgetu, který implementuje rozhraní IWidgetProvider. Metody tohoto rozhraní jsou vyvolány hostitelem widgetu, aby požádal o data, která definují widget, nebo aby poskytovatel widgetu reagoval na akci uživatele na widgetu. Poskytovatelé widgetů můžou podporovat jeden widget nebo více widgetů. V tomto příkladu definujeme dva různé widgety. Jedním widgetem je widget pro napodobení počasí, který znázorňuje některé možnosti formátování poskytované architekturou Adaptivní karty. Druhý widget předvede akce uživatele a vlastní funkci stavu widgetu tím, že zachová čítač, který se zvýší, kdykoli uživatel klikne na tlačítko zobrazené na widgetu.

snímek obrazovky s jednoduchým widgetem počasí Widget zobrazuje některé grafické objekty a data související s počasím a také některé diagnostické texty, které ilustrují, že se zobrazuje šablona widgetu střední velikosti.

Snímek obrazovky jednoduchého widgetu pro počítání. Widget zobrazuje řetězec obsahující číselnou hodnotu, kterou je třeba zvýšit, a tlačítko označené Přidat, stejně jako nějaký diagnostický text, který ilustruje, že se zobrazuje šablona widgetu malé velikosti.

Tento ukázkový kód v tomto článku je upraven z ukázky widgetů Windows App SDK. Pokud chcete implementovat poskytovatele widgetu pomocí C++/WinRT, přečtěte si téma Implementace zprostředkovatele widgetu v aplikaci win32 (C++/WinRT).

Požadavky

  • Vaše zařízení musí mít povolený vývojářský režim. Další informace najdete v tématu Nastavení pro vývojáře.
  • Visual Studio 2026 nebo novější s úlohou Vývoj aplikacíWinUI. Nezapomeňte přidat komponentu pro C++ (v143) z volitelného rozevíracího seznamu.

Vytvoření nové konzolové aplikace jazyka C#

V Visual Studio vytvořte nový projekt. V dialogovém okně Vytvořit nový projekt nastavte filtr jazyka na C# a filtr platformy na Windows a pak vyberte šablonu projektu konzolové aplikace. Pojmenujte nový projekt ExampleWidgetProvider. Po zobrazení výzvy nastavte cílovou .NET verzi na 8.0.

Po načtení projektu klikněte v Průzkumník řešení pravým tlačítkem myši na název projektu a vyberte Properties. Na stránce General přejděte dolů na Target OS a vyberte Windows. V části Cílová verze operačního systému vyberte verzi 10.0.19041.0 nebo novější.

Pokud chcete aktualizovat project na podporu .NET 8.0, klikněte v Průzkumník řešení pravým tlačítkem myši na název project a vyberte Edit Project Soubor. Uvnitř PropertyGroup přidejte následující element RuntimeIdentifiers.

<RuntimeIdentifiers>win-x86;win-x64;win-arm64</RuntimeIdentifiers>

Všimněte si, že tento průvodce používá konzolovou aplikaci, která při aktivaci widgetu otevře konzolové okno, aby umožnila snadné ladění. Až budete připravení publikovat aplikaci zprostředkovatele widgetů, můžete konzolovou aplikaci převést na aplikaci Windows pomocí kroků v Konvertovat konzolovou aplikaci na aplikaci Windows.

Přidání odkazů na Windows App SDK

Tato ukázka používá nejnovější stabilní balíček NuGet Windows App SDK. V Průzkumník řešení klikněte pravým tlačítkem na Dependencies a vyberte Spravované balíčky NuGet... . Ve správci balíčků NuGet vyberte kartu Browse a vyhledejte "Microsoft". WindowsAppSDK" V rozevíracím seznamu Verze vyberte nejnovější stabilní verzi a klikněte na Nainstalovat.

Přidání třídy WidgetProvider pro zpracování operací widgetu

V Visual Studio klikněte pravým tlačítkem na projekt ExampleWidgetProvider v Průzkumník řešení a vyberte Add->Class. V dialogovém okně Přidat třídu pojmenujte třídu WidgetProvider a klikněte na Přidat. Ve vygenerovaném WidgetProvider.cs souboru aktualizujte definici třídy tak, aby indikovala, že implementuje IWidgetProvider rozhraní.

// WidgetProvider.cs
internal class WidgetProvider : IWidgetProvider

Příprava ke sledování povolených widgetů

Poskytovatel widgetů může podporovat jeden widget nebo více widgetů. Kdykoli hostitel widgetu zahájí operaci s poskytovatelem widgetu, předá ID k identifikaci widgetu přidruženého k operaci. Každý widget má také přidružený název a hodnotu stavu, kterou lze použít k ukládání vlastních dat. V tomto příkladu deklarujeme jednoduchou pomocnou strukturu pro uložení ID, názvu a dat pro každý připnutý widget. Widgety můžou být také v aktivním stavu, který je popsán v části Aktivovat a deaktivovat níže. Tento stav budeme sledovat pro každý widget s logickou hodnotou. Do souboru WidgetProvider.cs přidejte následující definici do oboru názvů ExampleWidgetProvider, ale mimo definici třídy WidgetProvider.

// WidgetProvider.cs

public class CompactWidgetInfo
{
    public string? widgetId { get; set; }
    public string? widgetName { get; set; }
    public int customState = 0;
    public bool isActive = false;

}

V definici třídy WidgetProvider v WidgetProvider.cs přidejte člena pro mapu, která bude udržovat seznam povolených widgetů pomocí ID widgetu jako klíč pro každou položku.

// WidgetProvider.cs

// Class member of WidgetProvider
public static Dictionary<string, CompactWidgetInfo> RunningWidgets = new Dictionary<string, CompactWidgetInfo>(); 

Deklarace řetězců JSON šablony widgetu

Tento příklad deklaruje některé statické řetězce, které definují šablony JSON pro každý widget. Pro usnadnění práce jsou tyto šablony uloženy v proměnných členů třídy WidgetProvider. Pokud potřebujete obecné úložiště pro šablony – dají se zahrnout jako součást balíčku aplikace: přístup k souborům balíčků. Informace o vytvoření dokumentu JSON šablony widgetu najdete v tématu Vytvoření šablony widgetu pomocí návrháře Adaptivní karty.

V nejnovější verzi můžou aplikace, které implementují widgety systému Windows, přizpůsobit záhlaví svého widgetu, které se zobrazí na panelu widgetů, čímž přepíší výchozí prezentaci. Další informace naleznete v tématu Přizpůsobení oblasti záhlaví widgetu.

Poznámka:

V nejnovější verzi můžou aplikace, které implementují Windows widgety, naplnit obsah widgetu kódem HTML obsluhovaným ze zadané adresy URL místo toho, aby do datové části JSON předávané zprostředkovatele do panelu widgetů zadaly obsah ve formátu schématu adaptivní karty. Poskytovatelé widgetů musí stále poskytovat datovou část JSON adaptivní karty, takže kroky implementace v tomto návodu platí pro webové widgety. Další informace naleznete v tématu poskytovatelé webových widgetů.

// WidgetProvider.cs

// Class members of WidgetProvider
        const string weatherWidgetTemplate = """
{
    "$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 string countWidgetTemplate = """
{                                                                     
    "type": "AdaptiveCard",                                         
    "body": [                                                         
        {                                                               
            "type": "TextBlock",                                    
            "text": "You have clicked the button ${count} times"    
        },
        {
                "text":"Rendering Only if Small",
                "type":"TextBlock",
                "$when":"${$host.widgetSize==\"small\"}"
        },
        {
                "text":"Rendering Only if Medium",
                "type":"TextBlock",
                "$when":"${$host.widgetSize==\"medium\"}"
        },
        {
            "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"                                                
}
""";

Implementace metod IWidgetProvider

V následujících několika částech budeme implementovat metody IWidgetProvider rozhraní. Pomocná metoda UpdateWidget, která je volána v několika z těchto implementací metod, se zobrazí dále v tomto článku.

Poznámka:

Objekty předané do metod zpětného volání rozhraní IWidgetProvider jsou zaručeně platné pouze v rámci samotného zpětného volání. Odkazy na tyto objekty byste neměli ukládat, protože jejich chování mimo kontext zpětného volání není definováno.

VytvořitWidget

Hostitel widgetu volá CreateWidget, když uživatel připnul některý z widgetů vaší aplikace do hostitele widgetu. Nejprve tato metoda získá ID a název přidružené widgetu a přidá novou instanci naší pomocné struktury, CompactWidgetInfo, do kolekce povolených widgetů. Dále odešleme počáteční šablonu a data widgetu, která je zapouzdřená v pomocné metodě UpdateWidget.

// WidgetProvider.cs

public void CreateWidget(WidgetContext widgetContext)
{
    var widgetId = widgetContext.Id; // To save RPC calls
    var widgetName = widgetContext.DefinitionId;
    CompactWidgetInfo runningWidgetInfo = new CompactWidgetInfo() { widgetId = widgetId, widgetName = widgetName };
    RunningWidgets[widgetId] = runningWidgetInfo;


    // Update the widget
    UpdateWidget(runningWidgetInfo);
}

Odstranit Widget

Hostitel widgetu volá DeleteWidget, když uživatel odepnul některý z widgetů vaší aplikace z widgetového hostitele. Když k tomu dojde, odebereme přidružený widget ze seznamu povolených widgetů, abychom pro tento widget neposílali žádné další aktualizace.

// WidgetProvider.cs

public void DeleteWidget(string widgetId, string customState)
{
    RunningWidgets.Remove(widgetId);

    if(RunningWidgets.Count == 0)
    {
        emptyWidgetListEvent.Set();
    }
}

V tomto příkladu kromě odebrání widgetu se zadaným ze seznamu povolených widgetů také zkontrolujeme, jestli je seznam prázdný, a pokud ano, nastavíme událost, která se později použije k tomu, aby se aplikace ukončila, když nejsou povolené widgety. Do definice vaší třídy přidejte deklaraci ManualResetEvent a veřejnou přístupovou funkci.

// WidgetProvider.cs
static ManualResetEvent emptyWidgetListEvent = new ManualResetEvent(false);

public static ManualResetEvent GetEmptyWidgetListEvent()
{
    return emptyWidgetListEvent;
}

OnActionInvoked

Hostitel widgetu volá OnActionInvolat, když uživatel pracuje s akcí, kterou jste definovali v šabloně widgetu. Pro widget čítače použitého v tomto příkladu byla akce deklarována se slovesem a hodnotou "inc" v JSON šabloně pro widget. Kód zprostředkovatele widgetu použije tuto hodnotu příkazu k určení akce, která se má provést v reakci na interakci uživatele.

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

V metodě OnActionInvoked získejte hodnotu slovesa kontrolou vlastnosti Verb objektu WidgetActionInvokedArgs předaného do metody. Pokud je sloveso "inc", víme, že zvýšíme počet v přizpůsobeném stavu widgetu. Z WidgetActionInvokedArgszískejte objekt WidgetContext a potom WidgetId získat ID pro widget, který se aktualizuje. Vyhledejte položku v mapě povolených widgetů se zadaným ID a pak aktualizujte hodnotu vlastního stavu, která se používá k uložení počtu přírůstků. Nakonec aktualizujte obsah widgetu novou hodnotou pomocí UpdateWidget pomocné funkce.

// WidgetProvider.cs

public void OnActionInvoked(WidgetActionInvokedArgs actionInvokedArgs)
{
    var verb = actionInvokedArgs.Verb;
        if (verb == "inc")
        {
            var 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:
            var data = actionInvokedArgs.Data;
            if (RunningWidgets.ContainsKey(widgetId))
            {
                var localWidgetInfo = RunningWidgets[widgetId];
                // Increment the count
                localWidgetInfo.customState++;
                UpdateWidget(localWidgetInfo);
            }
        }
}

Informace o syntaxi Action.Execute pro Adaptivní karty najdete v tématu Action.Execute. Pro pokyny ohledně návrhu interakce widgetů si přečtěte Pokyny k návrhu interakce widgetu.

OnWidgetContextChanged

V aktuální verzi se OnWidgetContextChanged volá pouze v případě, že uživatel změní velikost připnutého widgetu. V závislosti na požadované velikosti můžete hostiteli widgetu vrátit jinou šablonu NEBO data JSON. Můžete také navrhnout JSON šablony tak, aby podporovala všechny dostupné velikosti pomocí podmíněného vykreslování na základě hodnoty host.widgetSize. Pokud pro změnu velikosti nepotřebujete odesílat novou šablonu nebo data, můžete pro účely telemetrie použít OnWidgetContextChanged.

// WidgetProvider.cs

public void OnWidgetContextChanged(WidgetContextChangedArgs contextChangedArgs)
{
    var widgetContext = contextChangedArgs.WidgetContext;
    var widgetId = widgetContext.Id;
    var widgetSize = widgetContext.Size;
    if (RunningWidgets.ContainsKey(widgetId))
    {
        var localWidgetInfo = RunningWidgets[widgetId];
        UpdateWidget(localWidgetInfo);
    }
}
    

Aktivace a deaktivace

Volá se metoda Activate, která informuje poskytovatele widgetu, že hostitel widgetu má v současné době zájem o příjem aktualizovaného obsahu od poskytovatele. Může to například znamenat, že uživatel aktuálně aktivně zobrazuje hostitele widgetu. Metoda Deactivate je volána k upozornění poskytovatele widgetu, že hostitel widgetu již nevyžaduje aktualizace obsahu. Tyto dvě metody definují okno, ve kterém má hostitel widgetu největší zájem o zobrazení nejaktuálnějšího obsahu up-to. Poskytovatelé widgetů můžou kdykoliv odesílat aktualizace do widgetu, například v reakci na push oznámení, ale stejně jako u jakéhokoli úkolu na pozadí, je důležité vyvážit up-to–date obsah se zdroji, jako je životnost baterie.

Aktivovat a Deaktivovat jsou volány pro každý widget zvlášť. Tento příklad sleduje aktivní stav jednotlivých widgetů v CompactWidgetInfo pomocné struktury. V metodě Activate voláme metodu pomocníka UpdateWidget pro aktualizaci widgetu. Všimněte si, že časové období mezi Aktivovat a Deaktivovat může být malé, proto doporučujeme, abyste se pokusili co nejrychleji aktualizovat cestu kódu widgetu.

// WidgetProvider.cs

public void Activate(WidgetContext widgetContext)
{
    var widgetId = widgetContext.Id;

    if (RunningWidgets.ContainsKey(widgetId))
    {
        var localWidgetInfo = RunningWidgets[widgetId];
        localWidgetInfo.isActive = true;

        UpdateWidget(localWidgetInfo);
    }
}
public void Deactivate(string widgetId)
{
    if (RunningWidgets.ContainsKey(widgetId))
    {
        var localWidgetInfo = RunningWidgets[widgetId];
        localWidgetInfo.isActive = false;
    }
}

Aktualizace widgetu

Definujte metodu pomocné rutiny UpdateWidget pro aktualizaci povoleného widgetu. V tomto příkladu zkontrolujeme název widgetu v CompactWidgetInfo pomocné struktury předané metodě a pak nastavíme odpovídající šablonu a data JSON na základě toho, který widget se aktualizuje. Možnost žádosti o aktualizaci widgetu je inicializována pomocí šablony, dat a vlastního stavu pro aktualizovaný widget. Zavolejte WidgetManager::GetDefault, abyste získali instanci třídy WidgetManager, a potom zavolejte UpdateWidget, abyste odeslali aktualizovaná data widgetu hostiteli widgetu.

// WidgetProvider.cs

void UpdateWidget(CompactWidgetInfo localWidgetInfo)
{
    WidgetUpdateRequestOptions updateOptions = new WidgetUpdateRequestOptions(localWidgetInfo.widgetId);

    string? templateJson = null;
    if (localWidgetInfo.widgetName == "Weather_Widget")
    {
        templateJson = weatherWidgetTemplate.ToString();
    }
    else if (localWidgetInfo.widgetName == "Counting_Widget")
    {
        templateJson = countWidgetTemplate.ToString();
    }

    string? dataJson = null;
    if (localWidgetInfo.widgetName == "Weather_Widget")
    {
        dataJson = "{}";
    }
    else if (localWidgetInfo.widgetName == "Counting_Widget")
    {
        dataJson = "{ \"count\": " + localWidgetInfo.customState.ToString() + " }";
    }

    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= localWidgetInfo.customState.ToString();
    WidgetManager.GetDefault().UpdateWidget(updateOptions);
}

Inicializace seznamu povolených widgetů při spuštění

Když je náš poskytovatel widgetu poprvé inicializován, je vhodné se zeptat WidgetManager pokud existují nějaké spuštěné widgety, které náš poskytovatel aktuálně obsluhuje. Pomůže obnovit aplikaci do předchozího stavu v případě restartování počítače nebo chybového ukončení poskytovatele. Chcete-li získat výchozí instanci správce widgetů pro aplikaci, volejte WidgetManager.GetDefault. Potom zavolejte GetWidgetInfos, které vrátí pole objektů WidgetInfo. Zkopírujte ID widgetu, názvy a vlastní stav do pomocné struktury CompactWidgetInfo a uložte ho do RunningWidgets členské proměnné. Vložte následující kód do definice třídy WidgetProvider.

// WidgetProvider.cs

public WidgetProvider()
{
    var runningWidgets = WidgetManager.GetDefault().GetWidgetInfos();

    foreach (var widgetInfo in runningWidgets)
    {
        var widgetContext = widgetInfo.WidgetContext;
        var widgetId = widgetContext.Id;
        var widgetName = widgetContext.DefinitionId;
        var customState = widgetInfo.CustomState;
        if (!RunningWidgets.ContainsKey(widgetId))
        {
            CompactWidgetInfo runningWidgetInfo = new CompactWidgetInfo() { widgetId = widgetId, widgetName = 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 = Convert.ToInt32(customState.ToString());
                runningWidgetInfo.customState = count;
            }
            catch
            {

            }
            RunningWidgets[widgetId] = runningWidgetInfo;
        }
    }
}

Implementace objektu pro vytváření tříd, která vytvoří instanci WidgetProvideru na žádost

Aby hostitel widgetu komunikoval s naším poskytovatelem widgetů, musíme volat CoRegisterClassObject. Tato funkce vyžaduje, abychom vytvořili implementaci IClassFactory , která vytvoří objekt třídy pro naši WidgetProvider třídy. Náš objekt pro vytváření tříd implementujeme do samostatné pomocné třídy.

V Visual Studio klikněte pravým tlačítkem na projekt ExampleWidgetProvider v Průzkumník řešení a vyberte Add->Class. V dialogovém okně Přidat třídu pojmenujte třídu FactoryHelper a klikněte na Přidat.

Obsah souboru FactoryHelper.cs nahraďte následujícím kódem. Tento kód definuje rozhraní IClassFactory a implementuje je dvě metody CreateInstance a LockServer. Tento kód je typický často používaný pro implementaci objektu pro vytváření tříd a není specifický pro funkce zprostředkovatele widgetu s tím rozdílem, že označujeme, že objekt třídy, který se vytváří, implementuje IWidgetProvider rozhraní.

// FactoryHelper.cs

using Microsoft.Windows.Widgets.Providers;
using System.Runtime.InteropServices;
using WinRT;

namespace COM
{
    static class Guids
    {
        public const string IClassFactory = "00000001-0000-0000-C000-000000000046";
        public const string IUnknown = "00000000-0000-0000-C000-000000000046";
    }

    /// 
    /// IClassFactory declaration
    /// 
    [ComImport, ComVisible(false), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid(COM.Guids.IClassFactory)]
    internal interface IClassFactory
    {
        [PreserveSig]
        int CreateInstance(IntPtr pUnkOuter, ref Guid riid, out IntPtr ppvObject);
        [PreserveSig]
        int LockServer(bool fLock);
    }

    [ComVisible(true)]
    class WidgetProviderFactory<T> : IClassFactory
    where T : IWidgetProvider, new()
    {
        public int CreateInstance(IntPtr pUnkOuter, ref Guid riid, out IntPtr ppvObject)
        {
            ppvObject = IntPtr.Zero;

            if (pUnkOuter != IntPtr.Zero)
            {
                Marshal.ThrowExceptionForHR(CLASS_E_NOAGGREGATION);
            }

            if (riid == typeof(T).GUID || riid == Guid.Parse(COM.Guids.IUnknown))
            {
                // Create the instance of the .NET object
                ppvObject = MarshalInspectable<IWidgetProvider>.FromManaged(new T());
            }
            else
            {
                // The object that ppvObject points to does not support the
                // interface identified by riid.
                Marshal.ThrowExceptionForHR(E_NOINTERFACE);
            }

            return 0;
        }

        int IClassFactory.LockServer(bool fLock)
        {
            return 0;
        }

        private const int CLASS_E_NOAGGREGATION = -2147221232;
        private const int E_NOINTERFACE = -2147467262;

    }
}

Vytvořte identifikátor GUID, který představuje CLSID pro vašeho poskytovatele widgetu

Dále je potřeba vytvořit identifikátor GUID představující CLSID , který se použije k identifikaci vašeho poskytovatele widgetu pro aktivaci COM. Stejná hodnota se použije také při balení aplikace. Vygenerujte identifikátor GUID ve Visual Studio tak, že přejdete na Nástroje->Vytvořit GUID. Vyberte možnost formát registru a klikněte na Kopírovat a vložte ho do textového souboru, abyste ho mohli později zkopírovat.

Registrace objektu třídy zprostředkovatele widgetu v OLE

V souboru Program.cs pro náš spustitelný soubor zavoláme CoRegisterClassObject k registraci poskytovatele widgetu v OLE, aby s ním hostitel widgetu mohl pracovat. Obsah Program.cs nahraďte následujícím kódem. Tento kód importuje funkci CoRegisterClassObject a volá ji a předává WidgetProviderFactory rozhraní, které jsme definovali v předchozím kroku. Nezapomeňte aktualizovat deklaraci proměnné CLSID_Factory tak, aby používala identifikátor GUID, který jste vygenerovali v předchozím kroku.

// Program.cs

using System.Runtime.InteropServices;
using ComTypes = System.Runtime.InteropServices.ComTypes;
using Microsoft.Windows.Widgets;
using ExampleWidgetProvider;
using COM;
using System;

[DllImport("kernel32.dll")]
static extern IntPtr GetConsoleWindow();

[DllImport("ole32.dll")]

static extern int CoRegisterClassObject(
            [MarshalAs(UnmanagedType.LPStruct)] Guid rclsid,
            [MarshalAs(UnmanagedType.IUnknown)] object pUnk,
            uint dwClsContext,
            uint flags,
            out uint lpdwRegister);

[DllImport("ole32.dll")] static extern int CoRevokeClassObject(uint dwRegister);

Console.WriteLine("Registering Widget Provider");
uint cookie;

Guid CLSID_Factory = Guid.Parse("XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX");
CoRegisterClassObject(CLSID_Factory, new WidgetProviderFactory<WidgetProvider>(), 0x4, 0x1, out cookie);
Console.WriteLine("Registered successfully. Press ENTER to exit.");
Console.ReadLine();

if (GetConsoleWindow() != IntPtr.Zero)
{
    Console.WriteLine("Registered successfully. Press ENTER to exit.");
    Console.ReadLine();
}
else
{
    // Wait until the manager has disposed of the last widget provider.
    using (var emptyWidgetListEvent = WidgetProvider.GetEmptyWidgetListEvent())
    {
        emptyWidgetListEvent.WaitOne();
    }

    CoRevokeClassObject(cookie);
}

Všimněte si, že tento příklad kódu naimportuje funkci GetConsoleWindow , která určí, jestli je aplikace spuštěná jako konzolová aplikace, výchozí chování pro tento názorný postup. Pokud funkce vrátí platný ukazatel, zapíšeme do konzoly informace o ladění. Jinak je aplikace spuštěná jako Windows aplikace. V takovém případě počkáme na událost, kterou jsme nastavili v DeleteWidget metodě, když je seznam povolených widgetů prázdný, a poté aplikaci ukončíme. Informace o převodu ukázkové konzolové aplikace na aplikaci Windows najdete v tématu Povertování konzolové aplikace na aplikaci Windows.

Zabalte svou aplikaci poskytovatele widgetů

V aktuální verzi je možné jako poskytovatele widgetů zaregistrovat jenom zabalené aplikace. Následující kroky vás provedou procesem zabalení aplikace a aktualizací manifestu aplikace pro registraci aplikace v operačním systému jako poskytovatele widgetu.

Vytvoření projektu balení MSIX

V Průzkumník řešení klikněte pravým tlačítkem na řešení a vyberte Přidat - Nový projekt.... V dialogovém okně Přidat nový projekt vyberte šablonu "Windows Aplikační balíček" a klikněte na Další. Nastavte název projektu na ExampleWidgetProviderPackage a klikněte na Vytvořit. Po zobrazení výzvy nastavte cílovou verzi na verzi 1809 nebo novější a klikněte na OK. Potom klikněte pravým tlačítkem myši na project ExampleWidgetProviderPackage a vyberte Add->Project reference. Vyberte projekt ExampleWidgetProvider a klikněte na tlačítko OK.

Přidání odkazu na balíček Windows App SDK do projektu balení

Do projektu balení MSIX je potřeba přidat odkaz na balíček nuget Windows App SDK. V Průzkumník řešení poklikejte na projekt ExampleWidgetProviderPackage a otevřete soubor ExampleWidgetProviderPackage.wapproj. Do elementu Project přidejte následující kód XML.

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

Poznámka:

Ujistěte se, že verze zadaná v elementu PackageReference odpovídá nejnovější stabilní verzi, na kterou jste odkazovali v předchozím kroku.

Pokud je v počítači již nainstalována správná verze Windows App SDK a nechcete v balíčku zabalit modul runtime sady SDK, můžete určit závislost balíčku v souboru Package.appxmanifest pro projekt ExampleWidgetProviderPackage.

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

Aktualizace manifestu balíčku

V Průzkumník řešení klikněte pravým tlačítkem na soubor Package.appxmanifest a vyberte View Code a otevřete soubor XML manifestu. Dále je potřeba přidat deklarace oboru názvů pro některá rozšíření balíčků aplikací, které budeme používat. Do elementu nejvyšší úrovně Package přidejte následující definice oboru názvů.

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

Uvnitř elementu Application vytvořte nový prázdný prvek s názvem Extensions. Dbejte na to, aby to přišlo po uzavírací značce uap:VisualElements.

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

    </Extensions>
</Application>

První rozšíření, které potřebujeme přidat, je rozšíření ComServer. Tím se zaregistruje vstupní bod spustitelného souboru v operačním systému. Toto rozšíření je ekvivalentem zabalené aplikace pro registraci serveru COM nastavením klíče registru a není specifické pro poskytovatele widgetů. Přidejte následující prvek com:Extension jako podřízený prvek Extensions. Změňte GUID v atributu Id elementu com:Class na GUID, který jste vygenerovali v předchozím kroku.

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

Dále přidejte rozšíření, které zaregistruje aplikaci jako poskytovatele widgetu. V následujícím úseku kódu vložte prvek uap3:Extension jako podřízený prvek elementu Extensions. Nezapomeňte nahradit atribut ClassId elementu COM identifikátorem GUID, který jste použili v předchozích krocích.

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

Podrobné popisy a informace o formátu pro všechny tyto prvky naleznete v tématu Formát XML manifestu balíčku poskytovatele widgetu.

Přidání ikon a dalších obrázků do projektu balení

V Průzkumník řešení klikněte pravým tlačítkem na ExampleWidgetProviderPackage a vyberte Add->New Folder. Pojmenujte tuto složku ProviderAssets, protože se používá v Package.appxmanifest z předchozího kroku. Tady uložíme naše ikony a snímky obrazovek pro naše widgety. Po přidání požadovaných ikon a snímků obrazovky se ujistěte, že názvy obrázků odpovídají tomu, co následuje po Path=ProviderAssets\ ve vašem Package.appxmanifest nebo se widgety nezobrazí v hostiteli widgetu.

Informace o požadavcích na návrh obrázků snímků obrazovky a konvencích vytváření názvů pro lokalizované snímky obrazovky najdete v tématu Integrace s nástrojem pro výběr widgetu.

Testování poskytovatele widgetů

Ujistěte se, že jste vybrali architekturu, která odpovídá vašemu vývojovému počítači, z rozevíracího seznamu platformy řešení , například x64. V Průzkumník řešení klikněte pravým tlačítkem na řešení a vyberte Build Solution. Po dokončení klikněte pravým tlačítkem na ExampleWidgetProviderPackage a vyberte možnost Nasadit. V aktuální verzi je jediným podporovaným hostitelem widgetů panel widgetů. Pokud chcete zobrazit widgety, budete muset otevřít panel widgetů a vybrat Přidat widgety v pravém horním rohu. Posuňte se do dolní části dostupných widgetů a měli byste vidět napodobení Weather Widget a Microsoft Counting Widget vytvořené v tomto kurzu. Kliknutím na widgety je připnete na panel widgetů a otestujete jejich funkčnost.

Ladění poskytovatele widgetů

Po připnutí widgetů spustí platforma widgetů aplikaci poskytovatele widgetů, aby mohla přijímat a odesílat relevantní informace o widgetu. Chcete-li ladit spuštěný widget, můžete buď připojit ladicí program ke spuštěné aplikaci zprostředkovatele widgetu, nebo můžete nastavit Visual Studio, aby se po spuštění automaticky spustil proces zprostředkovatele widgetu.

Chcete-li se připojit ke spuštěném procesu:

  1. V Visual Studio klikněte na Debug -> Připojit k procesu.
  2. Vyfiltrujte procesy a najděte požadovanou aplikaci zprostředkovatele widgetu.
  3. Připojte ladicí program.

Pokud chcete automaticky připojit ladicí program k procesu při počátečním spuštění:

  1. V Visual Studio klikněte na Debug -> Další cíle ladění -> Ladění nainstalovaného balíčku aplikace.
  2. Vyfiltrujte balíčky a najděte požadovaný balíček zprostředkovatele widgetu.
  3. Vyberte jej a zaškrtněte políčko s názvem Nespouštět, ale při spuštění ladit můj kód.
  4. Klepněte na tlačítko Připojit.

Převod konzolové aplikace na Windows aplikaci

Pokud chcete převést konzolovou aplikaci vytvořenou v tomto názorném postupu na aplikaci Windows, klikněte pravým tlačítkem na projekt ExampleWidgetProvider v Průzkumník řešení a vyberte Properties. V části Application->General změňte typ Output z konzolové aplikace na Windows Aplikace.

A snímek obrazovky znázorňující vlastnosti projektu zprostředkovatele widgetu C# s typem výstupu nastaveným na Windows Application

Publikování widgetu

Po vývoji a otestování widgetu můžete aplikaci publikovat na Microsoft Store, aby si uživatelé nainstalovali widgety do svých zařízení. Podrobné pokyny k publikování aplikace najdete v tématu Publikujte svou aplikaci v Microsoft Storu.

Kolekce obchodu s widgety

Po publikování vaší aplikace na Microsoft Store můžete požádat o její zahrnutí do kolekce widgetů v obchodě, která pomáhá uživatelům objevovat aplikace s funkcí Windows Widgety. Pokud chcete odeslat svou žádost, přečtěte si jak odeslat informace o widgetu pro přidání do kolekce obchodu.

Snímek obrazovky obchodu Microsoft Store zobrazující kolekci widgetů, která umožňuje uživatelům objevovat aplikace, které obsahují Windows Widgety.

Implementace přizpůsobení widgetu

Od Windows App SDK 1.4 můžou widgety podporovat přizpůsobení uživatelů. Při implementaci této funkce se do nabídky se třemi tečkami nad možností Odepnout widget přidá možnost Přizpůsobit widget.

snímek obrazovky znázorňující widget se zobrazeným dialogem přizpůsobení

Následující kroky shrnují proces přizpůsobení widgetu.

  1. V normálním provozu poskytovatel widgetu reaguje na požadavky z hostitele widgetu pomocí šablony a datových částí pro běžné prostředí widgetu.
  2. Uživatel klikne na tlačítko Přizpůsobit widget v nabídce s trojtečkou.
  3. Widget vyvolá událost OnCustomizationRequested poskytovateli widgetu, aby naznačil, že uživatel požádal o přizpůsobení widgetu.
  4. Poskytovatel widgetu nastaví interní příznak, který označuje, že widget je v režimu přizpůsobení. V režimu přizpůsobení odesílá poskytovatel widgetů šablony JSON pro uživatelské rozhraní pro přizpůsobení widgetu místo běžného uživatelského rozhraní widgetu.
  5. V režimu přizpůsobení obdrží poskytovatel widgetu OnActionInvoked události, jakmile uživatel pracuje s uživatelským rozhraním pro přizpůsobení a upravuje jeho interní konfiguraci a chování na základě akcí uživatele.
  6. Když je akce spojená s událostí OnActionInvoked akcí "ukončení přizpůsobení" definovanou aplikací, poskytovatel widgetu resetuje svůj interní příznak, aby označil, že už není v režimu přizpůsobení, a pokračuje v odesílání šablon vizuálního zobrazení a dat v JSON formátu pro běžné prostředí widgetu, což odráží změny požadované během přizpůsobení. Uživatel může zavřít prostředí pro přizpůsobení, aniž by klikl na aplikací určenou akci pro ukončení přizpůsobení. V tomto případě bude vyvolána IWidgetProviderAnalytics.OnAnalyticsInfoReported a WidgetAnalyticsInfoReportedArgs.AnalyticsJson bude mít druhInterakce "exitCustomization".
  7. Poskytovatel widgetu uchovává možnosti přizpůsobení disku nebo cloudu, aby se změny zachovaly mezi vyvoláním poskytovatele widgetu.

Poznámka:

U widgetů vytvořených pomocí Windows App SDK existuje známá Windows chyba, která způsobí, že nabídka se třemi tečkami přestane reagovat po zobrazení karty přizpůsobení.

V typických scénářích přizpůsobení widgetu uživatel zvolí, jaká data se ve widgetu zobrazí, nebo upraví vizuální prezentaci widgetu. Pro zjednodušení, příklad v této části zavede funkci, která uživateli umožní resetovat čítač počítacího widgetu, který byl implementován v předchozích krocích.

Poznámka:

Přizpůsobení widgetu se podporuje jenom v Windows App SDK 1.4 a novějších verzích. Nezapomeňte aktualizovat odkazy v projektu na nejnovější verzi balíčku NuGet.

Aktualizace manifestu balíčku pro deklaraci podpory přizpůsobení

Pokud chcete hostiteli widgetu dát vědět, že widget podporuje přizpůsobení, přidejte atribut IsCustomizable do elementu Definition widgetu a nastavte ho na true.

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

Sledování, kdy je widget v režimu přizpůsobení

Příklad v tomto článku používá pomocné struktury CompactWidgetInfo ke sledování aktuálního stavu našich aktivních widgetů. Přidejte pole inCustomization, které se použije ke sledování, kdy hostitel widgetů očekává, že místo běžné šablony widgetu odešleme šablonu JSON vlastního nastavení.

// WidgetProvider.cs
public class CompactWidgetInfo
{
    public string widgetId { get; set; }
    public string widgetName { get; set; }
    public int customState = 0;
    public bool isActive = false;
    public bool inCustomization = false;
}

Implementujte IWidgetProvider2

Funkce přizpůsobení widgetu je zpřístupněna prostřednictvím rozhraní IWidgetProvider2 . Aktualizujte definici třídy WidgetProvider pro implementaci tohoto rozhraní.

// WidgetProvider.cs
internal class WidgetProvider : IWidgetProvider, IWidgetProvider2

Přidejte implementaci pro zpětné volání OnCustomizationRequested v rozhraní IWidgetProvider2. Tato metoda používá stejný vzor jako ostatní zpětné volání, které jsme použili. ID widgetu, který se má přizpůsobit, získáme z WidgetContextu a najdeme pomocnou strukturu CompactWidgetInfo přidruženou k danému widgetu a nastavíme pole inCustomization na hodnotu true.

// WidgetProvider.cs
public void OnCustomizationRequested(WidgetCustomizationRequestedArgs customizationInvokedArgs)
{
    var widgetId = customizationInvokedArgs.WidgetContext.Id;
    if (RunningWidgets.ContainsKey(widgetId))
    {
        var localWidgetInfo = RunningWidgets[widgetId];
        localWidgetInfo.inCustomization = true;
        UpdateWidget(localWidgetInfo);
    }
}

Teď deklarujte proměnnou řetězce, která definuje šablonu JSON pro uživatelské rozhraní pro přizpůsobení widgetu. V tomto příkladu máme tlačítko Resetovat čítač a tlačítko Ukončit vlastní nastavení, které signalizují, že se náš poskytovatel vrátí k běžnému chování widgetu. Tuto definici umístěte vedle ostatních definic šablon.

// WidgetProvider.cs
const string countWidgetCustomizationTemplate = @"
{
    ""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""
}";

Odeslání šablony vlastního nastavení v nástroji UpdateWidget

V dalším kroku aktualizujeme naši UpdateWidget pomocnou metodu, která odesílá naše data a šablony JSON vizuálu hostiteli widgetů. Při aktualizaci widgetu pro počítání pošleme buď běžnou šablonu widgetu, nebo šablonu přizpůsobení v závislosti na hodnotě inCustomization pole. Pro stručnost není v tomto fragmentu kódu vynechán kód, který není relevantní pro přizpůsobení.

// WidgetProvider.cs
void UpdateWidget(CompactWidgetInfo localWidgetInfo)
{
    ...
    else if (localWidgetInfo.widgetName == "Counting_Widget")
    {
        if (!localWidgetInfo.inCustomization)
        {
            templateJson = countWidgetTemplate.ToString();
        }
        else
        {
            templateJson = countWidgetCustomizationTemplate.ToString();
        }
    
    }
    ...
    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 = localWidgetInfo.customState.ToString();
    WidgetManager.GetDefault().UpdateWidget(updateOptions);
}

Reakce na akce přizpůsobení

Když uživatelé pracují se vstupy v naší šabloně přizpůsobení, volá stejnou OnActionInvoked obslužnou rutinu, jako když uživatel pracuje s běžným prostředím widgetu. Abychom mohli podporovat přizpůsobení, vyhledáme příkazy "reset" a "exitCustomization" z naší šablony JSON pro přizpůsobení. Pokud se jedná o akci pro tlačítko „Resetovat čítač“, resetujeme čítač uložený v poli customState naší pomocné struktury na 0. Pokud je akce pro tlačítko "Ukončit přizpůsobení", nastavíme inCustomization pole false tak, aby při volání UpdateWidget, naše pomocná metoda odešle běžné šablony JSON, a ne šablonu přizpůsobení.

// WidgetProvider.cs
public void OnActionInvoked(WidgetActionInvokedArgs actionInvokedArgs)
{
    var verb = actionInvokedArgs.Verb;
    if (verb == "inc")
    {
        var 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:
        var data = actionInvokedArgs.Data;
        if (RunningWidgets.ContainsKey(widgetId))
        {
            var localWidgetInfo = RunningWidgets[widgetId];
            // Increment the count
            localWidgetInfo.customState++;
            UpdateWidget(localWidgetInfo);
        }
    } 
    else if (verb == "reset") 
    {
        var widgetId = actionInvokedArgs.WidgetContext.Id;
        var data = actionInvokedArgs.Data;
        if (RunningWidgets.ContainsKey(widgetId))
        {
            var localWidgetInfo = RunningWidgets[widgetId];
            // Reset the count
            localWidgetInfo.customState = 0;
            localWidgetInfo.inCustomization = false;
            UpdateWidget(localWidgetInfo);
        }
    }
    else if (verb == "exitCustomization")
    {
        var widgetId = actionInvokedArgs.WidgetContext.Id;
        var data = actionInvokedArgs.Data;
        if (RunningWidgets.ContainsKey(widgetId))
        {
            var localWidgetInfo = RunningWidgets[widgetId];
            // Stop sending the customization template
            localWidgetInfo.inCustomization = false;
            UpdateWidget(localWidgetInfo);
        }
    }
}

Nyní, když nasadíte widget, měli byste v nabídce s elipsou vidět tlačítko Přizpůsobit widget. Kliknutím na tlačítko Přizpůsobit zobrazíte šablonu vlastního nastavení.

Snímek obrazovky s uživatelským rozhraním pro přizpůsobení widgetů

Kliknutím na tlačítko Resetovat počítadlo resetujete počítadlo na 0. Kliknutím na tlačítko Ukončit přizpůsobení se vrátíte k běžnému chování widgetu.