在 C# Windows 应用中实现小组件提供程序 您所在的位置:网站首页 系统小组件应用怎么设置 在 C# Windows 应用中实现小组件提供程序

在 C# Windows 应用中实现小组件提供程序

#在 C# Windows 应用中实现小组件提供程序| 来源: 网络整理| 查看: 265

在 C# Windows 应用中实现小组件提供程序 项目 07/26/2023

本文指导你创建实现 IWidgetProvider 接口的简单小组件提供程序。 此接口的方法由小组件主机调用,以请求定义小组件的数据,或让小组件提供程序响应小组件上的用户操作。 小组件提供程序可以支持单个小组件或多个小组件。 在此示例中,我们将定义两个不同的小组件。 一个小组件是模拟天气小组件,用于说明自适应卡片框架提供的一些格式设置选项。 第二个小组件将通过维护一个计数器来演示用户操作和自定义小组件状态功能,只要用户单击小组件上显示的按钮,就会递增。

本文中的此示例代码改编自 Windows 应用 SDK 小组件示例。 若要使用 C++/WinRT 实现小组件提供程序,请参阅在 win32 应用中实现小组件提供程序(C++/WinRT)。

先决条件 设备必须启用开发人员模式。 有关详细信息,请参阅 “启用设备进行开发”。 具有通用 Windows 平台开发工作负载的 Visual Studio 2022 或更高版本。 请确保从可选下拉列表中添加 C++ 的组件(v143)。 创建新的 C# 控制台应用

在 Visual Studio 中,创建新的项目。 在 “创建新项目 ”对话框中,将语言筛选器设置为“C#”,并将平台筛选器设置为 Windows,然后选择控制台应用项目模板。 将新项目命名为“ExampleWidgetProvider”。 出现提示时,将目标 .NET 版本设置为 6.0。

加载项目时,在解决方案资源管理器中右键单击项目名称并选择“属性”。 在 “常规 ”页上,向下滚动到 目标 OS 并选择“Windows”。 在“目标 OS 版本”下,选择版本 10.0.19041.0 或更高版本。

请注意,本演练使用在激活小组件以启用轻松调试时显示控制台窗口的控制台应用。 准备好发布小组件提供程序应用时,可以按照将控制台应用转换为 Windows 应用的步骤 将控制台应用程序转换为 Windows 应用程序。

添加对 Windows 应用 SDK 和 Windows 实现库 NuGet 包的引用

此示例使用最新的稳定 Windows 应用 SDK NuGet 包。 在 解决方案资源管理器中,右键单击 “依赖项 ”并选择“ 管理 NuGet 包...”。在 NuGet 包管理器中,选择 “浏览 ”选项卡并搜索“Microsoft.WindowsAppSDK”。 在 “版本 ”下拉列表中选择最新的稳定版本,然后单击“ 安装”。

添加 WidgetProvider 类来处理小组件操作

在 Visual Studio 中,右键单击ExampleWidgetProvider解决方案资源管理器中的项目,然后选择“添加>类”。 在 “添加类 ”对话框中,将类命名为“WidgetProvider”,然后单击“ 添加”。 在生成的 WidgetProvider.cs 文件中,更新类定义以指示它实现 IWidgetProvider 接口。

// WidgetProvider.cs internal class WidgetProvider : IWidgetProvider 准备跟踪已启用的小组件

小组件提供程序可以支持单个小组件或多个小组件。 每当小组件主机使用小组件提供程序启动操作时,都会传递 ID 来标识与操作关联的小组件。 每个小组件还有一个关联的名称和一个状态值,可用于存储自定义数据。 在此示例中,我们将声明一个简单的帮助程序结构,用于存储每个固定小组件的 ID、名称和数据。 小组件也可以处于活动状态,下面的“激活和停用”部分对此进行了讨论,我们将使用布尔值跟踪每个小组件的状态。 将以下定义添加到 WidgetProvider.cs 文件中,位于 ExampleWidgetProvider 命名空间中,但在 WidgetProvider 类定义之外。

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

在 WidgetProvider.cs 中的 WidgetProvider 类定义中,添加一个将维护已启用小组件列表的映射成员,并使用小组件 ID 作为每个条目的键。

// WidgetProvider.cs // Class member of WidgetProvider public static Dictionary RunningWidgets = new Dictionary(); 声明小组件模板 JSON 字符串

此示例将声明一些静态字符串来定义每个小组件的 JSON 模板。 为方便起见,这些模板存储在 WidgetProvider 类的成员变量中。 如果需要模板的常规存储 - 这些模板可以包含在应用程序包中: 访问包文件。 有关创建小组件模板 JSON 文档的信息,请参阅 使用自适应卡片设计器创建小组件模板。

// WidgetProvider.cs // Class members of WidgetProvider const string weatherWidgetTemplate = @" { ""$schema"": ""http://adaptivecards.io/schemas/adaptive-card.json"", ""type"": ""AdaptiveCard"", ""version"": ""1.0"", ""speak"": ""The forecast for Seattle January 20 is mostly clear with a High of 51 degrees and Low of 40 degrees"", ""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"" }"; 实现 IWidgetProvider 方法

在接下来的几个部分中,我们将实现 IWidgetProvider 接口的方法。 本文后面的几个方法实现中调用的 helper 方法 UpdateWidget 。

注意

传递到 IWidgetProvider 接口的回调方法的对象只能在回调中有效。 不应存储对这些对象的引用,因为它们在回调上下文之外的行为未定义。

CreateWidget

当用户在小组件主机中固定了某个应用的小组件时,小组件主机将调用 CreateWidget 。 首先,此方法获取关联的小组件的 ID 和名称,并将帮助程序结构 CompactWidgetInfo 的新实例添加到已启用的小组件的集合中。 接下来,我们将发送小组件的初始模板和数据,该模板和数据封装在 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); } DeleteWidget

当用户从小组件主机取消固定应用的小组件之一时,小组件主机将调用 DeleteWidget 。 发生这种情况时,我们会从启用的小组件列表中删除关联的小组件,这样我们就不会再为该小组件发送任何更新。

// WidgetProvider.cs public void DeleteWidget(string widgetId, string customState) { RunningWidgets.Remove(widgetId); if(RunningWidgets.Count == 0) { emptyWidgetListEvent.Set(); } }

对于此示例,除了从启用的小组件列表中删除具有指定小组件的小组件外,我们还检查列表现在是否为空,如果是这样,我们还设置了一个事件,该事件稍后将用于允许应用在未启用小组件时退出。 在类定义中,添加 ManualResetEvent 和公共访问器函数的声明。

// WidgetProvider.cs static ManualResetEvent emptyWidgetListEvent = new ManualResetEvent(false); public static ManualResetEvent GetEmptyWidgetListEvent() { return emptyWidgetListEvent; } OnActionInvoked

当用户与小组件模板中定义的操作交互时,小组件主机调用 OnActionInvoked 。 对于此示例中使用的计数器小组件,操作在小组件的 JSON 模板中用 谓词 值“inc”声明。 小组件提供程序代码将使用此 谓词 值来确定响应用户交互时要执行的操作。

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

在 OnActionInvoked 方法中,通过检查传递给该方法的 WidgetActionInvokedArgs 的 Verb 属性来获取谓词值。 如果谓词为“inc”,则我们知道我们将递增小组件的自定义状态中的计数。 从 WidgetActionInvokedArgs 中,获取 WidgetContext 对象,然后获取 WidgetId 以获取要更新的小组件的 ID。 在启用的小组件映射中找到具有指定 ID 的条目,然后更新用于存储增量数的自定义状态值。 最后,使用 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); } } }

有关自适应卡片的 Action.Execute 语法的信息,请参阅 Action.Execute。 有关为小组件设计交互的指导,请参阅 小组件交互设计指南

OnWidgetContextChanged

在当前版本中, 仅当用户更改固定小组件的大小时,才会调用 OnWidgetContextChanged 。 可以选择将不同的 JSON 模板/数据返回到小组件主机,具体取决于所请求的大小。 还可以根据 host.widgetSize 的值设计模板 JSON 以支持所有可用大小。 如果不需要发送新的模板或数据来考虑大小更改,可以使用 OnWidgetContextChanged 进行遥测。

// WidgetProvider.cs public void OnWidgetContextChanged(WidgetContextChangedArgs contextChangedArgs) { var widgetContext = contextChangedArgs.WidgetContext; var widgetId = widgetContext.Id; var widgetSize = widgetContext.Size; if (RunningWidgets.ContainsKey(widgetId)) { var localWidgetInfo = RunningWidgets[widgetId]; UpdateWidget(localWidgetInfo); } } 激活和停用

调用 Activate 方法以通知小组件提供程序,小组件主机当前有兴趣从提供程序接收更新的内容。 例如,这可能意味着用户当前正在主动查看小组件主机。 调用 Deactivate 方法以通知小组件提供程序小组件主机不再请求内容更新。 这两种方法定义一个窗口,其中小组件主机最感兴趣的窗口显示最新的内容。 小组件提供程序可以随时向小组件发送更新,例如响应推送通知,但与任何后台任务一样,必须平衡提供最新内容与资源问题(如电池使用时间)一样。

按小组件调用激活 和 停用 。 此示例跟踪 CompactWidgetInfo 帮助程序结构中每个小组件的活动状态。 在 Activate 方法中,我们调用 UpdateWidget 帮助程序方法来更新小组件。 请注意,“激活”和“停用”之间的时间范围可能很小,因此建议尽量使小组件更新代码路径。

// 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; } } 更新小组件

定义 UpdateWidget 帮助程序方法以更新已启用的小组件。 在此示例中,我们检查传递给该方法的 CompactWidgetInfo 帮助程序结构中的小组件的名称,然后根据要更新的小组件设置相应的模板和数据 JSON。 WidgetUpdateRequestOptions 使用要更新的小组件的模板、数据和自定义状态进行初始化。 调用 WidgetManager::GetDefault 以获取 WidgetManager 类的实例,然后调用 UpdateWidget 将更新的小组件数据发送到小组件主机。

// 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); } 初始化启动时启用的小组件列表

首次初始化小组件提供程序时,最好询问 WidgetManager 是否存在提供程序当前提供的任何正在运行的小组件。 当计算机重启或提供程序崩溃时,它将帮助将应用恢复到以前的状态。 调用 WidgetManager.GetDefault 以获取应用的默认小组件管理器实例。 然后调用 GetWidgetInfos,它返回 WidgetInfo 对象的数组。 将小组件 ID、名称和自定义状态复制到帮助程序结构 CompactWidgetInfo ,并将其保存到 RunningWidgets 成员变量。 将以下代码粘贴到 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 = widgetName, widgetName = widgetId }; 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; } } } 实现将按需实例化 WidgetProvider 的类工厂

为了使小组件主机与我们的小组件提供程序通信,我们必须调用 CoRegisterClassObject。 此函数要求我们创建 IClassFactory 的实现,该实现将为 WidgetProvider 类创建类对象。 我们将在自包含帮助程序类中实现类工厂。

在 Visual Studio 中,右键单击ExampleWidgetProvider解决方案资源管理器中的项目,然后选择“添加>类”。 在 “添加类 ”对话框中,将类命名为“FactoryHelper”,然后单击“ 添加”。

将 FactoryHelper.cs 文件的内容替换为以下代码。 此代码定义 IClassFactory 接口并实现其两种方法: CreateInstance 和 LockServer。 此代码是实现类工厂的典型样板,并不特定于小组件提供程序的功能,只是我们指示所创建的类对象实现 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 : 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.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; } } 为小组件提供程序创建表示 CLSID 的 GUID

接下来,需要创建一个 GUID,该 GUID 表示将用于标识用于 COM 激活的小组件提供程序的 CLSID 。 打包应用时也会使用相同的值。 转到 “工具>创建 GUID”,在 Visual Studio 中生成 GUID。 选择注册表格式选项,然后单击“ 复制 ”,然后将其粘贴到文本文件中,以便稍后可以复制它。

使用 OLE 注册小组件提供程序类对象

在可执行文件的 Program.cs 文件中,我们将调用 CoRegisterClassObject 将小组件提供程序注册到 OLE,以便小组件主机可以与之交互。 将 Program.cs 的内容替换为以下代码。 此代码导入 CoRegisterClassObject 函数并调用它,并传入 我们在上一步中定义的 WidgetProviderFactory 接口。 请务必更新 CLSID_Factory 变量声明,以使用在上一步中生成的 GUID。

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

请注意,此代码示例导入 GetConsoleWindow 函数以确定应用是否作为控制台应用程序运行,这是本演练的默认行为。 如果函数返回有效的指针,我们会将调试信息写入控制台。 否则,应用将作为 Windows 应用运行。 在这种情况下,我们等待在启用的小组件列表为空且退出应用时在 DeleteWidget 方法中设置的事件。 有关将示例控制台应用转换为 Windows 应用的信息,请参阅 将控制台应用转换为 Windows 应用。

打包小组件提供程序应用

在当前版本中,只能将打包的应用注册为小组件提供程序。 以下步骤将引导你完成打包应用和更新应用清单以将应用注册到 OS 作为小组件提供程序的过程。

创建 MSIX 打包项目

在 解决方案资源管理器中,右键单击解决方案并选择“ 添加新>项目...”。在 “添加新项目 ”对话框中,选择“Windows 应用程序打包项目”模板,然后单击“ 下一步”。 将项目名称设置为“ExampleWidgetProviderPackage”,然后单击“ 创建”。 出现提示时,将目标版本设置为版本 1809 或更高版本,然后单击“ 确定”。 接下来,右键单击 ExampleWidgetProviderPackage 项目,然后选择“ 添加>项目”引用。 选择 ExampleWidgetProvider 项目,然后单击“确定”。

向打包项目添加 Windows 应用 SDK 包引用

需要向 MSIX 打包项目添加对 Windows 应用 SDK nuget 包的引用。 在 解决方案资源管理器中,双击 ExampleWidgetProviderPackage 项目以打开 ExampleWidgetProviderPackage.wapproj 文件。 在 Project 元素中添加以下 xml。

build

注意

确保 PackageReference 元素中指定的版本与上一步中引用的最新稳定版本匹配。

如果计算机上安装了正确的 Windows 应用 SDK 版本,并且不想在包中捆绑 SDK 运行时,则可以在 ExampleWidgetProviderPackage 项目的 Package.appxmanifest 文件中指定包依赖项。

... ... ... ... 更新包清单

在解决方案资源管理器中,右键单击Package.appxmanifest该文件,然后选择“查看代码”以打开清单 xml 文件。 接下来,需要为我们将使用的应用包扩展添加一些命名空间声明。 将以下命名空间定义添加到顶级 包 元素。

接下来,添加将应用注册为小组件提供程序的扩展。 将 uap3:Extension 元素粘贴到以下代码片段中,作为 Extensions 元素的子级。 请务必将 COM 元素的 ClassId 属性替换为在前面的步骤中使用的 GUID。

... Microsoft.MicrosoftEdge.Stable_8wekyb3d8bbwe

有关所有这些元素的详细说明和格式信息,请参阅 小组件提供程序包清单 XML 格式。

将图标和其他图像添加到打包项目

在 解决方案资源管理器中,右键单击 ExampleWidgetProviderPackage 并选择 “>添加新文件夹”。 将此文件夹命名为 ProviderAssets,因为这是上一步中使用的 Package.appxmanifest 内容。 我们将在此处存储 小组件的图标 和 屏幕截图 。 添加所需的图标和屏幕截图后,请确保图像名称与 Path=ProviderAssets\ 之后Package.appxmanifest的内容相匹配,否则小组件主机中将不会显示。

有关屏幕截图图像的设计要求以及本地化屏幕截图的命名约定的信息,请参阅 “与小组件选取器集成”。

测试小组件提供程序

请确保已从解决方案平台下拉列表中选择与开发计算机匹配的体系结构,例如“x64”。 在 解决方案资源管理器中,右键单击解决方案并选择“ 生成解决方案”。 完成此操作后,右键单击 ExampleWidgetProviderPackage 并选择“ 部署”。 在当前版本中,唯一受支持的小组件主机是小组件板。 若要查看小组件,需要打开小组件板并选择右上角的“ 添加小组件 ”。 滚动到可用小组件的底部,应会看到本教程中创建的模拟天气小组件和 Microsoft 计数小组件。 单击小组件将其固定到小组件板并测试其功能。

调试小组件提供程序

固定小组件后,小组件平台将启动小组件提供程序应用程序,以便接收和发送有关小组件的相关信息。 若要调试正在运行的小组件,可以将调试器附加到正在运行的小组件提供程序应用程序,也可以将 Visual Studio 设置为在启动小组件提供程序进程后自动开始调试小组件提供程序进程。

若要附加到正在运行的进程:

在 Visual Studio 中,单击“ 调试 -> 附加到进程”。 筛选进程并查找所需的小组件提供程序应用程序。 附加调试程序。

若要在最初启动调试器时自动将调试器附加到进程:

在 Visual Studio 中,单击“ 调试 -> 其他调试目标 -> 调试已安装的应用包”。 筛选包并查找所需的小组件提供程序包。 选中它并选中显示“不启动”的框,但在启动时调试我的代码。 单击 “附加”。 将控制台应用转换为 Windows 应用

若要将在本演练中创建的控制台应用转换为 Windows 应用,请右键单击解决方案资源管理器中的 ExampleWidgetProvider 项目,然后选择“属性”。 在“应用程序常规>”下,将 输出类型 从“控制台应用程序”更改为“Windows 应用程序”。

发布小组件

开发并测试小组件后,必须在 Microsoft Store 上发布应用,以便用户在其设备上安装小组件。 有关发布应用的分步指南,请参阅 在 Microsoft Store 中发布应用。

小组件存储集合

在 Microsoft Store 上发布应用后,你可以请求将你的应用包含在小组件应用商店集合中,以帮助用户发现具有 Windows 小组件的应用。 若要提交请求,请参阅 “提交小组件”信息,了解应用商店集合的附加内容。



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有