Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Este artigo o orienta na criação de um provedor de widget simples que implementa a interface IWidgetProvider . Os métodos dessa interface são invocados pelo host do widget para solicitar os dados que definem um widget ou para permitir que o provedor de widgets responda a uma ação do usuário em um widget. Os provedores de widgets podem suportar um único widget ou vários widgets. Neste exemplo, definiremos dois widgets diferentes. Um widget é um falso widget de meteorologia que ilustra algumas das opções de formatação fornecidas pelo framework Cartões Adaptativos. O segundo widget demonstrará as ações do usuário e o recurso de estado do widget personalizado, mantendo um contador que é incrementado sempre que o usuário clica em um botão exibido no widget.
Este código de exemplo neste artigo é adaptado do SDK de Aplicações Windows Widgets Sample. Para implementar um provedor de widgets usando C++/WinRT, consulte Implementar um provedor de widgets em um aplicativo win32 (C++/WinRT).
Pré-requisitos
- Seu dispositivo deve ter o modo de desenvolvedor ativado. Para obter mais informações, consulte Configurações para desenvolvedores.
- Visual Studio 2026 ou versões posteriores com o workload de desenvolvimento de aplicações WinUI. Certifique-se de adicionar o componente para C++ (v143) a partir da lista suspensa opcional.
Criar um novo aplicativo de console em C#
No Visual Studio, crie um novo projeto. No diálogo Criar um novo projeto, defina o filtro de linguagem para "C#" e o filtro da plataforma para Windows, depois selecione o modelo de projeto da Consola App. Nomeie o novo projeto como "ExampleWidgetProvider". Quando solicitado, defina a versão alvo de .NET para 8.0.
Quando o projeto carrega, em Explorador de Soluções clique com o botão direito no nome do projeto e selecione Properties. Na página Geral, desça até Target OS e selecione "Windows". Em Versãodo SO de destino , selecione a versão 10.0.19041.0 ou posterior.
Para atualizar o projeto para suportar .NET 8.0, em Explorador de Soluções clique com o botão direito no nome do projeto e selecione Editar Ficheiro de Projeto. Dentro de PropertyGroup, adicione o seguinte RuntimeIdentifiers elemento.
<RuntimeIdentifiers>win-x86;win-x64;win-arm64</RuntimeIdentifiers>
Note que este tutorial utiliza uma aplicação de consola que exibe a janela da consola quando o widget é ativado, a fim de facilitar a depuração. Quando estiver pronto para publicar a sua aplicação fornecedora de widgets, pode converter a aplicação de consola numa aplicação de Windows seguindo os passos em Converter a sua aplicação de consola numa aplicação de Windows.
Adicionar referências ao SDK de Aplicações Windows
Esta amostra utiliza o mais recente pacote estável do SDK de Aplicações Windows NuGet. Em
Adicionar uma classe WidgetProvider para lidar com operações de widget
No Visual Studio, clique com o botão direito no projeto ExampleWidgetProvider em Explorador de Soluções e selecione Add->Class. Na caixa de diálogo Adicionar classe, nomeie a classe "WidgetProvider" e clique em Adicionar. No arquivo de WidgetProvider.cs gerado, atualize a definição de classe para indicar que ele implementa a interface
// WidgetProvider.cs
internal class WidgetProvider : IWidgetProvider
Prepare-se para rastrear widgets ativados
Um provedor de widgets pode suportar um único widget ou vários widgets. Sempre que o host do widget inicia uma operação com o provedor do widget, ele passa um ID para identificar o widget associado à operação. Cada widget também tem um nome associado e um valor de estado que pode ser usado para armazenar dados personalizados. Neste exemplo, declararemos uma estrutura auxiliar simples para armazenar a ID, o nome e os dados de cada widget fixo. Os widgets também podem estar em um estado ativo, o que é discutido na seção
// WidgetProvider.cs
public class CompactWidgetInfo
{
public string? widgetId { get; set; }
public string? widgetName { get; set; }
public int customState = 0;
public bool isActive = false;
}
Na definição da classe WidgetProvider em WidgetProvider.cs, adicione um membro para o mapa que manterá a lista de widgets ativos, usando o ID do widget como a chave para cada entrada.
// WidgetProvider.cs
// Class member of WidgetProvider
public static Dictionary<string, CompactWidgetInfo> RunningWidgets = new Dictionary<string, CompactWidgetInfo>();
Declarar cadeias de caracteres JSON do modelo de widget
Este exemplo declarará algumas cadeias de caracteres estáticas para definir os modelos JSON para cada widget. Por conveniência, esses modelos são armazenados nas variáveis membro da classe
Na versão mais recente, as aplicações que implementam widgets do Windows podem personalizar o cabeçalho que é exibido para o seu widget no Quadro de Widgets, sobrescrevendo a apresentação predefinida. Para obter mais informações, consulte Personalizar a área de cabeçalho do widget.
Observação
Na versão mais recente, as aplicações que implementam widgets do Windows podem optar por preencher o conteúdo do widget com HTML servido a partir de uma URL especificada, em vez de fornecer conteúdo no formato de esquema do Adaptive Card no payload JSON passado do fornecedor para o Widgets Board. Os provedores de widgets ainda devem fornecer uma carga JSON do Adaptive Card, portanto, as etapas de implementação neste passo a passo são aplicáveis aos widgets da Web. Para obter mais informações, consulte provedores de widgets da Web.
// 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"
}
""";
Implementar os métodos IWidgetProvider
Nas próximas seções, implementaremos os métodos do IWidgetProvider interface. O método auxiliar UpdateWidget que é chamado em várias dessas implementações de método será mostrado posteriormente neste artigo.
Observação
Os objetos passados para os métodos de retorno de chamada da interface IWidgetProvider têm a sua validade garantida apenas dentro do retorno de chamada. Você não deve armazenar referências a esses objetos porque seu comportamento fora do contexto do retorno de chamada é indefinido.
CriarWidget
O anfitrião do widget chama CreateWidget quando o utilizador fixa um dos widgets da sua aplicação no anfitrião do widget. Primeiro, esse método obtém a ID e o nome do widget associado e adiciona uma nova instância de nossa estrutura auxiliar, CompactWidgetInfo, à coleção de widgets habilitados. Em seguida, enviamos o modelo inicial e os dados para o widget, que é encapsulado no UpdateWidget método auxiliar.
// 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);
}
Eliminar Widget
O host do widget chama DeleteWidget quando o utilizador retira um dos widgets do seu aplicativo do host do widget. Quando isso ocorrer, removeremos o widget associado da nossa lista de widgets ativados para que não enviemos mais atualizações para esse widget.
// WidgetProvider.cs
public void DeleteWidget(string widgetId, string customState)
{
RunningWidgets.Remove(widgetId);
if(RunningWidgets.Count == 0)
{
emptyWidgetListEvent.Set();
}
}
Neste exemplo, além de remover o widget com o especificado da lista de widgets habilitados, também verificamos se a lista agora está vazia e, em caso afirmativo, definimos um evento que será usado posteriormente para permitir que o aplicativo saia quando não houver widgets habilitados. Dentro da sua definição de classe, adicione a declaração do ManualResetEvent e uma função de acessador público.
// WidgetProvider.cs
static ManualResetEvent emptyWidgetListEvent = new ManualResetEvent(false);
public static ManualResetEvent GetEmptyWidgetListEvent()
{
return emptyWidgetListEvent;
}
OnActionInvoked
O host do widget chama OnActionInvoked quando o usuário interage com uma ação definida no modelo de widget. Para o widget de contador utilizado neste exemplo, foi declarada uma ação com um valor de verbo de "inc" no modelo JSON para o widget. O código do provedor de widget usará este valor de verbo para determinar que ação tomar em resposta à interação do utilizador.
...
"actions": [
{
"type": "Action.Execute",
"title": "Increment",
"verb": "inc"
}
],
...
No método OnActionInvoked, obtenha o valor do verbo verificando a propriedade Verb dos WidgetActionInvokedArgs passados para o método. Se o verbo é "inc", então sabemos que vamos incrementar a contagem no estado personalizado para o widget. No
// 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);
}
}
}
Para informações sobre a sintaxe Action.Execute para Cartões Adaptativos, veja Action.Execute. Para obter orientação sobre como projetar interação para widgets, consulte Diretrizes de design de interação de widgets
OnWidgetContextChanged
Na versão atual, OnWidgetContextChanged só é chamado quando o usuário altera o tamanho de um widget fixo. Você pode optar por retornar um modelo/dado JSON diferente para o host do widget, dependendo do tamanho solicitado. Você também pode projetar o modelo JSON para suportar todos os tamanhos disponíveis usando renderização condicional com base no valor de host.widgetSize. Se você não precisar enviar um novo modelo ou dados para levar em conta a alteração de tamanho, poderá usar o OnWidgetContextChanged para fins de telemetria.
// 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);
}
}
Ativar e Desativar
O método Activate é chamado para notificar o provedor de widgets de que o host do widget está atualmente interessado em receber conteúdo atualizado do provedor. Por exemplo, isso pode significar que o usuário está visualizando ativamente o host do widget. O método Deactivate é chamado para notificar o provedor de widgets de que o host do widget não está mais solicitando atualizações de conteúdo. Esses dois métodos definem uma janela na qual o host do widget está mais interessado em mostrar o conteúdo mais atualizado up-to. Os provedores de widgets podem enviar atualizações para o widget a qualquer momento, como em resposta a uma notificação por push, mas, como em qualquer tarefa em segundo plano, é importante equilibrar o fornecimento de conteúdo de up-todata com preocupações com recursos, como a duração da bateria.
Ativar e Desativar são chamados por widget. Este exemplo monitoriza o estado ativo de cada widget na estrutura auxiliar CompactWidgetInfo. No método Activate, chamamos o UpdateWidget método auxiliar para atualizar nosso widget. Observe que a janela de tempo entre Ativar e Desativar pode ser pequena, pelo que é recomendável tentar fazer com que o percurso do código de atualização do widget seja o mais rápido possível.
// 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;
}
}
Atualizar um widget
Defina o UpdateWidget método auxiliar para atualizar um widget habilitado. Neste exemplo, verificamos o nome do widget no CompactWidgetInfo struct auxiliar passado para o método e, em seguida, definimos o modelo apropriado e JSON de dados com base em qual widget está sendo atualizado. Um WidgetUpdateRequestOptions é inicializado com o modelo, os dados e o estado personalizado do widget a ser atualizado. Chame WidgetManager::GetDefault para obter uma instância da classe WidgetManager e, em seguida, execute UpdateWidget para enviar os dados do widget atualizado ao anfitrião do widget.
// 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);
}
Inicializar a lista de widgets ativados na inicialização
Quando nosso provedor de widgets é inicializado pela primeira vez, é uma boa ideia perguntar ao WidgetManager se há algum widget em execução que nosso provedor está servindo no momento. Isso ajudará a recuperar o aplicativo para o estado anterior no caso de reiniciar o computador ou o provedor falhar. Chame WidgetManager.GetDefault para obter a instância padrão do gerenciador de widgets para o aplicativo. Em seguida, chame GetWidgetInfos, que retorna uma matriz de objetos WidgetInfo . Copie os IDs, nomes e estado personalizado dos widgets para a estrutura auxiliar CompactWidgetInfo e guarde-os na variável membro RunningWidgets. Cole o código a seguir na definição de classe para a classe 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;
}
}
}
Implementar uma fábrica de classes que instanciará o WidgetProvider mediante solicitação
Para que o host do widget se comunique com nosso provedor de widgets, devemos chamar CoRegisterClassObject. Esta função requer que criemos uma implementação do
No Visual Studio, clique com o botão direito no projeto ExampleWidgetProvider em Explorador de Soluções e selecione Add->Class. Na caixa de diálogo Adicionar classe, nomeie a classe "FactoryHelper" e clique em Adicionar.
Substitua o conteúdo do arquivo FactoryHelper.cs pelo código a seguir. Este código define o IClassFactory interface e implementa seus dois métodos, CreateInstance e LockServer. Esse código é típico clichê para implementar uma fábrica de classes e não é específico para a funcionalidade de um provedor de widgets, exceto que indicamos que o objeto de classe que está sendo criado implementa a interface
// 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;
}
}
Crie um GUID representando o CLSID para seu provedor de widgets
Em seguida, precisas criar um GUID representando o CLSID que será usado para identificar o teu fornecedor de widgets para ativação do COM. O mesmo valor também será usado ao empacotar seu aplicativo. Gera um GUID em Visual Studio indo a Tools->Create GUID. Selecione a opção de formato do Registro e clique em Copiar e cole-a em um arquivo de texto para que você possa copiá-lo mais tarde.
Registrar o objeto de classe do provedor de widget com OLE
No arquivo Program.cs para nosso executável, chamaremos CoRegisterClassObject para registrar nosso provedor de widgets com OLE, para que o host do widget possa interagir com ele. Substitua o conteúdo do Program.cs pelo código a seguir. Este código importa a função CoRegisterClassObject e chama-a, passando na interface WidgetProviderFactory que definimos numa etapa anterior. Certifique-se de atualizar a declaração de variável CLSID_Factory para usar o GUID gerado na etapa anterior.
// 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);
}
Observe que este exemplo de código importa a função
Empacote seu aplicativo provedor de widgets
Na versão atual, apenas aplicativos empacotados podem ser registrados como provedores de widgets. As etapas a seguir irão guiá-lo através do processo de empacotamento de seu aplicativo e atualização do manifesto do aplicativo para registrar seu aplicativo no sistema operacional como um provedor de widgets.
Criar um projeto de empacotamento MSIX
Em Explorador de Soluções, clique com o botão direito na sua solução e selecione Adicionar->Novo Projeto.... No diálogo Adicionar um novo projeto, selecione o modelo "Windows Application Packaging Project" e clique em Seguinte. Defina o nome do projeto como "ExampleWidgetProviderPackage" e clique em Criar. Quando solicitado, defina a versão de destino para a versão 1809 ou posterior e clique em OK. Em seguida, clique com o botão direito no projeto ExampleWidgetProviderPackage e selecione Add->Project reference. Selecione o projeto ExampleWidgetProvider e clique em OK.
Adicionar referência ao pacote do SDK de Aplicações Windows ao projeto de empacotamento
Precisa de adicionar uma referência ao pacote NuGet do SDK de Aplicações Windows ao projeto de empacotamento MSIX. Em Explorador de Soluções, clique duas vezes no projeto ExampleWidgetProviderPackage para abrir o ficheiro ExampleWidgetProviderPackage.wapproj. Adicione o seguinte xml dentro do elemento Project.
<!--ExampleWidgetProviderPackage.wapproj-->
<ItemGroup>
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.2.221116.1">
<IncludeAssets>build</IncludeAssets>
</PackageReference>
</ItemGroup>
Observação
Verifique se a versão
Se a versão correta do SDK de Aplicações Windows já estiver instalada no computador e não quiser incluir o runtime do SDK no seu pacote, pode especificar a dependência do pacote no ficheiro Package.appxmanifest para o projeto 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>
...
Atualizar o manifesto do pacote
Em Explorador de Soluções clique com o botão direito no ficheiro Package.appxmanifest e selecione View Code para abrir o ficheiro manifest xml. Em seguida, você precisa adicionar algumas declarações de namespace para as extensões de pacote de aplicativo que usaremos. Adicione as seguintes definições de namespace ao elemento Package de nível superior.
<!-- Package.appmanifest -->
<Package
...
xmlns:uap3="http://schemas.microsoft.com/appx/manifest/uap/windows10/3"
xmlns:com="http://schemas.microsoft.com/appx/manifest/com/windows10"
Dentro do elemento Application, crie um novo elemento vazio chamado Extensions. Certifique-se de que isso esteja após a tag de fechamento seguinte para uap:VisualElements.
<!-- Package.appxmanifest -->
<Application>
...
<Extensions>
</Extensions>
</Application>
A primeira extensão que precisamos adicionar é a extensão ComServer. Isso registra o ponto de entrada do executável com o sistema operacional. Esta extensão é a aplicação empacotada equivalente ao registo de um servidor COM, através da definição de uma chave de registo, e não é específica para fornecedores de widgets. Adicione o seguinte elemento com:Extension como filho do elemento Extensions. Altere o GUID no atributo Id do elemento com:Class para o GUID gerado num passo anterior.
<!-- 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>
Em seguida, adicione a extensão que registra o aplicativo como um provedor de widgets. Cole o elemento uap3:Extension no trecho de código a seguir, como filho do elemento Extensions. Certifique-se de substituir o atributo ClassId do elemento COM pelo GUID usado nas etapas anteriores.
<!-- 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>
Para obter descrições detalhadas e informações de formato para todos esses elementos, consulte Widget provider package manifest XML format.
Adicione ícones e outras imagens ao seu projeto de embalagem
Em Explorador de Soluções, clique com o botão direito do rato em ExampleWidgetProviderPackage e selecione Adicionar-Nova Pasta. Nomeie essa pasta como ProviderAssets, pois é o que foi usado no Package.appxmanifest da etapa anterior. É aqui que armazenaremos os nossos ícones e capturas de tela para os nossos widgets. Depois de adicionar os ícones e capturas de tela desejados, certifique-se de que os nomes das imagens correspondam ao que vem depois Path=ProviderAssets\ em seu Package.appxmanifest ou os widgets não aparecerão no host do widget.
Para obter informações sobre os requisitos de design para capturas de tela e as convenções de nomenclatura para capturas de tela localizadas, consulte Integrar com o seletor de widgets.
Testando seu provedor de widgets
Certifique-se de ter selecionado a arquitetura que corresponde à sua máquina de desenvolvimento na lista suspensa das Plataformas de Solução, por exemplo, "x64". Em Explorador de Soluções, clique com o botão direito na sua solução e selecione Build Solution. Depois disso, clique com o botão direito do mouse no ExampleWidgetProviderPackage e selecione Implantar. Na versão atual, o único host de widget suportado é o Widgets Board. Para ver os widgets, você precisará abrir o Painel de widgets e selecionar Adicionar widgets no canto superior direito. Desce até ao final dos widgets disponíveis e deverá ver o mock Weather Widget e Microsoft Counting Widget que foram criados neste tutorial. Clique nos widgets para fixá-los ao seu painel de widgets e testar sua funcionalidade.
Depurando seu provedor de widgets
Depois de fixar seus widgets, a Plataforma de widgets iniciará seu aplicativo de provedor de widgets para receber e enviar informações relevantes sobre o widget. Para depurar o widget em execução, pode ou anexar um depurador à aplicação fornecedora de widgets em execução ou pode configurar o Visual Studio para começar automaticamente a depurar o processo do fornecedor de widgets assim que este for iniciado.
Para anexar ao processo em execução:
- Em Visual Studio clique em Debug -> Anexar ao processo.
- Filtre os processos e encontre o aplicativo de provedor de widgets desejado.
- Anexe o depurador.
Para anexar o depurador automaticamente ao processo quando este for iniciado inicialmente:
- Em Visual Studio clique Depuração -> Outros Alvos de Depuração -> Depurar Pacote de Aplicação Instalado.
- Filtre os pacotes e encontre o pacote desejado do provedor de widgets.
- Selecione-o e marque a caixa que diz Não iniciar, mas depurar meu código quando ele for iniciado.
- Clique Anexar.
Converta a tua aplicação de consola numa aplicação para Windows
Para converter a aplicação de consola criada neste walkthrough numa aplicação Windows, clique com o botão direito no projeto ExampleWidgetProvider em Explorador de Soluções e selecione Properties. Em Aplicação->Geral, altera o tipo de saída de "Aplicação de Consola" para "Aplicação Windows".
Publicando seu widget
Depois de desenvolver e testar o seu widget, pode publicar a sua aplicação na Microsoft Store para que os utilizadores possam instalar os seus widgets nos seus dispositivos. Para orientações passo a passo sobre como publicar uma aplicação, consulte Publique a sua aplicação na Microsoft Store.
A coleção da Widgets Store
Depois de a sua aplicação ser publicada na Microsoft Store, pode solicitar que a sua aplicação seja incluída na Coleção de Widgets Store, que ajuda os utilizadores a descobrir aplicações que incluem Widgets do Windows. Para enviar a sua solicitação, consulte Submeter as informações do widget para adição à Coleção da Loja.
Implementando a personalização de widgets
A partir do SDK de Aplicações Windows 1.4, os widgets podem suportar personalização do utilizador. Quando este recurso é implementado, uma opção Personalizar widget é adicionada ao menu de opções acima da opção Desafixar widget.
As etapas a seguir resumem o processo de personalização do widget.
- Durante a operação normal, o provedor de widgets responde aos pedidos do anfitrião do widget com o modelo e as cargas úteis de dados para a experiência regular do widget.
- O utilizador clica no botão Personalizar widget no menu de elipse.
- O widget levanta o evento OnCustomizationRequested no provedor de widgets para indicar que o utilizador solicitou a experiência de personalização do widget.
- O provedor de widgets define um sinalizador interno para indicar que o widget está no modo de personalização. Enquanto estiver no modo de personalização, o provedor de widgets envia os modelos JSON para a interface do usuário de personalização do widget em vez da interface do usuário do widget normal.
- Enquanto estiver no modo de personalização, o provedor de widgets recebe eventos de OnActionInvoked à medida que o usuário interage com a interface do usuário de personalização e ajusta sua configuração interna e comportamento com base nas ações do usuário.
- Quando a ação associada ao evento OnActionInvoked é a ação de "personalização de saída" definida pelo aplicativo, o provedor de widgets redefine seu sinalizador interno para indicar que não está mais no modo de personalização e retoma o envio dos modelos JSON visuais e de dados para a experiência regular do widget, refletindo as alterações solicitadas durante a personalização. É possível que o usuário feche a experiência de personalização sem clicar na ação de personalização de saída definida pelo aplicativo. Nesse caso, o IWidgetProviderAnalytics.OnAnalyticsInfoReported será gerado e o WidgetAnalyticsInfoReportedArgs.AnalyticsJson terá um interactionKind de "exitCustomization".
- O provedor de widgets persiste as opções de personalização no disco ou na nuvem para que as alterações sejam preservadas entre as invocações do provedor de widgets.
Observação
Existe um bug conhecido no Windows Widget Board, para widgets construídos com o SDK de Aplicações Windows, que faz com que o menu de reticência deixe de responder depois de mostrar o cartão de personalização.
Em cenários típicos de personalização do widget, o usuário escolherá quais dados serão exibidos no widget ou ajustará a apresentação visual do widget. Para simplificar, o exemplo nesta seção adicionará um comportamento de personalização que permite ao usuário redefinir o contador do widget de contagem implementado nas etapas anteriores.
Observação
A personalização de widgets só é suportada no SDK de Aplicações Windows 1.4 e versões posteriores. Certifique-se de atualizar as referências em seu projeto para a versão mais recente do pacote Nuget.
Atualizar o manifesto do pacote para declarar suporte à personalização
Para que o host do widget saiba que o widget suporta personalização, adicione o atributo
...
<Definition Id="Counting_Widget"
DisplayName="Microsoft Counting Widget"
Description="CONFIG counting widget description"
IsCustomizable="true">
...
Rastrear quando um widget está no modo de personalização
O exemplo neste artigo usa o auxiliar struct CompactWidgetInfo para rastrear o estado atual de nossos widgets ativos. Adicione o no campo inCustomization, que será usado para rastrear quando o host do widget espera que enviemos o nosso modelo json de personalização em vez do modelo regular de widget.
// 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;
}
Implementar IWidgetProvider2
A funcionalidade de personalização do widget é exposta através da interface IWidgetProvider2. Atualize a definição de classe do WidgetProvider para implementar esta interface.
// WidgetProvider.cs
internal class WidgetProvider : IWidgetProvider, IWidgetProvider2
Adicione uma implementação para o retorno de chamada
// 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);
}
}
Agora, declare uma variável de cadeia de caracteres que define o modelo JSON para a interface do usuário de personalização do widget. Para este exemplo, temos um botão "Redefinir contador" e um botão "Sair da personalização" que sinalizará ao nosso provedor para retornar ao comportamento normal do widget. Coloque essa definição ao lado das outras definições de modelo.
// 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""
}";
Enviar modelo de personalização em UpdateWidget
Em seguida, atualizaremos nosso UpdateWidget método auxiliar que envia nossos dados e modelos JSON visuais para o host do widget. Quando estamos atualizando o widget de contagem, enviamos o modelo de widget regular ou o modelo de personalização, dependendo do valor do campo inCustomization. Por uma questão de brevidade, o código não relevante para a personalização é omitido neste trecho de código.
// 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);
}
Responder a ações de personalização
Quando os utilizadores interagem com inputs no nosso modelo de personalização, chama o mesmo manipulador OnActionInvoked como quando o utilizador interage com a experiência de widget regular. Para dar suporte à personalização, procuramos os verbos «reset» e «exitCustomization» no nosso modelo JSON de personalização. Se a ação for para o botão "Redefinir contador", redefiniremos o contador mantido no campo customState de nossa estrutura auxiliar para 0. Se a ação for para o botão "Sair da personalização", definimos o campo inCustomization como false para que, quando chamarmos UpdateWidget, nosso método auxiliar enviará os modelos JSON regulares e não o modelo de personalização.
// 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);
}
}
}
Agora, ao implantar o seu widget, verá o botão Personalizar widget no menu de três pontos. Clicar no botão personalizar exibirá seu modelo de personalização.
Clique no botão Redefinir contador para redefinir o contador para 0. Clique no botão Sair da personalização para retornar ao comportamento normal do widget.
Windows developer