Personalizar el flujo de trabajo de impresión

Cree experiencias de flujo de trabajo de impresión personalizadas mediante el uso de una aplicación de flujo de trabajo de impresión.

Información general

Las aplicaciones de flujo de trabajo de impresión son aplicaciones para UWP que amplían la funcionalidad de las aplicaciones de dispositivos de Microsoft Store (WSDAs), por lo que será útil tener cierta familiaridad con WSDAs antes de continuar.

Al igual que en el caso de WSDAs, cuando el usuario de una aplicación de origen elige imprimir algo y navega por el cuadro de diálogo de impresión, el sistema comprueba si una aplicación de flujo de trabajo está asociada a esa impresora. Si es así, se inicia la aplicación de flujo de trabajo de impresión (principalmente como tarea en segundo plano; más información sobre esto a continuación). Una aplicación de flujo de trabajo puede modificar el vale de impresión (el documento XML que configura la configuración del dispositivo de impresora para la tarea de impresión actual) y el contenido XPS real que se va a imprimir. Opcionalmente, puede exponer esta funcionalidad al usuario iniciando una interfaz de usuario a mitad del proceso. Después de realizar su trabajo, pasa el contenido de impresión y el vale de impresión al controlador.

Dado que implica componentes en segundo plano y en primer plano, y debido a que funciona funcionalmente con otras aplicaciones, una aplicación de flujo de trabajo de impresión puede ser más complicada de implementar que otras categorías de aplicaciones para UWP. Se recomienda inspeccionar el ejemplo de aplicación flujo de trabajo mientras lee esta guía para comprender mejor cómo se pueden implementar las distintas características. Algunas características, como las diversas comprobaciones de errores y la administración de la interfaz de usuario, no están presentes en esta guía por motivos de simplicidad.

Introducción

La aplicación de flujo de trabajo debe indicar su punto de entrada al sistema de impresión para que se pueda iniciar en el momento adecuado. Esto se hace insertando la siguiente declaración en el Application/Extensions elemento del archivo package.appxmanifest del proyecto de UWP.

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

Importante

Hay muchos escenarios en los que la personalización de impresión no requiere la entrada del usuario. Por este motivo, las aplicaciones de flujo de trabajo de impresión se ejecutan como tareas en segundo plano de forma predeterminada.

Si una aplicación de flujo de trabajo está asociada a la aplicación de origen que inició el trabajo de impresión (consulte la sección posterior para obtener instrucciones sobre esto), el sistema de impresión examina sus archivos de manifiesto para un punto de entrada de tarea en segundo plano.

Realizar un trabajo en segundo plano en el vale de impresión

Lo primero que hace el sistema de impresión con la aplicación de flujo de trabajo es activar su tarea en segundo plano (en este caso, la WfBackgroundTask clase en el WFBackgroundTasks espacio de nombres). En el método de Run la tarea en segundo plano, debe convertir los detalles del desencadenador de la tarea como una instancia printWorkflowTriggerDetails . Esto proporcionará la funcionalidad especial para una tarea en segundo plano del flujo de trabajo de impresión. Expone la propiedad PrintWorkflowSession , que es una instancia de PrintWorkFlowBackgroundSession. Las clases de sesión de flujo de trabajo de impresión ( tanto las variedades en segundo plano como las de primer plano) controlarán los pasos secuenciales de la aplicación de flujo de trabajo de impresión.

A continuación, registre los métodos de controlador para los dos eventos que generará esta clase de sesión. Definirá estos métodos más adelante.

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

Cuando se llama al Start método , el administrador de sesiones generará primero el evento SetupRequested . Este evento expone información general sobre la tarea de impresión, así como el vale de impresión. En esta fase, el vale de impresión se puede editar en segundo plano.

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();

    // ...

Lo importante es que en el control de SetupRequested que la aplicación determinará si se debe iniciar un componente en primer plano. Esto podría depender de una configuración que se guardó anteriormente en el almacenamiento local, o de un evento que se produjo durante la edición del vale de impresión, o puede ser una configuración estática de la aplicación en particular.

// ...

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();

Realizar un trabajo en primer plano en el trabajo de impresión (opcional)

Si se llamó al método SetRequiresUI , el sistema de impresión examinará el archivo de manifiesto del punto de entrada a la aplicación en primer plano. El Application/Extensions elemento del archivo package.appxmanifest debe tener las siguientes líneas. Reemplace el valor de EntryPoint por el nombre de la aplicación en primer plano.

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

A continuación, el sistema de impresión llama al método OnActivated para el punto de entrada de la aplicación especificado. En el método OnActivated de su archivo App.xaml.cs , la aplicación de flujo de trabajo debe comprobar el tipo de activación para comprobar que es una activación de flujo de trabajo. Si es así, la aplicación de flujo de trabajo puede convertir los argumentos de activación en un objeto PrintWorkflowUIActivatedEventArgs , que expone un objeto PrintWorkflowForegroundSession como una propiedad. Este objeto, al igual que su homólogo de fondo en la sección anterior, contiene eventos generados por el sistema de impresión y puede asignar controladores a estos. En este caso, la funcionalidad de control de eventos se implementará en una clase independiente denominada WorkflowPage.

En primer lugar, en el archivo App.xaml.cs :

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

Una vez que la interfaz de usuario tiene controladores de eventos adjuntos y se ha cerrado el método OnActivated , el sistema de impresión activará el evento SetupRequested para que la interfaz de usuario la controle. Este evento proporciona los mismos datos que proporciona el evento de configuración de tareas en segundo plano, incluida la información del trabajo de impresión y el documento de vale de impresión, pero sin la capacidad de solicitar el inicio de la interfaz de usuario adicional. En el archivo WorkflowPage.xaml.cs :

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

A continuación, el sistema de impresión generará el evento XpsDataAvailable para la interfaz de usuario. En el controlador de este evento, la aplicación de flujo de trabajo puede acceder a todos los datos disponibles para el evento de instalación y además puede leer los datos XPS directamente, ya sea como una secuencia de bytes sin procesar o como un modelo de objetos. El acceso a los datos XPS permite a la interfaz de usuario proporcionar servicios de vista previa de impresión y proporcionar información adicional al usuario sobre las operaciones que ejecutará la aplicación de flujo de trabajo en los datos.

Como parte de este controlador de eventos, la aplicación de flujo de trabajo debe adquirir un objeto de aplazamiento si seguirá interactuando con el usuario. Sin aplazamiento, el sistema de impresión considerará la tarea de interfaz de usuario completada cuando el controlador de eventos XpsDataAvailable salga o cuando llame a un método asincrónico. Cuando la aplicación ha recopilado toda la información necesaria de la interacción del usuario con la interfaz de usuario, debe completar el aplazamiento para que el sistema de impresión pueda avanzar.

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

Además, la instancia PrintWorkflowSubmittedOperation expuesta por los argumentos del evento proporciona la opción de cancelar el trabajo de impresión o indicar que el trabajo es correcto, pero que no se necesitará ningún trabajo de impresión de salida. Esto se hace llamando al método Complete con un valor PrintWorkflowSubmittedStatus .

Nota

Si la aplicación de flujo de trabajo cancela el trabajo de impresión, se recomienda encarecidamente proporcionar una notificación del sistema que indique por qué se canceló el trabajo.

Realizar el trabajo final en segundo plano en el contenido de impresión

Una vez que la interfaz de usuario haya completado el aplazamiento en el evento PrintTaskXpsDataAvailable (o si se omitió el paso de la interfaz de usuario), el sistema de impresión activará el evento Submitted para la tarea en segundo plano. En el controlador de este evento, la aplicación de flujo de trabajo puede obtener acceso a todos los mismos datos proporcionados por el evento XpsDataAvailable . Sin embargo, a diferencia de cualquiera de los eventos anteriores, Submitted también proporciona acceso de escritura al contenido final del trabajo de impresión a través de una instancia PrintWorkflowTarget .

El objeto que se usa para poner en cola los datos para la impresión final depende de si se tiene acceso a los datos de origen como una secuencia de bytes sin procesar o como modelo de objetos XPS. Cuando la aplicación de flujo de trabajo accede a los datos de origen a través de un flujo de bytes, se proporciona un flujo de bytes de salida en el que escribir los datos finales del trabajo. Cuando la aplicación de flujo de trabajo accede a los datos de origen a través del modelo de objetos, se proporciona un escritor de documentos para escribir objetos en el trabajo de salida. En cualquier caso, la aplicación de flujo de trabajo debe leer todos los datos de origen, modificar los datos necesarios y escribir los datos modificados en el destino de salida.

Cuando la tarea en segundo plano termine de escribir los datos, debe llamar a Complete en el objeto PrintWorkflowSubmittedOperation correspondiente. Una vez que la aplicación de flujo de trabajo completa este paso y el controlador de eventos enviado se cierra, se cierra la sesión de flujo de trabajo y el usuario puede supervisar el estado del trabajo de impresión final a través de los cuadros de diálogo de impresión estándar.

Pasos finales

Registrar la aplicación de flujo de trabajo de impresión en la impresora

La aplicación de flujo de trabajo está asociada a una impresora con el mismo tipo de envío de archivos de metadatos que para WSDAs. De hecho, una sola aplicación para UWP puede actuar como una aplicación de flujo de trabajo y un WSDA que proporciona funcionalidad de configuración de tareas de impresión. Siga los pasos de WSDA correspondientes para crear la asociación de metadatos.

La diferencia es que, aunque los WSDA se activan automáticamente para el usuario (la aplicación siempre se iniciará cuando ese usuario imprima en el dispositivo asociado), las aplicaciones de flujo de trabajo no. Tienen una directiva independiente que se debe establecer.

Establecimiento de la directiva de la aplicación de flujo de trabajo

Los comandos de PowerShell establecen la directiva de aplicación de flujo de trabajo en el dispositivo que va a ejecutar la aplicación de flujo de trabajo. Los comandos Set-Printer, Add-Printer (puerto existente) y Add-Printer (nuevo puerto WSD) se modificarán para permitir que se establezcan las directivas de flujo de trabajo.

  • Disabled: las aplicaciones de flujo de trabajo no se activarán.
  • Uninitialized: las aplicaciones de flujo de trabajo se activarán si el flujo de trabajo DCA está instalado en el sistema. Si la aplicación no está instalada, la impresión continuará.
  • Enabled: el contrato de flujo de trabajo se activará si el flujo de trabajo DCA está instalado en el sistema. Si la aplicación no está instalada, se producirá un error en la impresión.

El siguiente comando hace que la aplicación de flujo de trabajo sea necesaria en la impresora especificada.

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

Un usuario local puede ejecutar esta directiva en una impresora local o, para la implementación empresarial, el administrador de impresoras puede ejecutar esta directiva en el servidor de impresión. A continuación, la directiva se sincronizará con todas las conexiones de cliente. El administrador de impresoras puede usar esta directiva cada vez que se agrega una nueva impresora.

Vea también

Ejemplo de aplicación de flujo de trabajo

Espacio de nombres Windows.Graphics.Printing.Workflow