Imprimir a partir da sua aplicação

Este tópico descreve como imprimir a partir de uma aplicação Windows.

Para funcionalidades mais avançadas, consulte Personalizar a interface de pré-visualização de impressão.

  • APIs importantes: espaço de nomes Windows.Graphics.Printing, PrintManager class, PrintTask, espaço de nomes Microsoft.UI.Xaml.Printing, PrintDocument class

Registe-se para impressão

O primeiro passo para adicionar impressão à sua aplicação é registar-se para impressão obtendo o objeto PrintManager para a janela atual. A classe PrintManager é responsável por orquestrar o fluxo de impressão do seu aplicativo. Para usar essa classe, você deve primeiro chamar o método que retorna o objeto PrintManager que é específico para a janela ativa atual.

Seu aplicativo deve fazer isso em todas as telas a partir das quais você deseja que seu usuário possa imprimir. Apenas o ecrã que é apresentado ao utilizador pode ser registado para impressão. Se um ecrã do seu aplicativo se registou para impressão, deve cancelar o registro para impressão quando sair do aplicativo. Se for substituído por outro ecrã, o ecrã seguinte deve registar-se para impressão quando abrir.

Sugestão

 Se precisar de suportar a impressão a partir de mais do que uma página na sua aplicação, pode colocar este código de impressão numa classe auxiliar comum e fazer com que as páginas da sua aplicação o reutilizem. Para um exemplo de como fazer isto, veja a PrintHelper classe no exemplo Print.

Depois de um utilizador iniciar a impressão, utiliza-se um PrintDocument para preparar as páginas a serem enviadas para a impressora. O PrintDocument tipo encontra-se no espaço de nomes Microsoft.UI.Xaml.Printing juntamente com outros tipos que suportam a preparação de conteúdo XAML para impressão.

A classe PrintDocument é usada para gerir grande parte da interação entre a aplicação e o PrintManager, mas expõe vários callbacks próprios. Durante o registo, crie instâncias de PrintManager e PrintDocument e registe manipuladores para os seus eventos de impressão.

Neste exemplo, o registo é realizado no RegisterForPrinting método, que é chamado a partir do manipulador do evento Carregado da página.

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

Advertência

Nos exemplos de impressão, recomenda-se registar-se para imprimir a partir do método de sobreposição OnNavigatedTo. Precisa de usar o handle da janela na chamada PrintManagerInterop.GetForWindow, por isso deve usar o evento Loaded para garantir que o handle da janela não seja null, que é o que pode ocorrer no OnNavigatedTo.

Aqui, os gestores de eventos não estão registados no UnregisterForPrinting método, que é chamado do método 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;
}

Observação

Se tiver uma aplicação de várias páginas e não desligar a impressão, será lançada uma exceção quando o utilizador sair da página e depois voltar a ela.

Criar um botão de impressão

Adicione um botão de impressão ao ecrã da sua aplicação onde pretende que apareça. Certifique-se de que não interfere com o conteúdo que pretende imprimir.

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

No manejador de eventos de clique do botão, mostre a interface de impressão do Windows ao utilizador.

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

Esse método é um método assíncrono que exibe a janela de impressão apropriada, portanto, você precisará adicionar a palavra-chave async ao manipulador Click. Recomendamos chamar primeiro o método IsSupported para verificar se a aplicação está a ser executada num dispositivo que suporta impressão (e tratar do caso em que não está). Se a impressão não puder ser realizada nesse momento por qualquer outro motivo, o método lançará uma exceção. Recomendamos capturar essas exceções e informar o usuário quando a impressão não puder continuar.

Neste exemplo, uma janela de impressão é exibida no manipulador de eventos para um clique no botão. Se o método lançar uma exceção (porque a impressão não pode ser realizada nesse momento), um controlo ContentDialog informa o utilizador da situação.

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

Formatar o conteúdo da sua aplicação

Quando ShowPrintUIForWindowAsync é chamado, o evento PrintTaskRequested é levantado. No gestor de PrintTaskRequested eventos, cria-se um PrintTask chamando o método PrintTaskRequest.CreatePrintTask . Passe o título da página de impressão e o nome de um delegado PrintTaskSourceRequestedHandler . O título é mostrado na interface de visualização de impressão. PrintTaskSourceRequestedHandler liga o PrintTask ao PrintDocument que fornecerá o conteúdo.

Neste exemplo, um manipulador de conclusão também é definido para detetar erros. É uma boa ideia lidar com eventos de conclusão porque, em seguida, seu aplicativo pode informar ao usuário se ocorreu um erro e fornecer possíveis soluções. Da mesma forma, seu aplicativo pode usar o evento de conclusão para indicar as etapas subsequentes a serem executadas pelo usuário após o trabalho de impressão ser bem-sucedido.

private void PrintTask_Requested(PrintManager sender, PrintTaskRequestedEventArgs args)
{
    // Create the PrintTask.
    // Defines the title and delegate for PrintTaskSourceRequested.
    PrintTask printTask = args.Request.CreatePrintTask("WinUI 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;
    });
}

Depois de criada a tarefa de impressão, o PrintManager solicita uma coleção de páginas de impressão para aparecer na interface de pré-visualização de impressão, levantando o evento Paginate . (Isto corresponde ao Paginate método da IPrintPreviewPageCollection interface.) O gestor de eventos que criou durante o registo é chamado neste momento.

Importante

 Se o utilizador alterar as configurações de impressão, o manipulador de eventos de paginação será chamado novamente para permitir que o conteúdo seja refluído. Para obter a melhor experiência do usuário, recomendamos verificar as configurações antes de refluir o conteúdo e evitar reinicializar o conteúdo paginado quando não for necessário.

No gestor de eventos Paginar, crie as páginas para mostrar na pré-visualização de impressão e enviar à impressora. O código que você usa para preparar o conteúdo do seu aplicativo para impressão é específico para seu aplicativo e o conteúdo que você imprime.

Este exemplo mostra as etapas básicas para criar uma única página de impressão que imprime uma imagem e legenda da página mostrada na tela.

  • Crie uma lista para armazenar os elementos da interface do usuário (páginas) a serem impressos.
  • Limpe a lista de páginas de visualização para que as páginas não sejam duplicadas sempre que ocorrer paginação.
  • Use a PrintPageDescription para obter o tamanho da página da impressora.
  • Formate o conteúdo em XAML para se ajustar à página da impressora. Cada página a ser impressa é um elemento da interface do usuário XAML (normalmente um elemento container que contém outro conteúdo). Neste exemplo, os elementos são criados em código e usam os mesmos dados que os elementos mostrados na tela.
  • Distribua o conteúdo por páginas adicionais conforme necessário. Várias páginas não são mostradas neste exemplo básico, mas dividir o conteúdo em páginas é uma parte importante do evento Paginate.
  • Adicione cada página à lista de páginas a imprimir.
  • Defina a contagem de páginas de visualização no 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);
}

Aqui está uma captura de tela da interface do aplicativo e de como o conteúdo aparece na visualização de impressão.

Uma captura de ecrã da interface de uma aplicação ao lado da interface de pré-visualização de impressão do sistema, mostrando uma imagem e legenda a imprimir.

Quando uma página específica for mostrada na janela de pré-visualização de impressão, o PrintManager levanta o evento GetPreviewPage . Isto corresponde ao MakePage método da IPrintPreviewPageCollection interface. O manipulador de eventos criado durante o registro é chamado neste momento.

No manipulador de eventos GetPreviewPage, defina a página apropriada no documento de impressão.

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

Finalmente, assim que o utilizador clica no botão de impressão, o PrintManager solicita a coleção final de páginas para enviar à impressora, chamando o MakeDocument método da IDocumentPageSource interface. No XAML, isto levanta o evento AddPages . O manipulador de eventos criado durante o registro é chamado neste momento.

No manipulador de eventos AddPages, adicione as páginas da coleção de páginas ao objecto PrintDocument para envio para a impressora. Se um usuário especificar páginas específicas ou um intervalo de páginas para imprimir, use essas informações aqui para adicionar apenas as páginas que realmente serão enviadas para a impressora.

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