自定义打印工作流Customize the print workflow

概述Overview

开发人员可以使用打印工作流应用自定义打印工作流。Developers can customize the printing workflow experience through the use of a print workflow app. 打印工作流应用是对 Microsoft Store 设备应用 (WSDA) 功能进行扩展的 UWP 应用,因此在继续之前,熟悉一下 WSDA 将很有帮助。Print workflow apps are UWP apps that expand on the functionality of Microsoft Store devices apps (WSDAs), so it will be helpful to have some familiarity with WSDAs before going further.

在使用 WSDA 的情况下,当源应用程序的用户选择打印某些内容,并通过打印对话框进行浏览时,系统会检查工作流应用是否与该打印机关联。Just as in the case of WSDAs, when the user of a source application elects to print something and navigates through the print dialog, the system checks whether a workflow app is associated with that printer. 如果关联,则打印工作流应用将启动(主要以后台任务形式运行;下方提供了相关详细信息)。If it is, the print workflow app launches (primarily as a background task; more on this below). 工作流应用能够改变打印票证(配置当前打印任务的打印机设备设置的 XML 文档)和要打印的实际 XPS 内容。A workflow app is able to alter both the print ticket (the XML document that configures the printer device settings for the current print task) and the actual XPS content to be printed. 它可以选择通过在过程进行到一半时启动 UI 向用户显示此功能。It can optionally expose this functionality to the user by launching a UI midway through the process. 完成后其工作之后,它将打印内容和打印票证传递给驱动程序。After doing its work, it passes the print content and print ticket on to the driver.

因为它涉及了后台和前台组件,并且它在功能上要与其他应用配合使用,打印工作流应用比起其他类型的 UWP 应用实现起来可能更复杂。Because it involves background and foreground components, and because it is functionally coupled with other app(s), a print workflow app can be more complicated to implement than other categories of UWP apps. 建议在阅读本指南时查看工作流应用示例,更好地了解如何实现不同功能。It is recommended that you inspect the Workflow app sample while reading this guide to better understand how the different features can be implemented. 为简单起见,本指南中不介绍某些功能,如各种错误检查和 UI 管理。Some features, such as various error checks and UI management, are absent from this guide for the sake of simplicity.

入门Getting started

工作流应用必须指明其到打印系统的入口点,以便在适当的时间启动它。The workflow app must indicate its entry point to the print system so that it can be launched at the appropriate time. 这是通过在 UWP 项目的 package.appxmanifest 文件的Application/Extensions 元素中插入以下声明来实现的。This is done by inserting the following declaration in the Application/Extensions element of the UWP project's package.appxmanifest file.

<uap:Extension Category="windows.printWorkflowBackgroundTask"  
    EntryPoint="WFBackgroundTasks.WfBackgroundTask" />

重要

在很多情况下,打印自定义不需要用户输入。There are many scenarios in which the print customization does not require user input. 因此,打印工作流应用默认以后台任务形式运行。For this reason, Print workflow apps run as background tasks by default.

如果工作流应用与启动打印作业的源应用程序关联(请参阅后面有关内容了解具体说明),打印系统会在其清单文件中查找后台任务入口点。If a workflow app is associated with the source application that started the print job (see later section for instructions on this), the print system examines its manifest files for a background task entry point.

执行打印票证上的后台工作Do background work on the print ticket

打印系统对工作流应用首先要做的是激活其后台任务(在本例中为 WFBackgroundTasks 命名空间中的 WfBackgroundTask 类)。The first thing the print system does with the workflow app is activate its background task (In this case, the WfBackgroundTask class in the WFBackgroundTasks namespace). 在后台任务的 Run 方法中,应该将任务触发器的详细信息转换为 PrintWorkflowTriggerDetails 实例。In the background task's Run method, you should cast the task's trigger details as a PrintWorkflowTriggerDetails instance. 这将为打印工作流后台任务提供特殊的功能。This will provide the special functionality for a print workflow background task. 它显示 PrintWorkflowSession 属性,这个属性是 PrintWorkFlowBackgroundSession 的一个实例。It exposes the PrintWorkflowSession property, which is an instance of PrintWorkFlowBackgroundSession. 打印工作流会话类(后台和前台变体)将控制打印工作流应用中的后续步骤。Print workflow session classes - both the background and foreground varieties - will control the sequential steps of the print workflow app.

然后为此会话类将触发的两个事件注册处理程序方法。Then register handler methods for the two events that this session class will raise. 这些方法将在以后定义。You will define these methods later on.

public void Run(IBackgroundTaskInstance taskInstance) {
    // Take out a deferral here and complete once all the callbacks are done
    runDeferral = taskInstance.GetDeferral();

    // Associate a cancellation handler with the background task.
    taskInstance.Canceled += new BackgroundTaskCanceledEventHandler(OnCanceled);

    // cast the task's trigger details as PrintWorkflowTriggerDetails
    PrintWorkflowTriggerDetails workflowTriggerDetails = taskInstance.TriggerDetails as PrintWorkflowTriggerDetails;

    // Get the session manager, which is unique to this print job
    PrintWorkflowBackgroundSession sessionManager = workflowTriggerDetails.PrintWorkflowSession;

    // add the event handler callback routines
    sessionManager.SetupRequested += OnSetupRequested;
    sessionManager.Submitted += OnXpsOMPrintSubmitted;

    // Allow the event source to start
    // This call blocks until all of the workflow callbacks complete
    sessionManager.Start();
}

调用 Start 方法时,会话管理器首先引发 SetupRequested 事件。When the Start method is called, the session manager will raise the SetupRequested event first. 此事件公开有关打印任务的一般信息,以及打印票证。This event exposes general information about the print task, as well as the print ticket. 在这个阶段,打印票证可以在后台进行编辑。At this stage, the print ticket can be edited in the background.

private void OnSetupRequested(PrintWorkflowBackgroundSession sessionManager, PrintWorkflowBackgroundSetupRequestedEventArgs printTaskSetupArgs) {
    // Take out a deferral here and complete once all the callbacks are done
    Deferral setupRequestedDeferral = printTaskSetupArgs.GetDeferral();

    // Get general information about the source application, print job title, and session ID
    string sourceApplicationName = printTaskSetupArgs.Configuration.SourceAppDisplayName;
    string jobTitle = printTaskSetupArgs.Configuration.JobTitle;
    string sessionId = printTaskSetupArgs.Configuration.SessionId;

    // edit the print ticket
    WorkflowPrintTicket printTicket = printTaskSetupArgs.GetUserPrintTicketAsync();

    // ...

重要的是,它是在处理 SetupRequested,该应用将为其确定是否启动前台组件。Importantly, it is in the handling of the SetupRequested that the app will determine whether to launch a foreground component. 这可能取决于之前保存到本地存储的设置或者在编辑打印票证期间发生的事件,或者它也可能是特定应用的静态设置。This could depend on a setting that was previously saved to local storage, or an event that occurred during the editing of the print ticket, or it may be a static setting of your particular app.

// ...

if (UIrequested) {
    printTaskSetupArgs.SetRequiresUI();

    // Any data that is to be passed to the foreground task must be stored the app's local storage.
    // It should be prefixed with the sourceApplicationName string and the SessionId string, so that
    // it can be identified as pertaining to this workflow app session.
}

// Complete the deferral taken out at the start of OnSetupRequested
setupRequestedDeferral.Complete();

执行打印作业的前台工作(可选)Do foreground work on the print job (optional)

如果调用了 SetRequiresUI 方法,则打印系统将在清单文件中查找前台应用程序的入口点。If the SetRequiresUI method was called, then the print system will examine the manifest file for the entry point to the foreground application. package.appxmanifest 文件的 Application/Extensions 元素必须包含以下几行。The Application/Extensions element of your package.appxmanifest file must have the following lines. EntryPoint 的值替换为前台应用的名称。Replace the value of EntryPoint with name of the foreground app.

<uap:Extension Category="windows.printWorkflowForegroundTask"  
    EntryPoint="MyWorkFlowForegroundApp.App" />

接下来,打印系统将对给定应用入口点调用 OnActivated 方法。Next, the print system calls the OnActivated method for the given app entry point. 在其 App.xaml.cs 文件的 OnActivated 方法中,工作流应用应该检查此激活操作的类型,验证这是否为工作流激活。In the OnActivated method of its App.xaml.cs file, the workflow app should check the activation kind to verify that it is a workflow activation. 如果是,则工作流应用可以将激活参数作为属性转换为 PrintWorkflowUIActivatedEventArgs 对象,它将 PrintWorkflowForegroundSession 对象作为属性公开。If so, the workflow app can cast the activation arguments to a PrintWorkflowUIActivatedEventArgs object, which exposes a PrintWorkflowForegroundSession object as a property. 此对象与在之前的部分中的后台对象一样,包含由打印系统触发的事件,你可以对这些事件分配处理程序。This object, like its background counterpart in the previous section, contains events that are raised by the print system, and you can assign handlers to these. 在本例中,事件处理功能在另一个名为 WorkflowPage 的类中实现。In this case, the event-handling functionality will be implemented in a separate class called WorkflowPage.

首先,在 App.xaml.cs 文件中:First, in the App.xaml.cs file:

protected override void OnActivated(IActivatedEventArgs args){

    if (args.Kind == ActivationKind.PrintWorkflowForegroundTask) {

        // the app should instantiate a new UI view so that it can properly handle the case when
        // several print jobs are active at the same time.
        Frame rootFrame = new Frame();
        if (null == Window.Current.Content)
        {
            rootFrame.Navigate(typeof(WorkflowPage));
            Window.Current.Content = rootFrame;
        }

        // Get the main page
        WorkflowPage workflowPage = (WorkflowPage)rootFrame.Content;

        // Make sure the page knows it's handling a foreground task activation
        workflowPage.LaunchType = WorkflowPage.WorkflowPageLaunchType.ForegroundTask;

        // Get the activation arguments
        PrintWorkflowUIActivatedEventArgs printTaskUIEventArgs = args as PrintWorkflowUIActivatedEventArgs;

        // Get the session manager
        PrintWorkflowForegroundSession taskSessionManager = printTaskUIEventArgs.PrintWorkflowSession;

        // Add the callback handlers - these methods are in the workflowPage class
        taskSessionManager.SetupRequested += workflowPage.OnSetupRequested;
        taskSessionManager.XpsDataAvailable += workflowPage.OnXpsDataAvailable;

        // start raising the print workflow events
        taskSessionManager.Start();
    }
}

在 UI 已连接事件处理程序,并且 OnActivated 方法已退出后,打印系统将引发 SetupRequested 事件以便 UI 处理。Once the UI has attached event handlers and the OnActivated method has exited, the print system will fire the SetupRequested event for the UI to handle. 此事件提供的数据与后台任务设置事件提供的数据相同,包括打印作业信息和打印票证文档,但不能请求启动其他 UI。This event provides the same data that the background task setup event provided, including the print job info and print ticket document, but without the ability to request the launch of additional UI. WorkflowPage.xaml.cs 文件中:In the WorkflowPage.xaml.cs file:

internal void OnSetupRequested(PrintWorkflowForegroundSession sessionManager, PrintWorkflowForegroundSetupRequestedEventArgs printTaskSetupArgs) {
    // If anything asynchronous is going to be done, you need to take out a deferral here,
    // since otherwise the next callback happens once this one exits, which may be premature
    Deferral setupRequestedDeferral = printTaskSetupArgs.GetDeferral();

    // Get information about the source application, print job title, and session ID
    string sourceApplicationName = printTaskSetupArgs.Configuration.SourceAppDisplayName;
    string jobTitle = printTaskSetupArgs.Configuration.JobTitle;
    string sessionId = printTaskSetupArgs.Configuration.SessionId;
    // the following string should be used when storing data that pertains to this workflow session
    // (such as user input data that is meant to change the print content later on)
    string localStorageVariablePrefix = string.Format("{0}::{1}::", sourceApplicationName, sessionID);

    try
    {
        // receive and store user input
        // ...
    }
    catch (Exception ex)
    {
        string errorMessage = ex.Message;
        Debug.WriteLine(errorMessage);
    }
    finally
    {
        // Complete the deferral taken out at the start of OnSetupRequested
        setupRequestedDeferral.Complete();
    }
}

接下来,打印系统将为 UI 引发 XpsDataAvailable 事件。Next, the print system will raise the XpsDataAvailable event for the UI. 在此事件的处理程序中,工作流应用可以访问提供给设置事件的所有数据,并且还可以采取原始字节流或对象模型的形式直接读取 XPS 数据。In the handler for this event, the workflow app can access all of the data available to the setup event and can additionally read the XPS data directly, either as a stream of raw bytes or as an object model. 对 XPS 数据的访问权限让 UI 能够提供打印预览服务,并为用户提供有关工作流应用可对数据执行的操作的信息。Access to the XPS data allows the UI to provide print preview services and to provide additional information to the user about the operations that the workflow app will execute on the data.

作为此事件处理程序的一部分,如果工作流应用将继续与用户交付,则必须获取延迟对象。As part of this event handler, the workflow app must acquire a deferral object if it will continue to interact with the user. 如果没有延迟,当 XpsDataAvailable 事件处理程序退出或它调用异步方法时,打印系统将认为 UI 任务已完成。Without a deferral, the print system will consider the UI task complete when the XpsDataAvailable event handler exits or when it calls an async method. 当应用已从用户与 UI 的交互中收集了所需的所有信息时,它应该会结束延迟以便打印系统继续。When the app has gathered all required information from the user's interaction with the UI, it should complete the deferral so that the print system can then advance.

internal async void OnXpsDataAvailable(PrintWorkflowForegroundSession sessionManager, PrintWorkflowXpsDataAvailableEventArgs printTaskXpsAvailableEventArgs)
{
    // Take out a deferral
    Deferral xpsDataAvailableDeferral = printTaskXpsAvailableEventArgs.GetDeferral();

    SpoolStreamContent xpsStream = printTaskXpsAvailableEventArgs.Operation.XpsContent.GetSourceSpoolDataAsStreamContent();

    IInputStream inputStream = xpsStream.GetInputSpoolStream();

    using (var inputReader = new Windows.Storage.Streams.DataReader(inputStream))
    {
        // Read the XPS data from input stream
        byte[] xpsData = new byte[inputReader.UnconsumedBufferLength];
        while (inputReader.UnconsumedBufferLength > 0)
        {
            inputReader.ReadBytes(xpsData);
            // Do something with the XPS data, e.g. preview
            // ...
        }
    }

    // Complete the deferral taken out at the start of this method
    xpsDataAvailableDeferral.Complete();
}

此外,事件参数公开的 PrintWorkflowSubmittedOperation 实例提供了选项来取消打印作业,或指示打印作业成功完成但是无需输出打印作业。Additionally, the PrintWorkflowSubmittedOperation instance exposed by the event args provides the option to cancel the print job or to indicate that the job is successful but that no output print job will be needed. 这是通过使用 PrintWorkflowSubmittedStatus 值调用 Complete 方法完成的。This is done by calling the Complete method with a PrintWorkflowSubmittedStatus value.

备注

如果工作流应用取消打印作业,强烈建议它提供指示取消打印作业原因的 toast 通知。If the workflow app cancels the print job, it is highly recommended that it provide a toast notification indicating why the job was cancelled.

对打印内容执行最终后台工作Do final background work on the print content

UI 在 PrintTaskXpsDataAvailable 事件中完成延迟(或绕过 UI 步骤)之后,打印系统将为后台任务引发 Submitted 事件。Once the UI has completed the deferral in the PrintTaskXpsDataAvailable event (or if the UI step was bypassed), the print system will fire the Submitted event for the background task. 在对此事件的处理程序中,工作流应用可以访问 XpsDataAvailable 事件提供的所有相同数据。In the handler for this event, the workflow app can get access to all of the same data provided by the XpsDataAvailable event. 但是,与之前的所有事件都不同的是,Submitted 还通过 PrintWorkflowTarget 实例提供对最终打印作业的写入访问权限。However, unlike any of the previous events, Submitted also provides write access to the final print job content through a PrintWorkflowTarget instance.

用于为进行最终打印而在后台打印数据的对象取决于是以原始字节流还是以 XPS 对象模型的形式访问源数据。The object that is used to spool the data for final printing depends on whether the source data is accessed as a raw byte stream or as the XPS object model. 当工作流应用通过字节流访问源数据时,将提供用来写入最终作业数据的输出字节流。When the workflow app accesses the source data through a byte stream, an output byte stream is provided to write the final job data to. 当工作流应用通过对象模型访问源数据时,将提供用来将对象写入输出作业的文档写入程序。When the workflow app accesses the source data through the object model, a document writer is provided to write objects to the output job. 无论是哪一种情况,工作流应用都应该读取所有源数据、根据需要修改任何数据,并将修改后的数据写入输出目标。In either case, the workflow app should read all of the source data, modify any data required, and write the modified data to the output target.

当后台任务完成写入数据时,它应该对相应的 PrintWorkflowSubmittedOperation 对象调用 CompleteWhen the background task finishes writing the data, it should call Complete on the corresponding PrintWorkflowSubmittedOperation object. 工作流应用完成此步骤后,Submitted 事件处理程序退出,工作流会话关闭,用户可以通过标准打印对话框监视最终打印作业的状态。Once the workflow app completes this step and the Submitted event handler exits, the workflow session is closed and the user can monitor the status of the final print job through the standard print dialogs.

最终步骤Final steps

向打印机注册打印工作流应用Register the print workflow app to the printer

工作流应用使用与 WSDA 相同类型的元数据文件提交与打印机关联。Your workflow app is associated with a printer using the same type of metadata file submission as for WSDAs. 实际上,一个 UWP 应用程序既可以充当工作流应用,又可以充当提供打印任务设置功能的 WSDA。In fact, a single UWP application can act as both a workflow app and a WSDA that provides print task settings functionality. 按照相应创建元数据关联的 WSDA 步骤操作。Follow the corresponding WSDA steps for creating the metadata association.

区别是 WSDA 会自动为用户激活(该应用将始终在用户通过关联设备打印时启动),而工作流应用不会。The difference is that while WSDAs are automatically activated for the user (the app will always launch when that user prints on the associated device), workflow apps are not. 后者有一个不同的策略,必须对其进行设置。They have a separate policy that must be set.

设置工作流应用的策略Set the workflow app's policy

工作流应用策略通过在要运行工作流应用的设备上的 Powershell 命令设置。The workflow app policy is set by Powershell commands on the device that is to run the workflow app. 将修改 Set-Printer、Add-Printer(现有端口)和 Add-Printer(新 WSD 端口)命令以允许设置工作流策略。The Set-Printer, Add-Printer (existing port) and Add-Printer (new WSD port) commands will be modified to allow Workflow policies to be set.

  • Disabled:不会激活工作流应用。Disabled: Workflow apps will not be activated.
  • Uninitialized:如果工作流 DCA 已安装在系统中,则将激活工作流应用。Uninitialized: Workflow apps will be activated if the Workflow DCA is installed in the system. 如果未安装该应用,仍将继续进行打印。If the app is not installed, printing will still proceed.
  • Enabled:如果工作流 DCA 已安装在系统中,则将激活工作流合同。Enabled: Workflow contract will be activated if the Workflow DCA is installed in the system. 如果未安装该应用,打印将失败。If the app is not installed, printing will fail.

以下命令可使工作流应用在指定打印机上成为必需。The following command makes the workflow app required on the specified printer.

Set-Printer –Name "Microsoft XPS Document Writer" -WorkflowPolicy Enabled

本地用户可以在本地打印机上运行此策略,或者,对于企业实施,打印机管理员可以在打印服务器上运行此策略。A local user can run this policy on a local printer, or, for enterprise implementation, the printer administrator can run this policy on the Print Server. 然后,该策略将同步到所有客户端连接。The policy will then be synchronized to all client connections. 只要增加新打印机,打印机管理员就可以使用此策略。The printer admin can use this policy whenever a new printer is added.

另请参阅See also

工作流应用示例Workflow app sample

Windows.Graphics.Printing.Workflow 命名空间Windows.Graphics.Printing.Workflow namespace