在 UWP 设备应用中使用打印通知

Important

设备元数据已弃用,并将在 Windows 的将来版本中删除。 有关替代功能的信息,请参阅驱动程序包容器元数据

UWP 设备应用可以响应从 v4 打印驱动程序发送的双向通信(Bidi)事件。 本文介绍打印通知,并介绍了 打印设置和打印通知 示例的 C# 版本如何使用后台任务来响应打印通知。 后台任务演示如何在本地应用数据存储中保存通知详细信息、发送 toast 通知,以及更新磁贴和徽章。 若要了解有关 UWP 设备应用的一般详细信息,请参阅 UWP 设备应用简介

打印设置和打印通知示例的 C# 版本演示了 BackgroundTask 项目中应用(后台任务)的背景部分。 The code for the background task is in the PrintBackgroundTask.cs file. The foreground app, the full-screen app that can be launched from Start, is in the DeviceAppForPrinters project. The InkLevel.xaml.cs file shows one way that notification details can be accessed from the foreground app. To work with print notifications, the sample uses the printer extension library in the PrinterExtensionLibrary project. 打印机扩展库提供了一种访问 v4 打印驱动程序的打印机扩展接口的便捷方法。 有关详细信息,请参阅 打印机扩展库概述

本文中所示的代码示例基于 打印设置和打印通知 示例的 C# 版本。 此示例在 JavaScript 和 C++中也可用。 由于C++可以直接访问 COM,因此示例C++版本不包括代码库项目。 下载示例以查看最新版本的代码。

打印通知允许 UWP 设备应用在打印时通知用户重要打印机事件,例如纸塞、打开打印机门、低墨量或打印机纸外错误。 当打印机触发通知时,系统事件代理将运行应用的后台任务。 从那里,后台任务可以保存通知详细信息、发送 Toast、更新磁贴、更新锁屏提醒或不执行任何作。 通过保存通知详细信息,你的应用可以提供一种有助于用户了解和修复打印机问题的体验。

打印机制造商必须在 v4 打印驱动程序中实现 Bidi 和 DriverEvent XML 文件,才能将其 UWP 设备应用使用打印通知。 For more information, see Bidirectional Communications.

当 DriverEvent 发生并且 UWP 设备应用的后台任务启动时,该应用有多种选项可以继续作。 有关导致任务启动的流的详细信息,请参阅 驱动程序对自定义 UI 的支持

后台任务可以选择:

磁贴通知或 Toast 通知可让用户方便地启动前台应用。 When the foreground app is launched, it can use the OnLaunched method in App.xaml.cs to check if it was launched by a tile or toast. 如果是,前台应用可以访问 本地应用数据存储中的任何打印通知详细信息。

Prerequisites

准备工作:

  1. 请确保使用 v4 打印驱动程序安装打印机。 有关详细信息,请参阅 开发 v4 打印驱动程序

  2. 设置开发电脑。 See Getting started for information about downloading the tools and creating a developer account.

  3. 将应用与应用商店相关联。 有关详细信息,请参阅 “创建 UWP 设备应用”。

  4. 为将它与应用关联的打印机创建设备元数据。 有关详细信息,请参阅 “创建设备元数据”。

  5. 为应用的主页生成 UI。 可以从“开始”启动启动所有 UWP 设备应用,其中显示全屏。 使用“开始”体验以与设备的特定品牌和功能匹配的方式突出显示产品或服务。 它可以使用的 UI 控件类型没有特殊限制。 若要开始设计全屏体验,请参阅 Microsoft应用商店设计原则

  6. If you're writing your app with C# or JavaScript, add the PrinterExtensionLibrary and DeviceAppForPrintersLibrary projects to your UWP device app solution. 可以在 “打印设置”和“打印通知 ”示例中找到每个项目。

由于C++可以直接访问 COM,C++应用不需要单独的库来处理基于 COM 的打印机设备上下文。

步骤 1:注册后台任务

为了使 Windows 能够识别应用可以处理打印通知,它必须注册打印通知的后台任务扩展。 此扩展在元素中 声明,其属性设置为 /> 和 属性设置为 。 该扩展还包括一个 Task 元素,用于指示它支持 systemEvent 任务类型。

You can add the print background task extension on the Declarations tab of the Manifest Designer in Microsoft Visual Studio. 还可以使用 XML(文本)编辑器手动编辑应用包清单 XML。 Right-click the Package.appxmanifest file in Solution Explorer for editing options.

This example shows the background task extension in the Extension element, as it appears in the app package manifest file, Package.appxmanifest.

<?xml version="1.0" encoding="utf-8"?>
<Package xmlns="http://schemas.microsoft.com/appx/2010/manifest">
  <Identity Name="Microsoft.SDKSamples.DeviceAppForPrinters.CS" Publisher="CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US" Version="1.0.0.0" />
  <Properties>
    <DisplayName>Device App For Printers C# sample</DisplayName>
    <PublisherDisplayName>Microsoft Corporation</PublisherDisplayName>
    <Logo>Assets\storeLogo-sdk.png</Logo>
  </Properties>
  <Prerequisites>
    <OSMinVersion>6.3.0</OSMinVersion>
    <OSMaxVersionTested>6.3.0</OSMaxVersionTested>
  </Prerequisites>
  <Resources>
    <Resource Language="x-generate" />
  </Resources>
  <Applications>
    <Application Id="DeviceAppForPrinters" Executable="$targetnametoken$.exe" EntryPoint="DeviceAppForPrinters.App">
      <VisualElements DisplayName="Device App For Printers C# sample" Logo="Assets\squareTile-sdk.png"
                      SmallLogo="Assets\smallTile-sdk.png" Description="DeviceAppForPrinters C# sample"
                      ForegroundText="light" BackgroundColor="#00b2f0" ToastCapable="true">
        <DefaultTile ShowName="allLogos" ShortName="App4PrinterCS" WideLogo="Assets\tile-sdk.png" />
        <SplashScreen Image="Assets\splash-sdk.png" BackgroundColor="#00b2f0" />
      </VisualElements>
      <Extensions>
        <Extension Category="windows.backgroundTasks" EntryPoint="BackgroundTask.PrintBackgroundTask">
          <BackgroundTasks>
            <Task Type="systemEvent" />
          </BackgroundTasks>
        </Extension>
        <Extension Category="windows.printTaskSettings" Executable="$targetnametoken$.exe" EntryPoint="DeviceAppForPrinters.App" />
      </Extensions>
    </Application>
  </Applications>
</Package>

步骤 2:配置设备元数据

使用设备元数据创作向导将应用与设备关联时,请确保在“指定 UWP 设备应用信息”页上完成“通知处理程序”框。 这有助于确保在打印通知期间调用应用的后台任务。

For step-by-step instructions on how to edit your device metadata, see the Testing section.

步骤 3:生成 UI

在构建应用之前,应与设计人员和营销团队合作,设计用户体验。 用户体验应投影公司的品牌方面,并帮助你与用户建立连接。

Design guidelines

在设计磁贴和锁屏提醒体验之前,请务必查看 Microsoft 应用商店应用指南。 这些指南有助于确保应用提供与其他 UWP 应用一致的直观体验。

对于应用的主页,请记住,Windows 8.1 可以在单个监视器上以各种大小显示多个应用。 请参阅以下指南,详细了解应用如何在屏幕大小、窗口大小和方向之间正常重排。

Best practices

  • 不要在通知中包含作词。 在通知消息中,不要使用通知用户推送、按或选择通知的文本。 用户已经了解他们可以按 Toast 来查找详细信息。 例如,只需编写“打印机墨迹不足”,而不是“打印机墨迹不足”。 按“进行故障排除”。

  • 保持交互简单。 通知体验上显示的所有内容都应与通知相关。 例如,有关纸塞的通知页应仅包含有关解决此问题的链接和信息。 它不应包含指向不相关的体验的链接,例如购买墨迹或其他支持信息。

  • Use multimedia. 使用设备的实际照片、视频或插图来帮助用户快速解决其设备的问题。

  • 将用户保留在应用的上下文中。 提供有关问题的信息时,请勿链接到联机或其他支持材料。 将用户保留在应用的上下文中。

步骤 4:创建后台任务

如果应用为打印通知注册后台任务,则它必须提供后台任务激活的处理程序。 在 “打印设置”和“打印通知 ”示例中, PrintBackgroundTask 类处理打印通知。

如果打印机状态不需要即时用户干预,请更新磁贴而不是显示 Toast。 例如,对于低墨迹条件,磁贴更新已足够。 但是,如果打印机墨迹不足,应用可能会显示 Toast 通知。

保存通知详细信息

后台任务无法直接启动前台应用,只有用户可以:从磁贴、Toast 或“开始”。 因此,为了确保前台应用可以访问打印通知详细信息,后台任务会将它们保存到本地存储。 有关使用本地存储的详细信息,请参阅 快速入门:本地应用数据

触发打印通知时,Windows 通过调用其 Run 方法运行后台任务。 通知数据通过必须强制转换为类型 Windows.Devices.Printers.Extensions.PrintNotificationEventDetails 的方法参数传递到后台任务。 EventDataPrinterName对象的属性分别携带打印机名称和 Bidi 消息。

This example shows the background task's Run method, in the PrintBackgroundTask.cs file, where the print notification details are saved to app settings before the toast, tile, and badge methods are called.

public void Run(Windows.ApplicationModel.Background.IBackgroundTaskInstance taskInstance)
{
    // Save notification details to local storage
    PrintNotificationEventDetails details = (PrintNotificationEventDetails)taskInstance.TriggerDetails;
    settings.Values[keyPrinterName] = details.PrinterName;
    settings.Values[keyAsyncUIXML] = details.EventData;

    // Demonstrate possible actions
    ShowToast(details.PrinterName, details.EventData);
    UpdateTile(details.PrinterName, details.EventData);
    UpdateBadge();
}

更新磁贴

当打印通知详细信息发送到 UpdateTile 方法时,示例的后台任务演示如何在磁贴上显示它们。 有关磁贴的详细信息,请参阅 磁贴和磁贴通知概述

This example shows the background task's UpdateTile method, in the PrintBackgroundTask.cs file.

void UpdateTile(string printerName, string bidiMessage)
{
    TileUpdater tileUpdater = TileUpdateManager.CreateTileUpdaterForApplication();
    tileUpdater.Clear();

    XmlDocument tileXml = TileUpdateManager.GetTemplateContent(TileTemplateType.TileWide310x150Text09);
    XmlNodeList tileTextAttributes = tileXml.GetElementsByTagName("text");
    tileTextAttributes[0].InnerText = printerName;
    tileTextAttributes[1].InnerText = bidiMessage;

    TileNotification tileNotification = new TileNotification(tileXml);
    tileNotification.Tag = "tag01";
    tileUpdater.Update(tileNotification);
}

更新锁屏提醒

该方法 UpdateBadge 演示如何使用 BadgeNotification 类更新锁屏提醒。 For more information about tiles, see Badge overview.

This example shows the background task's UpdateBadge method, in the PrintBackgroundTask.cs file.

void UpdateBadge()
{
    XmlDocument badgeXml = BadgeUpdateManager.GetTemplateContent(BadgeTemplateType.BadgeGlyph);
    XmlElement badgeElement = (XmlElement)badgeXml.SelectSingleNode("/badge");
    badgeElement.SetAttribute("value", "error");

    var badgeNotification = new BadgeNotification(badgeXml);
    BadgeUpdateManager.CreateBadgeUpdaterForApplication().Update(badgeNotification);
}

引发 toast

Toast 通知是向用户发送的暂时性消息,其中包含相关的时间敏感信息,并提供对应用中相关内容的快速访问。 应将 Toast 通知作为邀请查看给用户,以返回到应用以跟进感兴趣的内容。 有关详细信息,请参阅 Toast 通知概述

若要启用 Toast 通知,应用需要在应用包清单中注册支持 Toast 的功能。 在元素中 VisualElements ,将 ToastCapable 属性设置为 true。

Important

我们不建议始终显示 Toast,尤其是对于不可作的事件。 这可能会令用户恼火,并导致他们关闭应用中的所有 Toast。 对于不需要用户立即注意的事件,建议仅更新磁贴和锁屏提醒,而不显示 Toast。

This example shows the ToastCapable attribute in the VisualElements element, as it appears in the app package manifest file, Package.appxmanifest.

<VisualElements DisplayName="Device App For Printers C# sample" Logo="Assets\squareTile-sdk.png"
                SmallLogo="Assets\smallTile-sdk.png" Description="DeviceAppForPrinters C# sample"
                ForegroundText="light" BackgroundColor="#00b2f0" ToastCapable="true">
  <DefaultTile ShowName="allLogos" ShortName="App4PrinterCS" WideLogo="Assets\tile-sdk.png" />
  <SplashScreen Image="Assets\splash-sdk.png" BackgroundColor="#00b2f0" />
</VisualElements>

This example is from the ShowToast method of the PrintBackgroundTask.cs file. 它演示如何基于两个字符串(命名 titlebody)引发 Toast。

void ShowToast(string title, string body)
{
    //
    // Get Toast template
    //
    XmlDocument toastXml = ToastNotificationManager.GetTemplateContent(ToastTemplateType.ToastText02);

    //
    // Pass to app as eventArgs.detail.arguments
    //
    ((XmlElement)toastXml.SelectSingleNode("/toast")).SetAttribute("launch", title);

    //
    // The ToastText02 template has 2 text nodes (a header and a body)
    // Assign title to the first one, and body to the second one
    //
    XmlNodeList textList = toastXml.GetElementsByTagName("text");
    textList[0].AppendChild(toastXml.CreateTextNode(title));
    textList[1].AppendChild(toastXml.CreateTextNode(body));

    //
    // Show the Toast
    //
    ToastNotification toast = new ToastNotification(toastXml);
    ToastNotificationManager.CreateToastNotifier().Show(toast);
}

步骤 5:处理激活

打印通知触发后台任务后,可以通过点击 Toast 通知或磁贴启动应用。 如果应用从任一项激活,则参数将通过 LaunchActivatedEventArgs.arguments 属性传递给应用。 For more information about activation and the Microsoft Store app lifecycle, see Application lifecycle.

若要确定应用是否在这些情况下激活,请处理该 OnLaunched 事件,并检查传递给事件处理程序的事件参数。 如果事件参数为 null,则应用已由用户从 Start 激活。 如果事件参数不为 null,则应用是从 Toast 或磁贴启动的。

This example is from the OnLaunched method of the App.xaml.cs file. 它演示如何处理来自 Toast 或磁贴的激活。

protected override async void OnLaunched(LaunchActivatedEventArgs args)
{
    Frame rootFrame = Window.Current.Content as Frame;

    // Do not repeat app initialization when the Window already has content,
    // just ensure that the window is active

    if (rootFrame == null)
    {
        // Create a Frame to act as the navigation context and navigate to the first page
        rootFrame = new Frame();
        // Associate the frame with a SuspensionManager key
        SuspensionManager.RegisterFrame(rootFrame, "AppFrame");

        if (args.PreviousExecutionState == ApplicationExecutionState.Terminated)
        {
            // Restore the saved session state only when appropriate
            try
            {
                await SuspensionManager.RestoreAsync();
            }
            catch (SuspensionManagerException)
            {
                //Something went wrong restoring state.
                //Assume there is no state and continue
            }
        }

        // Place the frame in the current Window
        Window.Current.Content = rootFrame;
    }
    if (rootFrame.Content == null || !String.IsNullOrEmpty(args.Arguments))
    {
        // When the navigation stack isn't restored or there are launch arguments
        // indicating an alternate launch (e.g.: via toast or secondary tile),
        // navigate to the appropriate page, configuring the new page by passing required
        // information as a navigation parameter
        if (!rootFrame.Navigate(typeof(MainPage), args.Arguments))
        {
            throw new Exception("Failed to create initial page");
        }
    }
    // Ensure the current window is active
    Window.Current.Activate();
}

步骤 6:访问通知详细信息

由于后台任务无法直接启动前台应用,因此需要将打印通知详细信息保存到应用的设置中,以便前台应用可以访问它们。 有关使用本地存储的详细信息,请参阅 快速入门:本地应用数据

此示例演示如何从 打印设置和打印通知 示例中的应用设置检索打印机名称和 Bidi 消息。 The code is from the DisplayBackgroundTaskTriggerDetails method of the InkLevel.xaml.cs file. The key index values, keyPrinterName, and keyAsyncUIXML, are the same string constants that are used in the background task, PrintBackgroundTask.cs.

void DisplayBackgroundTaskTriggerDetails()
{
    String outputText = "\r\n";

    try
    {
        string printerName = settings.Values[keyPrinterName].ToString();
        outputText += ("Printer name from background task triggerDetails: " + printerName);
    }
    catch (Exception)
    {
        outputText += ("No printer name retrieved from background task triggerDetails ");
    }

    outputText += "\r\n";
    try
    {
        string asyncUIXML = settings.Values[keyAsyncUIXML].ToString();
        outputText += ("AsyncUI xml from background task triggerDetails: " + asyncUIXML);
    }
    catch (Exception)
    {
        outputText += ("No asyncUI xml retrieved from background task triggerDetails ");
    }

    ToastOutput.Text += outputText;
}

Testing

在测试 UWP 设备应用之前,必须使用设备元数据将其链接到打印机。

需要打印机的设备元数据包的副本,才能将设备应用信息添加到其中。 如果没有设备元数据,可以使用 设备元数据创作向导 生成它,如 UWP 设备应用创建设备元数据一文中所述。

若要使用 设备元数据创作向导,必须先安装 Microsoft Visual Studio Professional、Microsoft Visual Studio Ultimate 或 适用于 Windows 8.1 的独立 SDK,然后才能完成本文中的步骤。 安装 Microsoft Visual Studio Express for Windows 会安装不包含向导的 SDK 版本。

以下步骤生成应用并安装设备元数据。

  1. 启用测试签名。

    1. 通过双击DeviceMetadataWizard.exe,从 %ProgramFiles(x86)%\Windows Kits\8.1\bin\x86 启动设备元数据创作向导

    2. From the Tools menu, select Enable Test Signing.

  2. 重新启动计算机

  3. 通过打开解决方案(.sln)文件生成解决方案。 按 F7 或在加载示例后从顶部菜单中转到 生成>解决方案

  4. 断开连接并卸载打印机。 此步骤是必需的,以便 Windows 将在下次检测到设备时读取更新的设备元数据。

  5. 编辑和保存设备元数据。 若要将设备应用链接到设备,必须将设备应用与设备相关联。

    如果尚未创建设备元数据,请参阅 为 UWP 设备应用创建设备元数据

    1. 如果设备元数据创作向导尚未打开,请双击 DeviceMetadataWizard.exe从%ProgramFiles(x86)%\Windows Kits\8.1\bin\x86 启动它。

    2. 选择 “编辑设备元数据”。 使用此选项可以编辑现有的设备元数据包。

    3. In the Open dialog box, locate the device metadata package associated with your UWP device app. (It has a devicemetadata-ms file extension.)

    4. “指定 UWP 设备应用信息 ”页上,在 “UWP 设备应用 ”框中输入Microsoft应用商店应用信息。 选择 “导入 UWP 应用清单文件 ”以自动输入 包名称发布服务器名称和UWP 应用 ID

    5. If your app is registering for printer notifications, fill out the Notification handlers box. In Event ID, enter the name of the print event handler. In Event Asset, enter the name of the file where that code resides.

    6. When you're done, select Next until you get to the Finish page.

    7. 在“ 查看设备元数据包 ”页上,确保所有设置都正确,并选择“ 将设备元数据包复制到本地计算机上的元数据存储 ”复选框。 Then select Save.

  6. 重新连接打印机,以便 Windows 在设备连接时读取更新的设备元数据。

Troubleshooting

问题:未显示默认 Toast 通知

如果未在预期时显示默认打印通知...

  • Possible cause: Test signing isn't turned on. 有关打开调试的信息,请参阅本文中的“调试”部分。

  • Possible cause: Toast notifications are disabled by domain policies. 退出域,然后重试。

  • Possible cause: The printer hasn't implemented DriverEvents. 检查 v4 驱动程序是否支持 Bidi 和 DriverEvents。 有关详细信息,请参阅 驱动程序对自定义 UI 的支持

  • Possible cause: The machine has no recent job in the printer queue. 确保打印机图标显示在屏幕右下角。 如果没有,请发送另一个打印作业。

  • Possible cause: Your entry point for the background task (IBackgroundTask) is within the same project as your foreground app. 不允许使用此方案。 为后台任务处理程序分离出全新的类。

  • Possible cause: The class that is the entry point for notifications in your app is incorrectly given in your manifest or device metadata, causing the app to crash within the background host and not showing any toast. 检查是否存在以下问题:

    • Make sure the entry point is given correctly in the Declarations tab of the Manifest Designer. 它应采用适用于 C# 和 C++ 的 Namespace.ClassName 格式。 对于 JavaScript,它应该是 .js 文件的相对目录路径。

    • JavaScript 应用在完成后应调用 close()。

    • C# 类必须实现 Windows.ApplicationModel.Background.IBackgroundTask,并且必须具有公共 void Run(Windows.ApplicationModel.Background.IBackgroundTaskInstance taskInstance) 方法。

    • C++类必须实现 Windows::ApplicationModel::Background::IBackgroundTask,并且必须具有方法 virtual void Run(Windows::ApplicationModel::Background::IBackgroundTaskInstance^ taskInstance)