Stampare dall'app

Questo argomento descrive come stampare da un'app di Windows.

Per funzionalità più avanzate, vedere Personalizza l'interfaccia utente dell'anteprima di stampa.

Registrarsi per la stampa

Il primo passaggio per aggiungere la stampa all'app consiste nel registrarsi per la stampa recuperando l'oggetto PrintManager per la finestra corrente. La classe PrintManager è responsabile dell'orchestrazione del flusso di stampa per l'app. Per utilizzare questa classe, è prima necessario chiamare il metodo che restituisce l'oggetto PrintManager specifico della finestra attiva corrente.

L'app deve eseguire questa operazione in ogni schermata da cui si vuole che l'utente possa stampare. Solo la schermata visualizzata all'utente può essere registrata per la stampa. Se una schermata dell'app è stata registrata per la stampa, deve annullare la registrazione per la stampa all'uscita. Se viene sostituito da un'altra schermata, la schermata successiva deve registrarsi per la stampa all'apertura.

Suggerimento

 Qualora sia necessario supportare la stampa da più pagine nella propria app, è possibile inserire questo codice di stampa in una classe helper comune e riutilizzare le pagine dell'app. Per un esempio di come eseguire questa operazione, vedere la PrintHelper classe in Esempio di stampa UWP.

Dopo che un utente ha avviato la stampa, utilizzare printDocument per preparare le pagine da inviare alla stampante. Il PrintDocument tipo si trova nello spazio dei nomi Microsoft.UI.Xaml.Printing insieme ad altri tipi che supportano la preparazione del contenuto XAML per la stampa.

La classe PrintDocument viene usata per gestire gran parte dell'interazione tra l'app e PrintManager, ma espone diversi propri callback. Durante la registrazione, creare istanze di e PrintDocument registrare i gestori per gli eventi di PrintManager stampa.

In questo esempio la registrazione viene eseguita nel RegisterForPrinting metodo , che viene chiamato dal gestore eventi Loaded della pagina.

using Microsoft.UI.Xaml.Printing;
using Windows.Graphics.Printing;

PrintDocument printDocument = null;
IPrintDocumentSource printDocumentSource = null;
List<UIElement> printPreviewPages = new List<UIElement>();

private void MainPage_Loaded(object sender, RoutedEventArgs e)
{
    RegisterForPrinting();
}

private void RegisterForPrinting()
{
    var hWnd = WinRT.Interop.WindowNative.GetWindowHandle(App.MainWindow);
    PrintManager printManager = PrintManagerInterop.GetForWindow(hWnd);
    printManager.PrintTaskRequested += PrintTask_Requested;

    printDocument = new PrintDocument();
    printDocumentSource = printDocument.DocumentSource;
    printDocument.Paginate += PrintDocument_Paginate;
    printDocument.GetPreviewPage += PrintDocument_GetPreviewPage;
    printDocument.AddPages += PrintDocument_AddPages;
}

Avviso

Negli esempi di stampa UWP è consigliabile registrarsi per la stampa dall'override del metodo OnNavigatedTo. Nelle app non UWP devi usare l'handle di finestra nella chiamata PrintManagerInterop.GetForWindow, quindi devi usare l'evento Loaded per assicurarti che l'handle della finestra non nullsia , che potrebbe essere il caso in OnNavigatedTo.

In questo caso, i gestori eventi vengono annullati la registrazione nel UnregisterForPrinting metodo , che viene chiamato dal metodo OnNavigatedFrom .

protected override void OnNavigatedFrom(NavigationEventArgs e)
{
    base.OnNavigatedFrom(e);
    UnregisterForPrinting();
}


private void UnregisterForPrinting()
{
    if (printDocument == null)
    {
        return;
    }

    printDocument.Paginate -= PrintDocument_Paginate;
    printDocument.GetPreviewPage -= PrintDocument_GetPreviewPage;
    printDocument.AddPages -= PrintDocument_AddPages;

    var hWnd = WinRT.Interop.WindowNative.GetWindowHandle(App.MainWindow);
    PrintManager printManager = PrintManagerInterop.GetForWindow(hWnd);
    printManager.PrintTaskRequested -= PrintTask_Requested;
}

Nota

Se si dispone di un'app a più pagine e non si scollega la stampa, viene lanciata un'eccezione quando l'utente abbandona la pagina e poi vi ritorna.

Creare un pulsante di stampa

Aggiungere un pulsante di stampa alla schermata dell'app in cui si vuole visualizzarla. Assicurarsi che non interferisca con il contenuto che si desidera stampare.

<Button x:Name="InvokePrintingButton"
        Content="Print"
        Click="InvokePrintingButton_Click"/>

Nel gestore dell'evento Click del pulsante visualizzare l'interfaccia utente di stampa di Windows all'utente.

var hWnd = WinRT.Interop.WindowNative.GetWindowHandle(App.MainWindow);
await PrintManagerInterop.ShowPrintUIForWindowAsync(hWnd);

Questo metodo è un metodo asincrono che visualizza la finestra di stampa appropriata, quindi è necessario aggiungere la parola chiave asincrona al gestore Click. È consigliabile richiamare prima il metodo IsSupported per verificare che l'app venga eseguita in un dispositivo che supporta la stampa (e gestire il caso in cui non lo è). Se non è possibile eseguire la stampa in quel momento per qualsiasi altro motivo, il metodo genererà un'eccezione. È consigliabile intercettare queste eccezioni e consentire all'utente di sapere quando la stampa non può continuare.

In questo esempio viene visualizzata una finestra di stampa nel gestore eventi per un clic su un pulsante. Se il metodo genera un'eccezione (perché la stampa non può essere eseguita in quel momento), un controllo ContentDialog informa l'utente della situazione.

private async void InvokePrintingButton_Click(object sender, RoutedEventArgs e)
{
    if (PrintManager.IsSupported())
    {
        try
        {
            // Show system print UI.
            var hWnd = WinRT.Interop.WindowNative.GetWindowHandle(App.MainWindow);
            await Windows.Graphics.Printing.PrintManagerInterop.ShowPrintUIForWindowAsync(hWnd);
        }
        catch
        {
            // Printing cannot proceed at this time.
            ContentDialog noPrintingDialog = new ContentDialog()
            {
                Title = "Printing error",
                Content = "\nSorry, printing can' t proceed at this time.",
                PrimaryButtonText = "OK"
            };
            await noPrintingDialog.ShowAsync();
        }
    }
    else
    {
        // Printing is not supported on this device.
        ContentDialog noPrintingDialog = new ContentDialog()
        {
            Title = "Printing not supported",
            Content = "\nSorry, printing is not supported on this device.",
            PrimaryButtonText = "OK"
        };
        await noPrintingDialog.ShowAsync();
    }
}

Formattare il contenuto dell'app

Quando ShowPrintUIForWindowAsync viene chiamato, viene generato l'evento PrintTaskRequested . PrintTaskRequested Nel gestore eventi si crea un printTask chiamando il metodo PrintTaskRequest.CreatePrintTaskTask. Passare il titolo per la pagina di stampa e il nome di un delegato PrintTaskSourceRequestedHandler . Il titolo viene visualizzato nell'interfaccia utente dell'anteprima di stampa. PrintTaskSourceRequestedHandler Collega l'oggetto PrintTask PrintDocument con che fornirà il contenuto.

In questo esempio viene definito anche un gestore di completamento per rilevare gli errori. È consigliabile gestire gli eventi di completamento perché l'app può comunicare all'utente se si è verificato un errore e fornire possibili soluzioni. Analogamente, l'app potrebbe usare l'evento di completamento per indicare i passaggi successivi che l'utente deve eseguire al termine del processo di stampa.

private void PrintTask_Requested(PrintManager sender, PrintTaskRequestedEventArgs args)
{
    // Create the PrintTask.
    // Defines the title and delegate for PrintTaskSourceRequested.
    PrintTask printTask = args.Request.CreatePrintTask("WinUI 3 Printing example", PrintTaskSourceRequested);

    // Handle PrintTask.Completed to catch failed print jobs.
    printTask.Completed += PrintTask_Completed;

    DispatcherQueue.TryEnqueue(DispatcherQueuePriority.Normal, () =>
    {
        InvokePrintingButton.IsEnabled = false;
    });
}

private void PrintTaskSourceRequested(PrintTaskSourceRequestedArgs args)
{
    // Set the document source.
    args.SetSource(printDocumentSource);
}

private void PrintTask_Completed(PrintTask sender, PrintTaskCompletedEventArgs args)
{
    string StatusBlockText = string.Empty;

    // Notify the user if the print operation fails.
    if (args.Completion == PrintTaskCompletion.Failed)
    {
        StatusBlockText = "Failed to print.";
    }
    else if (args.Completion == PrintTaskCompletion.Canceled)
    {
        StatusBlockText = "Printing canceled.";
    }
    else
    {
        StatusBlockText = "Printing completed.";
    }


    DispatcherQueue.TryEnqueue(DispatcherQueuePriority.Normal, () =>
    {
        StatusBlock.Text = StatusBlockText;
        InvokePrintingButton.IsEnabled = true;
    });
}

Dopo aver creato l'attività di stampa, PrintManager richiede una raccolta di pagine di stampa da visualizzare nell'interfaccia utente dell'anteprima di stampa generando l'evento Impagina. Corrisponde al Paginate metodo dell'interfaccia IPrintPreviewPageCollection . Al momento viene chiamato il gestore eventi creato durante la registrazione.

Importante

 Se l'utente modifica le impostazioni di stampa, il gestore eventi impaginato verrà richiamato di nuovo per consentire la rielaborazione del contenuto. Per un'esperienza utente ottimale, è consigliabile controllare le impostazioni prima di rielaborare il contenuto ed evitare di reinizializzare il contenuto impaginato quando non è necessario.

Nel gestore eventi Paginate creare le pagine da visualizzare nell'interfaccia utente dell'anteprima di stampa e inviare alla stampante. Il codice usato per preparare il contenuto dell'app per la stampa è specifico per l'app e per il contenuto stampato.

In questo esempio vengono illustrati i passaggi di base per creare una singola pagina di stampa che stampa un'immagine e una didascalia dalla pagina visualizzata sullo schermo.

  • Creare un elenco per contenere gli elementi dell'interfaccia utente (pagine) da stampare.
  • Cancellare l'elenco delle pagine di anteprima in modo che le pagine non siano duplicate ogni volta che viene eseguita la paginazione.
  • Utilizzare PrintPageDescription per ottenere le dimensioni della pagina della stampante.
  • Formattare il contenuto XAML in base alla pagina della stampante. Ogni pagina da stampare è un elemento dell'interfaccia utente XAML (in genere un elemento contenitore che contiene altri contenuti). In questo esempio gli elementi vengono creati nel codice e usano gli stessi dati degli elementi visualizzati sullo schermo.
  • Propagare il contenuto in pagine aggiuntive in base alle esigenze. In questo esempio di base non vengono visualizzate più pagine, ma la suddivisione del contenuto in pagine è una parte importante dell'evento Paginate.
  • Aggiungere ogni pagina all'elenco di pagine da stampare.
  • Impostare il numero di pagine di anteprima in PrintDocument.
List<UIElement> printPreviewPages = new List<UIElement>();

private void PrintDocument_Paginate(object sender, PaginateEventArgs e)
{
    // Clear the cache of preview pages.
    printPreviewPages.Clear();

    // Get the PrintTaskOptions.
    PrintTaskOptions printingOptions = ((PrintTaskOptions)e.PrintTaskOptions);
    // Get the page description to determine the size of the print page.
    PrintPageDescription pageDescription = printingOptions.GetPageDescription(0);

    // Create the print layout.
    StackPanel printLayout = new StackPanel();
    printLayout.Width = pageDescription.PageSize.Width;
    printLayout.Height = pageDescription.PageSize.Height;
    printLayout.BorderBrush = new Microsoft.UI.Xaml.Media.SolidColorBrush(Microsoft.UI.Colors.Black);
    printLayout.BorderThickness = new Thickness(48);

    Image printImage = new Image();
    printImage.Source = printContent.Source;

    printImage.Width = pageDescription.PageSize.Width / 2;
    printImage.Height = pageDescription.PageSize.Height / 2;

    TextBlock imageDescriptionText = new TextBlock();
    imageDescriptionText.Text = imageDescription.Text;
    imageDescriptionText.FontSize = 24;
    imageDescriptionText.HorizontalAlignment = HorizontalAlignment.Center;
    imageDescriptionText.Width = pageDescription.PageSize.Width / 2;
    imageDescriptionText.TextWrapping = TextWrapping.WrapWholeWords;

    printLayout.Children.Add(printImage);
    printLayout.Children.Add(imageDescriptionText);

    // Add the print layout to the list of preview pages.
    printPreviewPages.Add(printLayout);

    // Report the number of preview pages created.
    PrintDocument printDocument = (PrintDocument)sender;
    printDocument.SetPreviewPageCount(printPreviewPages.Count,
                                          PreviewPageCountType.Intermediate);
}

Ecco uno screenshot dell'interfaccia utente dell'app e il modo in cui il contenuto viene visualizzato nell'interfaccia utente dell'anteprima di stampa.

Screenshot di un'interfaccia utente dell'app accanto all'interfaccia utente dell'anteprima di stampa di sistema, che mostra un'immagine e una didascalia da stampare.

Quando una determinata pagina deve essere visualizzata nella finestra di anteprima di stampa, PrintManager richiama l'evento GetPreviewPage. Corrisponde al MakePage metodo dell'interfaccia IPrintPreviewPageCollection . Al momento viene chiamato il gestore eventi creato durante la registrazione.

Nel gestore eventi GetPreviewPage impostare la pagina appropriata nel documento di stampa.

private void PrintDocument_GetPreviewPage(object sender, GetPreviewPageEventArgs e)
{
    PrintDocument printDocument = (PrintDocument)sender;
    printDocument.SetPreviewPage(e.PageNumber, printPreviewPages[e.PageNumber - 1]);
}

Infine, quando l'utente fa clic sul pulsante di stampa, PrintManager richiede la raccolta finale di pagine da inviare alla stampante chiamando il MakeDocument metodo dell'interfaccia IDocumentPageSource . In XAML viene generato l'evento AddPages . Al momento viene chiamato il gestore eventi creato durante la registrazione.

Nel gestore eventi AddPages aggiungere pagine dall'insieme di pagine all'oggetto PrintDocument da inviare alla stampante. Se un utente specifica pagine particolari o un intervallo di pagine da stampare, utilizzare tali informazioni qui per aggiungere solo le pagine che verranno effettivamente inviate alla stampante.

private void PrintDocument_AddPages(object sender, AddPagesEventArgs e)
{
    PrintDocument printDocument = (PrintDocument)sender;

    // Loop over all of the preview pages and add each one to be printed.
    for (int i = 0; i < printPreviewPages.Count; i++)
    {
        printDocument.AddPage(printPreviewPages[i]);
    }

    // Indicate that all of the print pages have been provided.
    printDocument.AddPagesComplete();
}