Freigeben über


Implementieren eines Widgetanbieters in einem C#-Windows-App

Dieser Artikel führt Sie durch die Erstellung eines einfachen Widgetanbieters, der die Schnittstelle IWidgetProvider implementiert. Die Methoden dieser Schnittstelle werden vom Widgethost aufgerufen, um die Daten anzufordern, die ein Widget definieren, oder um den Widgetanbieter auf eine Benutzeraktion für ein Widget reagieren zu lassen. Widgetanbieter können ein einzelnes Widget oder mehrere Widgets unterstützen. In diesem Beispiel werden zwei verschiedene Widgets definiert. Ein Widget ist ein simuliertes Wetter-Widget, das einige der Formatierungsoptionen veranschaulicht, die vom Adaptive Karten Framework bereitgestellt werden. Das zweite Widget zeigt Benutzeraktionen und das Feature des benutzerdefinierten Widget-Zustands, indem ein Zähler verwaltet wird, der immer dann inkrementiert wird, wenn der Benutzer auf eine Schaltfläche klickt, die im Widget angezeigt wird.

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

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

Dieser Beispielcode in diesem Artikel wird aus dem Windows App SDK Widgets Sample angepasst. Informationen zum Implementieren eines Widgetanbieters mithilfe von C++/WinRT finden Sie unter Implementieren eines Widgetanbieters in einer Win32-App (C++/WinRT).

Voraussetzungen

  • Auf Ihrem Gerät muss der Entwicklermodus aktiviert sein. Weitere Informationen finden Sie unter "Einstellungen für Entwickler".
  • Visual Studio 2026 oder höher mit der WinUI-Anwendungsentwicklung Workload. Stellen Sie sicher, dass Sie die Komponente für C++ (v143) aus der optionalen Dropdownliste hinzufügen.

Erstellen einer neuen Konsolen-App in C#

Erstellen Sie in Visual Studio ein neues Projekt. Legen Sie im Dialogfeld Ein neues Projekt erstellen den Sprachfilter auf "C#" und den Plattformfilter auf Windows fest, und wählen Sie dann die Konsolen-App-Projektvorlage aus. Nennen Sie das neue Projekt „ExampleWidgetProvider“. Wenn Sie dazu aufgefordert werden, legen Sie das Ziel .NET Version auf 8.0 fest.

Wenn das Projekt geladen wird, klicken Sie in Projektmappen-Explorer mit der rechten Maustaste auf den Projektnamen, und wählen Sie Properties aus. Scrollen Sie auf der Seite General nach unten zu Target OS und wählen Sie "Windows" aus. Wählen Sie unter Zielbetriebssystemversion die Version 10.0.19041.0 oder höher aus.

Um Ihr Projekt zur Unterstützung von .NET 8.0 zu aktualisieren, klicken Sie in Projektmappen-Explorer mit der rechten Maustaste auf den Projektnamen und wählen Sie Projektdatei bearbeiten aus. Fügen Sie innerhalb der PropertyGroup das folgende RuntimeIdentifiers-Element hinzu.

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

Beachten Sie, dass in dieser exemplarischen Vorgehensweise eine Konsolen-App verwendet wird, die das Konsolenfenster anzeigt, wenn das Widget aktiviert wird, um einfaches Debuggen zu ermöglichen. Wenn Sie bereit sind, Ihre Widget-Provider-App zu veröffentlichen, können Sie die Konsolenanwendung in eine Windows-Anwendung umwandeln, indem Sie die Schritte in Umwandeln Ihrer Konsolen-App in eine Windows-App befolgen.

Hinzufügen von Verweisen auf die Windows App SDK

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

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

Klicken Sie in Visual Studio mit der rechten Maustaste auf das projekt ExampleWidgetProvider in Projektmappen-Explorer und wählen Sie Add->Class aus. Nennen Sie die Klasse im Dialogfeld Klasse hinzufügen „WidgetProvider“, und klicke dann auf Hinzufügen. Aktualisieren Sie die Klassendefinition in der generierten WidgetProvider.cs-Datei, um anzugeben, dass sie die IWidgetProvider-Schnittstelle implementiert.

// WidgetProvider.cs
internal class WidgetProvider : IWidgetProvider

Vorbereiten der Nachverfolgung aktivierter Widgets

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

// WidgetProvider.cs

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

}

Fügen Sie in der WidgetProvider-Klassendefinition in „WidgetProvider.cs“ ein Member für die Abbildung hinzu, das die aktivierten Widgets speichert und die Widget-ID als Schlüssel für jeden Eintrag verwendet.

// WidgetProvider.cs

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

Deklarieren von JSON-Zeichenfolgen für Widgetvorlagen

In diesem Beispiel werden einige statische Zeichenfolgen deklariert, um die JSON-Vorlagen für jedes Widget zu definieren. Zur Vereinfachung werden diese Vorlagen in den Membervariablen der WidgetProvider-Klasse gespeichert. Wenn Sie einen allgemeinen Speicher für die Vorlagen benötigen, können sie als Teil des Anwendungspakets einbezogen werden: Zugreifen auf Paketdateien. Informationen zum Erstellen des JSON-Dokuments der Widgetvorlage finden Sie unter Create a widget template with the Adaptive Karten Designer.

In der neuesten Version können Apps, die Windows Widgets implementieren, die Kopfzeile anpassen, die für ihr Widget im Widgets Board angezeigt wird, wobei die Standardpräsentation außer Kraft gesetzt wird. Weitere Informationen finden Sie unter Anpassen des Widget-Kopfzeilenbereichs.

Hinweis

In der neuesten Version können Apps, die Windows Widgets implementieren, den Widgetinhalt mit HTML füllen, der von einer angegebenen URL bereitgestellt wird, anstatt Inhalte im Schemaformat adaptiver Karten in der JSON-Nutzlast anzugeben, die vom Anbieter an das Widgets Board übergeben wird. Widgetanbieter müssen weiterhin eine JSON-Nutzlast für adaptive Karten bereitstellen, sodass die Implementierungsschritte in diesem Leitfaden auf Web-Widgets anwendbar sind. Weitere Informationen finden Sie unter Web-Widget-Anbieter.

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

Implementieren der IWidgetProvider-Methoden

In den nächsten Abschnitten werden die Methoden der IWidgetProvider-Schnittstelle implementiert. Die in mehreren dieser Methodenimplementierungen aufgerufene Hilfsmethode UpdateWidget wird weiter unten in diesem Artikel gezeigt.

Hinweis

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

WidgetErstellen

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

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

Widget löschen

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

// WidgetProvider.cs

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

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

In diesem Beispiel wird zusätzlich zum Entfernen des Widgets aus der Liste der aktivierten Widgets auch überprüft, ob die Liste jetzt leer ist. Ist dies der Fall, wird ein Ereignis festgelegt, das später verwendet wird, damit die App beendet werden kann, wenn keine aktivierten Widgets mehr vorhanden sind. Fügen Sie in Ihrer Klassendefinition die Deklaration von ManualResetEvent und eine öffentliche Accessorfunktion hinzu.

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

public static ManualResetEvent GetEmptyWidgetListEvent()
{
    return emptyWidgetListEvent;
}

OnActionInvoked

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

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

Rufen Sie in der Methode OnActionInvoked den Verbwert ab, indem Sie die Eigenschaft Verb der an die Methode übergebenen WidgetActionInvokedArgs-Werte überprüfen. Wenn das Verb „inc“ ist, wissen wir, dass die Zahl im benutzerdefinierten Zustand für das Widget erhöht wird. Rufen Sie aus WidgetActionInvokedArgs das WidgetContext-Objekt und dann die WidgetId ab, um die ID für das Widget abzurufen, das aktualisiert wird. Suchen Sie den Eintrag mit der angegebenen ID in unserer Zuordnung aktivierter Widgets und aktualisieren Sie dann den benutzerdefinierten Zustandswert, der zur Speicherung der Inkrementanzahl verwendet wird. Aktualisieren Sie schließlich den Widgetinhalt mit dem neuen Wert unter Verwendung der Hilfsfunktion UpdateWidget.

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

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

OnWidgetContextChanged (BeiÄnderungenDesWidgetKontexts)

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

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

Aktivieren und Deaktivieren

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

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

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

Aktualisieren eines Widgets

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

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

Initialisieren der Liste der aktivierten Widgets beim Start

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

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

Implementierung einer Klassenfabrik, die WidgetProvider auf Anfrage instanziiert

Damit der Widgethost mit dem Widgetanbieter kommunizieren kann, muss CoRegisterClassObject aufgerufen werden. Diese Funktion erfordert die Erstellung einer Implementierung der IClassFactory, die wiederum ein Klassenobjekt für die WidgetProvider-Klasse erstellt. Wir werden unsere Klassenfabrik in einer autarken Hilfsklasse implementieren.

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

Ersetzen Sie den Inhalt der Datei „FactoryHelper.cs“ mit folgendem Code. Dieser Code definiert die IClassFactory-Schnittstelle und implementiert die beiden Methoden CreateInstance und LockServer. Dieser Code ist ein typischer Baustein für die Implementierung einer Klassenfactory und nicht spezifisch für die Funktionalität eines Widgetanbieters. Es wird lediglich angegeben, dass das zu erstellende Klassenobjekt die IWidgetProvider-Schnittstelle implementiert.

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

    }
}

Erstellen einer GUID, die die CLSID für Ihren Widgetanbieter darstellt

Als Nächstes müssen Sie eine GUID erstellen, die die CLSID darstellt, die verwendet wird, um Ihren Widgetanbieter für die COM-Aktivierung zu identifizieren. Der gleiche Wert wird auch beim Packen Ihrer App verwendet. Generieren Sie eine GUID in Visual Studio, indem Sie zu Tools->Create GUID wechseln. Wählen Sie die Registrierungsformatoption aus, klicken Sie auf Kopieren, und fügen Sie sie dann in eine Textdatei ein, damit Sie sie später kopieren können.

Registrieren des Widgetanbieter-Klassenobjekts bei OLE

In der Datei „Program.cs“ für die ausführbare Datei wird CoRegisterClassObject aufgerufen, um den Widgetanbieter bei OLE zu registrieren, damit der Widgethost damit interagieren kann. Ersetzen Sie den Inhalt von "Program.cs" durch den folgenden Code. Dieser Code importiert die Funktion CoRegisterClassObject und ruft sie auf, wobei die in einem vorherigen Schritt definierte WidgetProviderFactory-Schnittstelle übergeben wird. Aktualisieren Sie die CLSID_Factory-Variablendeklaration unbedingt so, dass die im vorherigen Schritt generierte GUID verwendet wird.

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

Beachten Sie, dass in diesem Codebeispiel die Funktion GetConsoleWindow importiert wird, um zu bestimmen, ob die App als Konsolenanwendung ausgeführt wird. Dies ist das Standardverhalten für diese exemplarische Vorgehensweise. Wenn die Funktion einen gültigen Zeiger zurückgibt, werden Debuginformationen in die Konsole geschrieben. Andernfalls wird die App als Windows-App ausgeführt. In diesem Fall wird auf das in der DeleteWidget-Methode festgelegte Ereignis gewartet, wenn die Liste der aktivierten Widgets leer ist und die App beendet wird. Informationen zum Konvertieren der Beispielkonsolen-App in eine Windows-App finden Sie unter Convert your console app to a Windows app.

Paketieren Sie Ihre Widget-Anbieter-App

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

Erstellen eines MSIX-Paketerstellungsprojekts

Klicken Sie in Projektmappen-Explorer mit der rechten Maustaste auf Ihre Lösung, und wählen Sie Add->Neue Project aus... . Wählen Sie im Dialogfeld Add a new project die Vorlage "Windows Application Packaging Project" aus, und klicken Sie auf Next. Legen Sie den Projektnamen auf „ExampleWidgetProviderPackage“ fest, und klicken Sie auf Erstellen. Wenn Sie dazu aufgefordert werden, legen Sie die Zielversion auf Version 1809 oder höher fest, und klicken Sie auf OK. Klicken Sie als Nächstes mit der rechten Maustaste auf das ExampleWidgetProviderPackage-Projekt und wählen Sie Projektverweis hinzufügen aus. Wählen Sie das Projekt ExampleWidgetProvider aus, und klicken Sie auf „OK“.

Hinzufügen eines Windows App SDK-Paketverweises zum Verpackungsprojekt

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

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

Hinweis

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

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

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

Aktualisieren des Paketmanifests

Klicken Sie in Projektmappen-Explorer mit der rechten Maustaste auf die Datei Package.appxmanifest, und wählen Sie View Code aus, um die XML-Manifestdatei zu öffnen. Als Nächstes müssen Sie einige Namespace-Deklarationen für die App-Paketerweiterungen hinzufügen, die wir verwenden werden. Fügen Sie dem Package-Element der obersten Ebene die folgenden Namespacedefinitionen hinzu.

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

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

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

    </Extensions>
</Application>

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

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

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

<!-- Package.appxmanifest -->
<Extensions>
    ...
    <uap3:Extension Category="windows.appExtension">
        <uap3:AppExtension Name="com.microsoft.windows.widgets" DisplayName="WidgetTestApp" Id="ContosoWidgetApp" PublicFolder="Public">
            <uap3:Properties>
                <WidgetProvider>
                    <ProviderIcons>
                        <Icon Path="Images\StoreLogo.png" />
                    </ProviderIcons>
                    <Activation>
                        <!-- Apps exports COM interface which implements IWidgetProvider -->
                        <CreateInstance ClassId="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" />
                    </Activation>

                    <TrustedPackageFamilyNames>
                        <TrustedPackageFamilyName>Microsoft.MicrosoftEdge.Stable_8wekyb3d8bbwe</TrustedPackageFamilyName>
                    </TrustedPackageFamilyNames>

                    <Definitions>
                        <Definition Id="Weather_Widget"
                            DisplayName="Weather Widget"
                            Description="Weather Widget Description"
                            AllowMultiple="true">
                            <Capabilities>
                                <Capability>
                                    <Size Name="small" />
                                </Capability>
                                <Capability>
                                    <Size Name="medium" />
                                </Capability>
                                <Capability>
                                    <Size Name="large" />
                                </Capability>
                            </Capabilities>
                            <ThemeResources>
                                <Icons>
                                    <Icon Path="ProviderAssets\Weather_Icon.png" />
                                </Icons>
                                <Screenshots>
                                    <Screenshot Path="ProviderAssets\Weather_Screenshot.png" DisplayAltText="For accessibility" />
                                </Screenshots>
                                <!-- DarkMode and LightMode are optional -->
                                <DarkMode />
                                <LightMode />
                            </ThemeResources>
                        </Definition>
                        <Definition Id="Counting_Widget"
                                DisplayName="Microsoft Counting Widget"
                                Description="Couting Widget Description">
                            <Capabilities>
                                <Capability>
                                    <Size Name="small" />
                                </Capability>
                            </Capabilities>
                            <ThemeResources>
                                <Icons>
                                    <Icon Path="ProviderAssets\Counting_Icon.png" />
                                </Icons>
                                <Screenshots>
                                    <Screenshot Path="ProviderAssets\Counting_Screenshot.png" DisplayAltText="For accessibility" />
                                </Screenshots>
                                <!-- DarkMode and LightMode are optional -->
                                <DarkMode>

                                </DarkMode>
                                <LightMode />
                            </ThemeResources>
                        </Definition>
                    </Definitions>
                </WidgetProvider>
            </uap3:Properties>
        </uap3:AppExtension>
    </uap3:Extension>
</Extensions>

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

Hinzufügen von Symbolen und anderen Bildern zu Ihrem Paketerstellungsprojekt

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

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

Testen Ihres Widgetanbieters

Stellen Sie sicher, dass Sie im Dropdown-Menü Lösungsplattformen die Architektur ausgewählt haben, die Ihrem Entwicklungscomputer entspricht, z. B. „x64“. Klicken Sie in Projektmappen-Explorer mit der rechten Maustaste auf Ihre Lösung, und wählen Sie Build-Lösung aus. Sobald dies abgeschlossen ist, klicken Sie mit der rechten Maustaste auf Ihr ExampleWidgetProviderPackage und wählen Sie Bereitstellen aus. Im aktuellen Release ist der einzige unterstützte Widgethost das Widgets Board. Um die Widgets anzuzeigen, müssen Sie das Widgets Board öffnen und oben rechts Widgets hinzufügen auswählen. Scrollen Sie nach unten zu den verfügbaren Widgets, und Sie sollten das simulierte Weather Widget und das Microsoft Counting Widget sehen, die in diesem Tutorial erstellt wurden. Klicken Sie auf die Widgets, um sie an Ihr Widgets Board anzuheften und ihre Funktionalität zu testen.

Debuggen Ihres Widgetanbieters

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

Zum Anfügen an den laufenden Prozess:

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

Um den Debugger beim ersten Start automatisch an den Prozess anzuhängen:

  1. Klicken Sie in Visual Studio auf Debuggen -> Weitere Debug-Ziele -> App-Paket debuggen.
  2. Filtern Sie die Pakete, und suchen Sie ihr gewünschtes Widgetanbieterpaket.
  3. Wählen Sie es aus und aktivieren Sie das Kontrollkästchen „Nicht starten, sondern meinen Code beim Start debuggen“.
  4. Klicken Sie auf Anfügen.

Konvertieren der Konsolen-App in eine Windows-App

Um die in dieser exemplarischen Vorgehensweise erstellte Konsolen-App in eine Windows-App zu konvertieren, klicken Sie mit der rechten Maustaste auf die ExampleWidgetProvider Projekt in Projektmappen-Explorer und wählen Sie Properties aus. Ändern Sie unter Application->General den Output-Typ von "Console Application" in "Windows Application".

Ein Screenshot, der die Projekteigenschaften des C#-Widgetanbieters zeigt, mit dem Ausgabetyp auf Windows-Anwendung festgelegt

Veröffentlichen Ihres Widgets

Nachdem Sie Ihr Widget entwickelt und getestet haben, können Sie Ihre App auf dem Microsoft Store veröffentlichen, damit Benutzer Ihre Widgets auf ihren Geräten installieren können. Eine schrittweise Anleitung zum Veröffentlichen einer App finden Sie unter Publish your app in the Microsoft Store.For step by step guidance for publishing an app, see Publish your app in the Microsoft Store.

Die Widgets-Sammlung im Store

Nachdem Ihre App auf dem Microsoft Store veröffentlicht wurde, können Sie anfordern, dass Ihre App in die Store-Sammlung von Widgets aufgenommen wird, mit der Benutzer Apps entdecken können, die Windows Widgets enthalten sind. Informationen zum Übermitteln Ihrer Anforderung finden Sie unter Übermittlung Ihrer Widgetinformationen zur Aufnahme in die Store-Sammlung.

Screenshot des Microsoft Store, der die Widget-Sammlung zeigt, mit der Benutzer Apps entdecken können, die Windows Widgets enthalten.

Implementieren der Widgetanpassung

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

Screenshot eines Widgets mit angezeigtem Anpassungsdialogfeld.

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

  1. Im normalen Betrieb antwortet der Widgetanbieter auf Anforderungen vom Widgethost mit den Vorlagen und Daten-Payloads für die normale Widgeterfahrung.
  2. Der Benutzer klickt im Menü mit den Auslassungspunkten auf die Schaltfläche Widget anpassen.
  3. Das Widget löst das OnCustomizationRequested-Ereignis beim Widgetanbieter aus, um anzuzeigen, dass der Benutzer die Widgetanpassung angefordert hat.
  4. Der Widgetanbieter legt ein internes Flag fest, um anzuzeigen, dass sich das Widget im Anpassungsmodus befindet. Im Anpassungsmodus sendet der Widgetanbieter die JSON-Vorlagen für die Widgetanpassungs-Benutzeroberfläche anstelle der normalen Widget-Benutzeroberfläche.
  5. Im Anpassungsmodus empfängt der Widgetanbieter OnActionInvoked-Ereignisse, wenn der Benutzer mit der Anpassungsbenutzeroberfläche interagiert, und passt seine interne Konfiguration und sein Verhalten auf der Grundlage der Aktionen des Benutzers an.
  6. Wenn die Aktion, die dem OnActionInvoked-Ereignis zugeordnet ist, die appdefinierte Aktion "Beenden der Anpassung" ist, setzt der Widgetanbieter sein internes Flag zurück, um anzugeben, dass er sich nicht mehr im Anpassungsmodus befindet und setzt das Senden der visuellen und Daten-JSON-Vorlagen für die normale Widget-Erfahrung fort. Dabei werden die Änderungen angezeigt, die während der Anpassung angefordert wurden. Es ist möglich, dass der Benutzer die Anpassungsoberfläche schließen kann, ohne auf die appdefinierte Exitanpassungsaktion zu klicken. In diesem Fall wird der IWidgetProviderAnalytics.OnAnalyticsInfoReported ausgelöst, und das WidgetAnalyticsInfoReportedArgs.AnalyticsJson erhält eine interactionKind von "exitCustomization".
  7. Der Widgetanbieter behält die Anpassungsoptionen auf dem Datenträger oder in der Cloud bei, sodass die Änderungen zwischen Aufrufen des Widgetanbieters erhalten bleiben.

Hinweis

Es gibt einen bekannten Fehler mit dem Windows Widget Board für Widgets, die mit dem Windows App SDK erstellt wurden, was bewirkt, dass das Auslassungszeichenmenü nach dem Anzeigen der Anpassungskarte nicht mehr reagiert.

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

Hinweis

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

Aktualisieren des Paketmanifests zum Deklarieren der Anpassungsunterstützung

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

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

Nachverfolgen, wenn sich ein Widget im Anpassungsmodus befindet

Im Beispiel in diesem Artikel wird die Hilfsstruktur CompactWidgetInfo verwendet, um den aktuellen Status unserer aktiven Widgets nachzuverfolgen. Fügen Sie das Feld inCustomization hinzu, mit dem nachverfolgt wird, wann der Widgethost erwartet, dass wir unsere JSON-Anpassungsvorlage anstelle der normalen Widgetvorlage senden.

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

Implementieren von IWidgetProvider2

Das Widgetanpassungsfeature wird über die IWidgetProvider2-Schnittstelle verfügbar gemacht. Aktualisieren Sie die WidgetProvider-Klassendefinition, um diese Schnittstelle zu implementieren.

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

Fügen Sie eine Implementierung für den OnCustomizationRequested-Rückruf der IWidgetProvider2-Schnittstelle hinzu. Bei dieser Methode wird dasselbe Muster verwendet wie bei den anderen verwendeten Rückrufen. Die ID des anzupassenden Widgets rufen wir aus dem WidgetContext ab, anschließend suchen wir die mit dem Widget verknüpfte Hilfsstruktur CompactWidgetInfo und legen das Feld inCustomization auf true fest.

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

Deklarieren Sie jetzt eine Zeichenfolgenvariable, welche die JSON-Vorlage für die Widgetanpassungs-Benutzeroberfläche definiert. In diesem Beispiel gibt es eine Schaltfläche „Zähler zurücksetzen“ und eine Schaltfläche „Anpassung beenden“, die unserem Anbieter signalisiert, dass er zum normalen Widgetverhalten zurückkehren soll. Platzieren Sie diese Definition neben den anderen Vorlagendefinitionen.

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

Anpassungsvorlage in UpdateWidget senden

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

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

Reagieren auf Anpassungsaktionen

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

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

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

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

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