Personalizzare il flusso di lavoro di stampa

Crea esperienze personalizzate del flusso di lavoro di stampa tramite l'uso di un'app flusso di lavoro di stampa.

Panoramica

Le app del flusso di lavoro di stampa sono app UWP che espandono le funzionalità delle app per dispositivi di Microsoft Store (WSDAs), quindi sarà utile avere familiarità con WSDA prima di continuare.

Come nel caso di WSDA, quando l'utente di un'applicazione di origine sceglie di stampare un elemento e passa attraverso la finestra di dialogo di stampa, il sistema verifica se un'app del flusso di lavoro è associata a tale stampante. In caso affermativo, l'app flusso di lavoro di stampa viene avviata (principalmente come attività in background, più avanti). Un'app flusso di lavoro è in grado di modificare sia il ticket di stampa (il documento XML che configura le impostazioni del dispositivo della stampante per l'attività di stampa corrente) che il contenuto XPS effettivo da stampare. Può facoltativamente esporre questa funzionalità all'utente avviando un'interfaccia utente a metà del processo. Dopo aver eseguito il lavoro, passa il contenuto di stampa e il ticket di stampa al driver.

Poiché include componenti in background e in primo piano e perché è associato funzionalmente ad altre app, un'app flusso di lavoro di stampa può essere più complessa da implementare rispetto ad altre categorie di app UWP. È consigliabile esaminare l'esempio dell'app Flusso di lavoro durante la lettura di questa guida per comprendere meglio come implementare le diverse funzionalità. Alcune funzionalità, ad esempio i vari controlli degli errori e la gestione dell'interfaccia utente, sono assenti da questa guida per motivi di semplicità.

Introduzione

L'app del flusso di lavoro deve indicare il punto di ingresso al sistema di stampa in modo che possa essere avviato al momento appropriato. A tale scopo, inserire la dichiarazione seguente nell'elemento Application/Extensions del file package.appxmanifest appartenente al progetto UWP.

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

Importante

Esistono molti scenari in cui la personalizzazione della stampa non richiede l'input dell'utente. Per questo motivo, le app del flusso di lavoro di stampa vengono eseguite come attività in background per impostazione predefinita.

Se un'app flusso di lavoro è associata all'applicazione di origine che ha avviato il processo di stampa (vedere la sezione successiva per istruzioni su questo), il sistema di stampa esamina i relativi file manifesto per un punto di ingresso dell'attività in background.

Eseguire operazioni in background sul ticket di stampa

La prima cosa che il sistema di stampa esegue con l'app del flusso di lavoro è attivare l'attività in background (in questo caso la WfBackgroundTask classe nello spazio dei nomi WFBackgroundTasks ). Nel metodo dell'attività in background Run è necessario eseguire il cast dei dettagli del trigger dell'attività come istanza PrintWorkflowTriggerDetails . In questo modo verranno fornite le funzionalità speciali per un'attività in background del flusso di lavoro di stampa. Espone la proprietà PrintWorkflowSession, che è un'istanza di PrintWorkFlowBackgroundSession. Le classi di sessione del flusso di lavoro di stampa, sia in background che in primo piano, controllano i passaggi sequenziali dell'app flusso di lavoro di stampa.

Registrare quindi i metodi del gestore per i due eventi generati da questa classe di sessione. Questi metodi verranno definiti in un secondo momento.

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

Quando viene richiamato il Start metodo, lo strumento di gestione sessione genererà in primo luogo l'evento SetupRequested . Questo evento espone informazioni generali sull'attività di stampa, nonché sul ticket di stampa. In questa fase, il ticket di stampa può essere modificato in 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();

    // ...

È essenziale notare che in base alla gestione di SetupRequested, l'app determinerà se avviare un componente in primo piano. Questo potrebbe dipendere da un'impostazione salvata in precedenza nella risorsa di archiviazione locale o da un evento che si è verificato durante la modifica del ticket di stampa oppure potrebbe trattarsi di un'impostazione statica dell'app specifica.

// ...

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

Eseguire operazioni in primo piano sul processo di stampa (facoltativo)

Se è stato richiamato il metodo SetRequiresUI , il sistema di stampa esaminerà il file manifesto per il punto di ingresso all'applicazione in primo piano. L'elemento Application/Extensions del file package.appxmanifest deve avere le righe seguenti. Sostituire il valore di EntryPoint con il nome dell'app in primo piano.

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

Il sistema di stampa richiama quindi il metodo OnActivated per il punto di ingresso dell'app specificato. Nel metodo OnActivated del relativo file App.xaml.cs, l'app del flusso di lavoro deve controllare il tipo di attivazione per verificare che si tratti di un'attivazione del flusso di lavoro. In tal caso, l'app del flusso di lavoro può eseguire il cast degli argomenti di attivazione a un oggetto PrintWorkflowUIActivatedEventArgs che espone un oggetto PrintWorkflowForegroundSession come proprietà. Questo oggetto, come la controparte di sfondo nella sezione precedente, contiene eventi generati dal sistema di stampa ed è possibile assegnare gestori a questi elementi. In questo caso, la funzionalità di gestione degli eventi verrà implementata in una classe separata denominata WorkflowPage.

Innanzitutto, nel file 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();
    }
}

Dopo che l'interfaccia utente ha collegato gestori eventi e il metodo OnActivated è stato chiuso, il sistema di stampa attiverà l'evento SetupRequested per l'interfaccia utente da gestire. Questo evento fornisce gli stessi dati forniti dall'evento di configurazione dell'attività in background, incluse le informazioni sul processo di stampa e il documento del ticket di stampa, ma senza la possibilità di richiedere l'avvio di un'interfaccia utente aggiuntiva. Nel file 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();
    }
}

Il sistema di stampa genererà quindi l'evento XpsDataAvailable per l'interfaccia utente. Nel gestore per questo evento, l'app del flusso di lavoro può accedere a tutti i dati disponibili per l'evento di installazione e può anche leggere direttamente i dati XPS, come flusso di byte non elaborati o come modello a oggetti. L'accesso ai dati XPS consente all'interfaccia utente di fornire servizi di anteprima di stampa e fornire informazioni aggiuntive all'utente sulle operazioni che l'app flusso di lavoro eseguirà sui dati.

Come parte di questo gestore eventi, l'app del flusso di lavoro deve acquisire un oggetto di differimento se continuerà a interagire con l'utente. Senza un differimento, il sistema di stampa considererà l'attività dell'interfaccia utente completata quando il gestore eventi XpsDataAvailable viene chiuso o quando chiama un metodo asincrono. Quando l'app ha raccolto tutte le informazioni necessarie dall'interazione dell'utente con l'interfaccia utente, dovrà completare il differimento in modo che il sistema di stampa possa quindi avanzare.

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

Inoltre, l'istanza PrintWorkflowSubmittedOperation esposta dagli argomenti dell'evento offre la possibilità di annullare il processo di stampa o di indicare che il processo ha esito positivo, ma che non sarà necessario alcun processo di stampa di output. A tale scopo, richiamare il metodo Complete con un valore PrintWorkflowSubmittedStatus.

Nota

Se l'app del flusso di lavoro annulla il processo di stampa, è consigliabile inviare una notifica di tipo avviso popup che indica il motivo per cui il processo è stato annullato.

Eseguire operazioni in background finali sul contenuto di stampa

Dopo che l'interfaccia utente ha completato il differimento nell'evento PrintTaskXpsDataAvailable (o se il passaggio dell'interfaccia utente è stato ignorato), il sistema di stampa attiverà l'evento Submitted per l'attività in background. Nel gestore per questo evento, l'app del flusso di lavoro può accedere a tutti gli stessi dati forniti dall'evento XpsDataAvailable. Tuttavia, a differenza di uno qualsiasi degli eventi precedenti, Submitted fornisce anche l'accesso write al contenuto del processo di stampa finale tramite un'istanza PrintWorkflowTarget instance.

L'oggetto utilizzato per eseguire lo spooling dei dati per la stampa finale dipende dall'accesso ai dati di origine come flusso di byte non elaborato o come modello a oggetti XPS. Quando l'app del flusso di lavoro accede ai dati di origine tramite un flusso di byte, viene fornito un flusso di byte di output in cui scrivere i dati finali del processo. Quando l'app flusso di lavoro accede ai dati di origine tramite il modello a oggetti, viene fornito un writer di documenti per scrivere oggetti nel processo di output. In entrambi i casi, l'app del flusso di lavoro deve leggere tutti i dati di origine, modificare i dati necessari e scrivere i dati modificati nella destinazione di output.

Al termine della scrittura dei dati, l'attività in background deve richiamare Complete sull'oggetto PrintWorkflowSubmittedOperation corrispondente. Dopo che l'app del flusso di lavoro completa questo passaggio e il gestore eventi Inviato viene chiuso, la sessione del flusso di lavoro viene chiusa e l'utente può monitorare lo stato del processo di stampa finale tramite le finestre di dialogo di stampa standard.

Passaggi finali

Registrare l'app flusso di lavoro di stampa nella stampante

L'app flusso di lavoro è associata a una stampante usando lo stesso tipo di invio di file di metadati per WSDA. Infatti, una singola applicazione UWP può fungere sia da app del flusso di lavoro che da WSDA che fornisce la funzionalità delle impostazioni delle attività di stampa. Seguire i passaggi WSDA corrispondenti per creare l'associazione di metadati.

La differenza è che mentre le applicazioni WSDA vengono attivate automaticamente per l'utente (l'app verrà sempre avviata quando l'utente stampa sul dispositivo associato), ciò non accade per le app del flusso di lavoro. Infatti, hanno un criterio separato che deve essere impostato.

Impostare i criteri dell'app del flusso di lavoro

I criteri dell'app del flusso di lavoro vengono impostati dai comandi di PowerShell nel dispositivo che esegue l'app del flusso di lavoro. I comandi Set-Printer, Add-Printer (porta esistente) e Add-Printer (nuova porta WSD) verranno modificati per consentire l'impostazione dei criteri flusso di lavoro.

  • Disabled: le app del flusso di lavoro non verranno attivate.
  • Uninitialized: le app del flusso di lavoro verranno attivate se il flusso di lavoro DCA è installato nel sistema. Se l'app non è installata, la stampa continuerà.
  • Enabled: il contratto del flusso di lavoro verrà attivato se il flusso di lavoro DCA è installato nel sistema. Se l'app non è installata, la stampa avrà esito negativo.

Il comando seguente rende necessaria l'app del flusso di lavoro nella stampante specificata.

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

Un utente locale può eseguire questo criterio su una stampante locale o, per l'implementazione aziendale, l'amministratore della stampante può eseguire questo criterio nel server di stampa. I criteri verranno quindi sincronizzati con tutte le connessioni client. L'amministratore della stampante può utilizzare questo criterio ogni volta che viene aggiunta una nuova stampante.

Vedi anche

Esempio di app flusso di lavoro

Windows.Graphics.Printing.Workflow namespace