Not
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Den här artikeln beskriver hur du skapar en enkel widgetprovider som implementerar IWidgetProvider--gränssnittet. Metoderna i det här gränssnittet anropas av widgetvärden för att begära data som definierar en widget eller för att låta widgetprovidern svara på en användaråtgärd på en widget. Widgetprovidrar kan stödja en enda widget eller flera widgetar. I det här exemplet definierar vi två olika widgetar. En widget är en simulerad väderwidget som illustrerar några av formateringsalternativen som tillhandahålls av ramverket adaptiva kort. Den andra widgeten visar användaråtgärder och funktionen för anpassat widgettillstånd genom att upprätthålla en räknare som ökas när användaren klickar på en knapp som visas på widgeten.
Den här exempelkoden i den här artikeln är anpassad från Windows App SDK Widgets Sample. Information om hur du implementerar en widgetprovider med C++/WinRT finns i Implementera en widgetprovider i en win32-app (C++/WinRT).
Förutsättningar
- Enheten måste ha aktiverat utvecklarläge. Mer information finns i Inställningar för utvecklare.
- Visual Studio 2022 eller senare med -utveckling för Universal Windows Platform och arbetsbelastning. Se till att lägga till komponenten för C++ (v143) från den valfria listrutan.
Skapa en ny C#-konsolapp
Skapa ett nytt projekt i Visual Studio. I dialogrutan Skapa ett nytt projekt anger du språkfiltret till "C#" och plattformsfiltret till Windows och väljer sedan projektmallen Konsolapp. Ge det nya projektet namnet "ExampleWidgetProvider". När du uppmanas till det anger du .NET-målversionen till 8.0.
När projektet läses in, i Solution Explorer högerklicka på projektnamnet och välj Egenskaper. På sidan Allmänt rullar du ned till Måloperativsystem och väljer "Windows". Under Mål os-version väljer du version 10.0.19041.0 eller senare.
Om du vill uppdatera projektet så att det stöder .NET 8.0 högerklickar du på projektnamnet i Solution Explorer och väljer Redigera projektfil. I PropertyGroup lägger du till följande RuntimeIdentifiers-element .
<RuntimeIdentifiers>win-x86;win-x64;win-arm64</RuntimeIdentifiers>
Observera att den här genomgången använder en konsolapp som visar konsolfönstret när widgeten aktiveras för att möjliggöra enkel felsökning. När du är redo att publicera widgetproviderappen kan du konvertera konsolprogrammet till ett Windows-program genom att följa stegen i Konvertera konsolappen till en Windows-app.
Lägga till referenser till Windows App SDK
Det här exemplet använder det senaste stabila Windows App SDK NuGet-paketet. Högerklicka på Beroenden i Solution Explorer och välj Hantera NuGet-paket.... I NuGet-pakethanteraren väljer du fliken Bläddra och söker efter "Microsoft.WindowsAppSDK". Välj den senaste stabila versionen i listrutan Version och klicka sedan på Installera.
Lägga till en WidgetProvider-klass för att hantera widgetåtgärder
I Visual Studio högerklickar du på projektet ExampleWidgetProvider i Solution Explorer och väljer Lägg till>klass. I dialogrutan Lägg till klass namnger du klassen "WidgetProvider" och klickar på Lägg till. I den genererade WidgetProvider.cs-filen uppdaterar du klassdefinitionen så att den anger att den implementerar gränssnittet IWidgetProvider .
// WidgetProvider.cs
internal class WidgetProvider : IWidgetProvider
Förbereda för att spåra aktiverade widgetar
En widgetprovider kan stödja en enda widget eller flera widgetar. När widgetvärden initierar en åtgärd med widgetprovidern skickar den ett ID för att identifiera widgeten som är associerad med åtgärden. Varje widget har också ett associerat namn och ett tillståndsvärde som kan användas för att lagra anpassade data. I det här exemplet deklarerar vi en enkel hjälpstruktur för att lagra ID, namn och data för varje fäst widget. Widgetar kan också vara i ett aktivt tillstånd, vilket beskrivs i avsnittet Aktivera och inaktivera nedan, och vi spårar det här tillståndet för varje widget med ett booleskt värde. Lägg till följande definition i filen WidgetProvider.cs i namnområdet ExampleWidgetProvider , men utanför klassdefinitionen WidgetProvider .
// WidgetProvider.cs
public class CompactWidgetInfo
{
public string? widgetId { get; set; }
public string? widgetName { get; set; }
public int customState = 0;
public bool isActive = false;
}
I klassen WidgetProvider i WidgetProvider.cs lägger du till en medlem för kartan som behåller listan över aktiverade widgetar med widget-ID:t som nyckel för varje post.
// WidgetProvider.cs
// Class member of WidgetProvider
public static Dictionary<string, CompactWidgetInfo> RunningWidgets = new Dictionary<string, CompactWidgetInfo>();
Deklarera JSON-strängar för widgetmall
I det här exemplet deklareras några statiska strängar för att definiera JSON-mallarna för varje widget. För enkelhetens skull lagras dessa mallar i medlemsvariablerna i klassen WidgetProvider . Om du behöver en allmän lagring för mallarna kan de ingå som en del av programpaketet: Åtkomst till paketfiler. Information om hur du skapar JSON-dokumentet för widgetmallen finns i Skapa en widgetmall med Adaptive Card Designer.
I den senaste versionen kan appar som implementerar Windows-widgetar anpassa rubriken som visas för widgeten i Widgets Board, vilket åsidosätter standardpresentationen. Mer information finns i Anpassa området för widgethuvuden.
Anmärkning
I den senaste versionen kan appar som implementerar Windows-widgetar välja att fylla i widgetinnehållet med HTML som hanteras från en angiven URL i stället för att tillhandahålla innehåll i schemaformatet Adaptive Card i JSON-nyttolasten som skickas från providern till Widgets Board. Widgetprovidrar måste fortfarande tillhandahålla en JSON-nyttolast för adaptiva kort, så implementeringsstegen i den här genomgången gäller för webbwidgetar. För mer information, se leverantörer av webbwidgets.
// 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"
}
""";
Implementera IWidgetProvider-metoderna
I de kommande avsnitten implementerar vi metoderna i IWidgetProvider--gränssnittet. Hjälpmetoden UpdateWidget som anropas i flera av dessa metodimplementeringar visas senare i den här artikeln.
Anmärkning
Objekt som skickas till återanropsmetoderna i IWidgetProvider--gränssnittet garanteras endast vara giltiga inom själva återanropet. Du bör inte lagra referenser till dessa objekt eftersom deras beteende utanför kontexten för återanropet är odefinierat.
SkapaWidget
Widgetvärden kallar på CreateWidget när användaren har fäst en av dina appars widgets i widgetvärden. Först hämtar den här metoden ID och namnet på den associerade widgeten och lägger till en ny instans av vår hjälpstruktur, CompactWidgetInfo, till samlingen med aktiverade widgetar. Sedan skickar vi den första mallen och data för widgeten, som är inkapslad i hjälpmetoden 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);
}
Ta bort widget
Widgetvärden anropar DeleteWidget när användaren har tagit bort en av appens widgetar från värdprogrammet. När detta inträffar tar vi bort den associerade widgeten från vår lista över aktiverade widgetar så att vi inte skickar några ytterligare uppdateringar för widgeten.
// WidgetProvider.cs
public void DeleteWidget(string widgetId, string customState)
{
RunningWidgets.Remove(widgetId);
if(RunningWidgets.Count == 0)
{
emptyWidgetListEvent.Set();
}
}
I det här exemplet, förutom att ta bort widgeten med angiven från listan över aktiverade widgetar, kontrollerar vi också om listan nu är tom, och i så fall anger vi en händelse som ska användas senare för att appen ska kunna avslutas när det inte finns några aktiverade widgetar. Inuti klassdefinitionen lägger du till deklarationen för ManualResetEvent och en offentlig åtkomstfunktion.
// WidgetProvider.cs
static ManualResetEvent emptyWidgetListEvent = new ManualResetEvent(false);
public static ManualResetEvent GetEmptyWidgetListEvent()
{
return emptyWidgetListEvent;
}
OnActionInvoked
Widgetvärden anropar OnActionInvoked när användaren utför en åtgärd som du har definierat i din widgetmall. För räknarens widget som används i detta exempel, deklarerades en åtgärd där ett verb värde av "inc" angavs i JSON-mallen för widgeten. Koden för widgetprovidern använder det här verbet värde för att avgöra vilken åtgärd som ska vidtas som svar på användarinteraktionen.
...
"actions": [
{
"type": "Action.Execute",
"title": "Increment",
"verb": "inc"
}
],
...
I metoden OnActionInvoked, hämtar du verbvärdet genom att kontrollera Verb-egenskapen hos WidgetActionInvokedArgs som skickas in i metoden. Om verbet är "inc" vet vi att vi kommer att öka antalet i det anpassade tillståndet för widgeten. Från WidgetActionInvokedArgshämtar du objektet WidgetContext och sedan WidgetId för att hämta ID:t för widgeten som uppdateras. Hitta posten i vår aktiverade widgetkarta med det angivna ID:t och uppdatera sedan det anpassade tillståndsvärdet som används för att lagra antalet steg. Uppdatera slutligen widgetinnehållet med det nya värdet med hjälpfunktionen 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);
}
}
}
Information om Action.Execute syntax för adaptiva kort finns i Action.Execute. Vägledning om hur du utformar interaktion för widgetar finns i Vägledning för interaktionsdesign för widgetar
OnWidgetContextFörändrad
I den aktuella versionen anropas OnWidgetContextChanged bara när användaren ändrar storleken på en fäst widget. Du kan välja att returnera en annan JSON-mall eller -data till widgetvärden, beroende på storleken som begärs. Du kan också utforma JSON-mallen så att den stöder alla tillgängliga storlekar med villkorsstyrd återgivning baserat på värdet för host.widgetSize. Om du inte behöver skicka en ny mall eller data för att ta hänsyn till storleksändringen kan du använda OnWidgetContextChanged- i telemetrisyfte.
// 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);
}
}
Aktivera och inaktivera
Metoden Aktivera anropas för att meddela widgetprovidern att widgetvärden för närvarande är intresserad av att ta emot uppdaterat innehåll från providern. Det kan till exempel innebära att användaren för närvarande aktivt tittar på widgetvärden. Metoden Inaktivera anropas för att meddela widgetprovidern att widgetvärden inte längre begär innehållsuppdateringar. Dessa två metoder definierar ett fönster där widgetvärden är mest intresserad av att visa det mest up-to-date-innehåll. Widgetprovidrar kan när som helst skicka uppdateringar till widgeten, till exempel som svar på ett push-meddelande, men precis som med alla bakgrundsaktiviteter är det viktigt att balansera tillhandahållande av up-to-date-innehåll med resursproblem som batteritid.
Aktivera och Inaktivera anropas för varje widget. Det här exemplet spårar den aktiva statusen för varje widget i hjälpstrukturen CompactWidgetInfo. I metoden Aktivera anropar vi hjälpmetoden UpdateWidget för att uppdatera widgeten. Observera att tidsperioden mellan Aktivera och Inaktivera kan vara liten, så vi rekommenderar att du försöker göra kodsökvägen för widgetuppdatering så snabb som möjligt.
// 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;
}
}
Uppdatera en widget
Definiera hjälpmetoden UpdateWidget för att uppdatera en aktiverad widget. I det här exemplet kontrollerar vi namnet på widgeten i CompactWidgetInfo hjälp struct som skickas till metoden och anger sedan lämplig mall och data-JSON baserat på vilken widget som uppdateras. En WidgetUpdateRequestOptions initialiseras med mallen, data och det anpassade tillståndet för widgeten som uppdateras. Anropa WidgetManager::GetDefault för att hämta en instans av klassen WidgetManager och anropa sedan UpdateWidget för att skicka uppdaterade widgetdata till widgetvärden.
// 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);
}
Initiera listan över aktiverade widgetar vid start
När vår widgetprovider först initieras är det en bra idé att fråga WidgetManager om det finns några widgetar som vår leverantör för närvarande betjänar. Det hjälper till att återställa appen till det tidigare tillståndet om datorn startas om eller om providern kraschar. Anropa WidgetManager.GetDefault för att hämta standardinstansen för widgethanteraren för appen. Anropa sedan GetWidgetInfos, som returnerar en matris med WidgetInfo- objekt. Kopiera widget-ID:n, namnen och det anpassade tillståndet till hjälpstrukturen CompactWidgetInfo och spara dem i medlemsvariabeln RunningWidgets. Klistra in följande kod i klassdefinitionen för klassen 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;
}
}
}
Implementera en klassfabrik som instansierar WidgetProvider vid begäran
För att widgetvärden ska kunna kommunicera med vår widgetprovider måste vi anropa CoRegisterClassObject. Den här funktionen kräver att vi skapar en implementering av IClassFactory som skapar ett klassobjekt för klassen WidgetProvider . Vi implementerar vår klassfabrik i en fristående hjälpklass.
I Visual Studio högerklickar du på projektet ExampleWidgetProvider i Solution Explorer och väljer Lägg till>klass. I dialogrutan Lägg till klass ger du klassen namnet "FactoryHelper" och klickar på Lägg till.
Ersätt innehållet i FactoryHelper.cs-filen med följande kod. Den här koden definierar IClassFactory-gränssnittet och implementerar två metoder, CreateInstance och LockServer. Den här koden är typisk för implementering av en klassfabrik och är inte specifik för funktionerna i en widgetprovider, förutom att vi anger att klassobjektet som skapas implementerar gränssnittet IWidgetProvider .
// 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;
}
}
Skapa ett GUID som representerar CLSID för widgetprovidern
Därefter måste du skapa ett GUID som representerar en CLSID som ska användas för att identifiera widgetleverantören för COM-aktivering. Samma värde används också när du paketerar din app. Generera ett GUID i Visual Studio genom att gå till Tools –>Skapa GUID-. Välj alternativet registerformat och klicka på Kopiera och klistra sedan in det i en textfil så att du kan kopiera det senare.
Registrera klassobjektet för widgetprovidern med OLE
I den Program.cs filen för vår körbara fil anropar vi CoRegisterClassObject för att registrera vår widgetprovider med OLE, så att widgetvärden kan interagera med den. Ersätt innehållet i Program.cs med följande kod. Den här koden importerar funktionen CoRegisterClassObject och anropar den, samtidigt som det skickar det WidgetProviderFactory- gränssnitt vi definierade i ett tidigare steg. Se till att uppdatera CLSID_Factory variabeldeklarationen så att den använder det GUID som du genererade i föregående steg.
// 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);
}
Observera att det här kodexemplet importerar funktionen GetConsoleWindow för att avgöra om appen körs som ett konsolprogram, standardbeteendet för den här genomgången. Om funktionen returnerar en giltig pekare skriver vi felsökningsinformation till konsolen. Annars körs appen som en Windows-app. I så fall väntar vi på händelsen som vi angav i metoden DeleteWidget när listan över aktiverade widgetar är tom och vi avslutar appen. Information om hur du konverterar exempelkonsolappen till en Windows-app finns i Konvertera konsolappen till en Windows-app.
Paketera widget-leverantörsappen
I den aktuella versionen kan endast paketerade appar registreras som widgetproviders. Följande steg tar dig igenom processen för att paketera din app och uppdatera appmanifestet för att registrera din app med operativsystemet som en widgetprovider.
Skapa ett MSIX-paketeringsprojekt
I Solution Explorerhögerklickar du på lösningen och väljer Lägg till>Nytt projekt.... I dialogrutan Lägg till ett nytt projekt väljer du mallen "Windows Application Packaging Project" och klickar på Nästa. Ange projektnamnet till "ExampleWidgetProviderPackage" och klicka på Skapa. När du uppmanas till det anger du målversionen till version 1809 eller senare och klickar på OK. Högerklicka sedan på projektet ExampleWidgetProviderPackage och välj Lägg till ->Projektreferens. Välj projektet ExampleWidgetProvider och klicka på OK.
Lägg till paketreferensen för Windows App SDK i paketeringsprojektet
Du måste lägga till en referens till Windows App SDK-nuget-paketet i MSIX-paketeringsprojektet. I Solution Explorerdubbelklickar du på projektet ExampleWidgetProviderPackage för att öppna filen ExampleWidgetProviderPackage.wapproj. Lägg till följande XML i elementet Project.
<!--ExampleWidgetProviderPackage.wapproj-->
<ItemGroup>
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.2.221116.1">
<IncludeAssets>build</IncludeAssets>
</PackageReference>
</ItemGroup>
Anmärkning
Kontrollera att version som anges i elementet PackageReference matchar den senaste stabila versionen som du refererade till i föregående steg.
Om rätt version av Windows App SDK redan är installerad på datorn och du inte vill paketera SDK-körningen i paketet kan du ange paketberoendet i package.appxmanifest-filen för Projektet 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>
...
Uppdatera paketmanifestet
I Solution Explorer högerklicka på filen Package.appxmanifest och välj Visa kod för att öppna xml-manifestfilen. Därefter måste du lägga till några namnområdesdeklarationer för de apppakettillägg som vi kommer att använda. Lägg till följande namnområdesdefinitioner i elementet Package på den översta nivån.
<!-- Package.appmanifest -->
<Package
...
xmlns:uap3="http://schemas.microsoft.com/appx/manifest/uap/windows10/3"
xmlns:com="http://schemas.microsoft.com/appx/manifest/com/windows10"
I elementet Application skapar du ett nytt tomt element med namnet Extensions. Kom ihåg att detta ska komma efter den avslutande taggen för uap:VisualElements.
<!-- Package.appxmanifest -->
<Application>
...
<Extensions>
</Extensions>
</Application>
Det första tillägget vi behöver lägga till är ComServer-tillägget. Detta registrerar startpunkten för körprogrammet i operativsystemet. Det här tillägget motsvarar en paketerad app för registrering av en COM-server genom att ange en registernyckel och är inte avsett enbart för widgetleverantörer. Lägg till följande com:Extension-element som ett underordnat element till Extensions-elementet. Ändra GUID i attributet Id för elementet com:Class till det GUID som du genererade i ett tidigare steg.
<!-- 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>
Lägg sedan till tillägget som registrerar appen som en widgetprovider. Klistra in elementet uap3:Extension i kodexemplet nedan, som ett underordnat element till Extensions. Ersätt attributet ClassId för elementet COM med det GUID som du använde i föregående steg.
<!-- 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>
Detaljerade beskrivningar och formatinformation för alla dessa element finns i XML-format för widgetproviderpaketmanifestet.
Lägga till ikoner och andra bilder i paketeringsprojektet
I Solution Explorerhögerklickar du på ExampleWidgetProviderPackage och väljer Lägg till ->Ny mapp. Namnge den här mappen ProviderAssets eftersom det här är vad som användes i Package.appxmanifest från föregående steg. Här lagrar vi våra Ikoner och Skärmbilder för våra widgetar. När du har lagt till önskade ikoner och skärmbilder kontrollerar du att bildnamnen matchar vad som kommer efter Path=ProviderAssets\ i Package.appxmanifest, annars visas inte widgetarna i widgetvärden.
Information om designkraven för skärmbilder och namngivningskonventionerna för lokaliserade skärmbilder finns i Integrera med widgetväljaren.
Testa din widgetleverantör
Kontrollera att du har valt den arkitektur som matchar utvecklingsdatorn från listrutan Lösningsplattformar, till exempel "x64". Högerklicka på lösningen i Solution Exploreroch välj Build Solution. När detta är klart högerklickar du på ExampleWidgetProviderPackage och väljer Lansera. Den enda widgetvärd som stöds i den aktuella versionen är Widgets Board. Om du vill se widgetarna måste du öppna Widgets Board och välja Lägg till widgetar längst upp till höger. Rulla längst ner bland de tillgängliga widgetarna och du bör se Weather Widget och Microsoft Counting Widget som skapades i den här handledningen. Klicka på widgetarna för att fästa dem på widgetkortet och testa deras funktioner.
Felsöka widgetleverantören
När du har fäst dina widgetar startar Widget Platform ditt program för widgetprovidern för att ta emot och skicka relevant information om widgeten. Om du vill felsöka widgeten som körs kan du antingen koppla ett felsökningsprogram till det program som kör widgetprovidern eller så kan du konfigurera Visual Studio för att automatiskt börja felsöka widgetproviderprocessen när den har startats.
För att ansluta till den process som körs:
- I Visual Studio klickar du på Felsöka –> Anslut till process.
- Filtrera processerna och hitta önskat program för widgetprovidern.
- Anslut felsökningsprogrammet.
För att automatiskt koppla felsökaren till processen när den startas:
- I Visual Studio klickar du på Felsökning –> Andra felsökningsmål –> Felsöka installerat apppaket.
- Filtrera paketen och hitta önskat widgetproviderpaket.
- Markera den och markera kryssrutan med texten Starta inte, men felsök min kod när den startas.
- Klicka på Bifoga.
Konvertera konsolappen till en Windows-app
Om du vill konvertera konsolappen som skapades i den här genomgången till en Windows-app högerklickar du på projektet ExampleWidgetProvider i Solution Explorer och väljer Egenskaper. Under Program-Allmänt ändrar > från "Konsolprogram" till "Windows-program".
Publicera widgeten
När du har utvecklat och testat widgeten kan du publicera din app på Microsoft Store för att användarna ska kunna installera widgetarna på sina enheter. Stegvisa anvisningar för att publicera en app finns i Publicera din app i Microsoft Store-.
Widgets Store-samlingen
När din app har publicerats i Microsoft Store kan du begära att din app inkluderas i Widgets Store-samlingen som hjälper användare att identifiera appar med Windows Widgets. För att skicka en förfrågan, se Skicka din Widget-information för tillägg till Store-samlingen.
Implementera anpassning av widgetar
Från och med Windows App SDK 1.4 kan widgetar stödja användaranpassning. När den här funktionen implementeras läggs alternativet Anpassa widget till på ellipsmenyn ovanför alternativet Ta bort widgeten.
Följande steg sammanfattar processen för anpassning av widgetar.
- I normal drift svarar widgetprovidern på begäranden från widgetvärden med mallen och datanyttolaster för den vanliga widgetupplevelsen.
- Användaren klickar på knappen Anpassa widget i ellipsmenyn.
- Widgeten genererar OnCustomizationRequested händelse på widgetprovidern för att indikera att användaren har begärt anpassningsupplevelsen för widgeten.
- Widgetprovidern anger en intern flagga som anger att widgeten är i anpassningsläge. I anpassningsläge skickar widgetprovidern JSON-mallarna för användargränssnittet för widgetanpassning i stället för det vanliga widgetgränssnittet.
- I anpassningsläge tar widgetprovidern emot OnActionInvoked händelser när användaren interagerar med anpassningsgränssnittet och justerar sin interna konfiguration och sitt beteende baserat på användarens åtgärder.
- När åtgärden som är associerad med händelsen OnActionInvoked är den appdefinierade åtgärden "avsluta anpassning" återställer widgetprovidern sin interna flagga för att indikera att den inte längre är i anpassningsläge och återupptar sändningen av JSON-mallar för visuella objekt och data för den vanliga widgetupplevelsen, vilket återspeglar de ändringar som begärdes under anpassningen. Det är möjligt för användaren att stänga anpassningsupplevelsen utan att klicka på den appdefinierade avslutningsanpassningsåtgärden. I det här fallet kommer IWidgetProviderAnalytics.OnAnalyticsInfoReported att utlöstas och WidgetAnalyticsInfoReportedArgs.AnalyticsJson kommer att ha en interaktionsTyp av "exitCustomization".
- Widgetprovidern bevarar anpassningsalternativen till disken eller molnet så att ändringarna bevaras mellan widgetproviderns anrop.
Anmärkning
Det finns en känd bugg med Windows Widget Board, för widgetar som skapats med hjälp av Windows App SDK, som gör att ellipsmenyn inte svarar efter att anpassningskortet har visats.
I typiska scenarier för widgetanpassning väljer användaren vilka data som visas på widgeten eller justerar den visuella presentationen av widgeten. För enkelhetens skull lägger exemplet i det här avsnittet till anpassningsbeteende som gör att användaren kan återställa räknaren för räknarwidgeten som implementerades i föregående steg.
Anmärkning
Widgetanpassning stöds endast i Windows App SDK 1.4 och senare. Se till att du uppdaterar referenserna i projektet till den senaste versionen av Nuget-paketet.
Uppdatera paketmanifestet för att deklarera anpassningsstöd
Om du vill att widgetvärden ska veta att widgeten stöder anpassning lägger du till attributet IsCustomizable i definitionselementet för widgeten och anger det till sant.
...
<Definition Id="Counting_Widget"
DisplayName="Microsoft Counting Widget"
Description="CONFIG counting widget description"
IsCustomizable="true">
...
Spåra när en widget är i anpassningsläge
I exemplet i den här artikeln används hjälp struct CompactWidgetInfo för att spåra det aktuella tillståndet för våra aktiva widgetar. Lägg till fältet inCustomization, som används för att spåra när widgetvärden förväntar sig att vi skickar vår json-mall för anpassning i stället för den vanliga widgetmallen.
// 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;
}
Implementera IWidgetProvider2
Anpassningsfunktionen för widgeten exponeras via gränssnittet IWidgetProvider2 . Uppdatera klassdefinitionen WidgetProvider för att implementera det här gränssnittet.
// WidgetProvider.cs
internal class WidgetProvider : IWidgetProvider, IWidgetProvider2
Lägg till en implementering för OnCustomizationRequested-återanropet för gränssnittet IWidgetProvider2 . Den här metoden använder samma mönster som de andra återanrop som vi har använt. Vi får ID:t för widgeten som ska anpassas från WidgetContext- och hittar CompactWidgetInfo- hjälpstruktur som är associerad med den widgeten och sätter inCustomization- fältet till sant.
// 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);
}
}
Deklarera nu en strängvariabel som definierar JSON-mallen för användargränssnittet för widgetanpassning. I det här exemplet har vi knappen "Återställ räknare" och knappen "Avsluta anpassning" som kommer att signalera att vår leverantör återgår till det vanliga widgetbeteendet. Placera den här definitionen bredvid de andra malldefinitionerna.
// 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""
}";
Skicka anpassningsmall i UpdateWidget
Därefter uppdaterar vi vår UpdateWidget- hjälpmetod som skickar våra data och visuella JSON-mallar till widgetvärden. När vi uppdaterar räknarwidgeten skickar vi antingen den vanliga widgetmallen eller anpassningsmallen beroende på värdet för fältet inCustomization. För korthet utelämnas kod som inte är relevant för anpassning i det här kodfragmentet.
// 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);
}
Svara på anpassningsåtgärder
När användare interagerar med indata i vår anpassningsmall anropas samma OnActionInvoked-hanterare som när användaren interagerar med den vanliga widgetupplevelsen. För att stödja anpassning letar vi efter verben "reset" och "exitCustomization" från vår JSON-mall för anpassning. Om åtgärden är för knappen Återställ räknare återställer vi räknaren som finns i fältet customState i vår hjälp struct till 0. Om åtgärden är för knappen "Avsluta anpassning" anger vi fältet inCustomization till false så att när vi anropar UpdateWidgetskickar vår hjälpmetod de vanliga JSON-mallarna och inte anpassningsmallen.
// 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);
}
}
}
När du distribuerar widgeten bör du nu se knappen Anpassa widget på ellipsmenyn. Om du klickar på knappen Anpassa visas din anpassningsmall.
Klicka på knappen Återställ räknaren för att återställa räknaren till 0. Klicka på knappen Avsluta anpassning för att återgå till widgetens vanliga beteende.
Windows developer