Compartilhar via


Como exibir uma caixa de diálogo de impressão (WPF .NET)

Deseja imprimir do seu aplicativo? Você pode usar a classe PrintDialog para abrir uma caixa de diálogo de impressão padrão do Microsoft Windows. Veja como.

Nota

O controle System.Windows.Controls.PrintDialog usado para o WPF e discutido aqui, não deve ser confundido com o componente System.Windows.Forms.PrintDialog do Windows Forms.

A classe PrintDialog fornece um único controle para configuração de impressão e envio de trabalho de impressão. O controle é fácil de usar e pode ser instanciado usando marcação XAML ou código. Os exemplos a seguir criam e exibem uma instância de PrintDialog usando código.

Você pode usar a caixa de diálogo de impressão para configurar opções de impressão, como:

  • Imprima apenas um intervalo específico de páginas.
  • Selecione entre impressoras instaladas em seu computador. Você pode usar a opção Gravador de Documentos do Microsoft XPS para criar esses tipos de documento:
    • XPS (Especificação de Papel XML)
    • Open XML Paper Specification (OpenXPS)

Este exemplo imprime todas as páginas de um documento XPS. Por padrão, o código:

  1. Abra uma janela de diálogo de impressão que solicita que o usuário selecione uma impressora e inicie um trabalho de impressão.
  2. Instancie um objeto XpsDocument com o conteúdo do documento XPS.
  3. Use o objeto XpsDocument para gerar um objeto DocumentPaginator que contém todas as páginas do documento XPS.
  4. Chame o método PrintDocument, passando o objeto DocumentPaginator, para enviar todas as páginas para a impressora especificada.
/// <summary>
/// Print all pages of an XPS document.
/// Optionally, hide the print dialog window.
/// </summary>
/// <param name="xpsFilePath">Path to source XPS file</param>
/// <param name="hidePrintDialog">Whether to hide the print dialog window (shown by default)</param>
/// <returns>Whether the document printed</returns>
public static bool PrintWholeDocument(string xpsFilePath, bool hidePrintDialog = false)
{
    // Create the print dialog object and set options.
    PrintDialog printDialog = new();

    if (!hidePrintDialog)
    {
        // Display the dialog. This returns true if the user presses the Print button.
        bool? isPrinted = printDialog.ShowDialog();
        if (isPrinted != true)
            return false;
    }

    // Print the whole document.
    try
    {
        // Open the selected document.
        XpsDocument xpsDocument = new(xpsFilePath, FileAccess.Read);

        // Get a fixed document sequence for the selected document.
        FixedDocumentSequence fixedDocSeq = xpsDocument.GetFixedDocumentSequence();

        // Create a paginator for all pages in the selected document.
        DocumentPaginator docPaginator = fixedDocSeq.DocumentPaginator;

        // Print to a new file.
        printDialog.PrintDocument(docPaginator, $"Printing {Path.GetFileName(xpsFilePath)}");

        return true;
    }
    catch (Exception e)
    {
        MessageBox.Show(e.Message);

        return false;
    }
}
''' <summary>
''' Print all pages of an XPS document.
''' Optionally, print all pages without showing a print dialog window.
''' </summary>
''' <param name="xpsFilePath">Path to source XPS file</param>
''' <param name="hidePrintDialog">Whether to hide the print dialog window (shown by default)</param>
''' <returns>Whether the document printed</returns>
Public Shared Function PrintWholeDocument(xpsFilePath As String, Optional hidePrintDialog As Boolean = False) As Boolean

    ' Create the print dialog object and set options.
    Dim printDialog As New PrintDialog

    If Not hidePrintDialog Then

        ' Display the dialog. This returns true if the user presses the Print button.
        Dim isPrinted As Boolean? = printDialog.ShowDialog()
        If isPrinted <> True Then Return False

    End If

    ' Print the whole document.
    Try

        ' Open the selected document.
        Dim xpsDocument As New XpsDocument(xpsFilePath, FileAccess.Read)

        ' Get a fixed document sequence for the selected document.
        Dim fixedDocSeq As FixedDocumentSequence = xpsDocument.GetFixedDocumentSequence()

        ' Create a paginator for all pages in the selected document.
        Dim docPaginator As DocumentPaginator = fixedDocSeq.DocumentPaginator

        ' Print to a new file.
        printDialog.PrintDocument(docPaginator, $"Printing {Path.GetFileName(xpsFilePath)}")

        Return True

    Catch e As Exception

        MessageBox.Show(e.Message)

        Return False

    End Try

End Function

Às vezes, você só deseja imprimir um intervalo específico de páginas em um documento XPS. Para fazer isso, estendemos a classe DocumentPaginator abstrata para adicionar suporte a intervalos de páginas. Por padrão, o código:

  1. Abra uma janela de diálogo de impressão que solicita que o usuário selecione uma impressora, especifique um intervalo de páginas e inicie um trabalho de impressão.
  2. Instancie um objeto XpsDocument com o conteúdo do documento XPS.
  3. Use o objeto XpsDocument para gerar um objeto DocumentPaginator padrão que contém todas as páginas do documento XPS.
  4. Crie uma instância da classe DocumentPaginator estendida que dê suporte a intervalos de páginas, passando o objeto padrão DocumentPaginator e a estrutura PageRange retornada pelo PrintDialog.
  5. Chame o método PrintDocument, passando uma instância da classe estendida DocumentPaginator, para enviar o intervalo de páginas especificado para a impressora especificada.
/// <summary>
/// Print a specific range of pages within an XPS document.
/// </summary>
/// <param name="xpsFilePath">Path to source XPS file</param>
/// <returns>Whether the document printed</returns>
public static bool PrintDocumentPageRange(string xpsFilePath)
{
    // Create the print dialog object and set options.
    PrintDialog printDialog = new()
    {
        UserPageRangeEnabled = true
    };

    // Display the dialog. This returns true if the user presses the Print button.
    bool? isPrinted = printDialog.ShowDialog();
    if (isPrinted != true)
        return false;

    // Print a specific page range within the document.
    try
    {
        // Open the selected document.
        XpsDocument xpsDocument = new(xpsFilePath, FileAccess.Read);

        // Get a fixed document sequence for the selected document.
        FixedDocumentSequence fixedDocSeq = xpsDocument.GetFixedDocumentSequence();

        // Create a paginator for all pages in the selected document.
        DocumentPaginator docPaginator = fixedDocSeq.DocumentPaginator;

        // Check whether a page range was specified in the print dialog.
        if (printDialog.PageRangeSelection == PageRangeSelection.UserPages)
        {
            // Create a document paginator for the specified range of pages.
            docPaginator = new DocPaginator(fixedDocSeq.DocumentPaginator, printDialog.PageRange);
        }

        // Print to a new file.
        printDialog.PrintDocument(docPaginator, $"Printing {Path.GetFileName(xpsFilePath)}");

        return true;
    }
    catch (Exception e)
    {
        MessageBox.Show(e.Message);

        return false;
    }
}

/// <summary>
/// Extend the abstract DocumentPaginator class to support page range printing. This class is based on the following online resources:
///
/// https://www.thomasclaudiushuber.com/2009/11/24/wpf-printing-how-to-print-a-pagerange-with-wpfs-printdialog-that-means-the-user-can-select-specific-pages-and-only-these-pages-are-printed/
///
/// https://social.msdn.microsoft.com/Forums/vstudio/en-US/9180e260-0791-4f2d-962d-abcb22ba8d09/how-to-print-multiple-page-ranges-with-wpf-printdialog
///
/// https://social.msdn.microsoft.com/Forums/en-US/841e804b-9130-4476-8709-0d2854c11582/exception-quotfixedpage-cannot-contain-another-fixedpagequot-when-printing-to-the-xps-document?forum=wpf
/// </summary>
public class DocPaginator : DocumentPaginator
{
    private readonly DocumentPaginator _documentPaginator;
    private readonly int _startPageIndex;
    private readonly int _endPageIndex;
    private readonly int _pageCount;

    public DocPaginator(DocumentPaginator documentPaginator, PageRange pageRange)
    {
        // Set document paginator.
        _documentPaginator = documentPaginator;

        // Set page indices.
        _startPageIndex = pageRange.PageFrom - 1;
        _endPageIndex = pageRange.PageTo - 1;

        // Validate and set page count.
        if (_startPageIndex >= 0 &&
            _endPageIndex >= 0 &&
            _startPageIndex <= _documentPaginator.PageCount - 1 &&
            _endPageIndex <= _documentPaginator.PageCount - 1 &&
            _startPageIndex <= _endPageIndex)
            _pageCount = _endPageIndex - _startPageIndex + 1;
    }

    public override bool IsPageCountValid => true;

    public override int PageCount => _pageCount;

    public override IDocumentPaginatorSource Source => _documentPaginator.Source;

    public override Size PageSize { get => _documentPaginator.PageSize; set => _documentPaginator.PageSize = value; }

    public override DocumentPage GetPage(int pageNumber)
    {
        DocumentPage documentPage = _documentPaginator.GetPage(_startPageIndex + pageNumber);

        // Workaround for "FixedPageInPage" exception.
        if (documentPage.Visual is FixedPage fixedPage)
        {
            var containerVisual = new ContainerVisual();
            foreach (object child in fixedPage.Children)
            {
                var childClone = (UIElement)child.GetType().GetMethod("MemberwiseClone", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(child, null);

                FieldInfo parentField = childClone.GetType().GetField("_parent", BindingFlags.Instance | BindingFlags.NonPublic);
                if (parentField != null)
                {
                    parentField.SetValue(childClone, null);
                    containerVisual.Children.Add(childClone);
                }
            }

            return new DocumentPage(containerVisual, documentPage.Size, documentPage.BleedBox, documentPage.ContentBox);
        }

        return documentPage;
    }
}
''' <summary>
''' Print a specific range of pages within an XPS document.
''' </summary>
''' <param name="xpsFilePath">Path to source XPS file</param>
''' <returns>Whether the document printed</returns>
Public Shared Function PrintDocumentPageRange(xpsFilePath As String) As Boolean

    ' Create the print dialog object and set options.
    Dim printDialog As New PrintDialog With {
        .UserPageRangeEnabled = True
    }

    ' Display the dialog. This returns true if the user presses the Print button.
    Dim isPrinted As Boolean? = printDialog.ShowDialog()
    If isPrinted <> True Then Return False

    ' Print a specific page range within the document.
    Try

        ' Open the selected document.
        Dim xpsDocument As New XpsDocument(xpsFilePath, FileAccess.Read)

        ' Get a fixed document sequence for the selected document.
        Dim fixedDocSeq As FixedDocumentSequence = xpsDocument.GetFixedDocumentSequence()

        ' Create a paginator for all pages in the selected document.
        Dim docPaginator As DocumentPaginator = fixedDocSeq.DocumentPaginator

        ' Check whether a page range was specified in the print dialog.
        If printDialog.PageRangeSelection = PageRangeSelection.UserPages Then

            ' Create a document paginator for the specified range of pages.
            docPaginator = New DocPaginator(fixedDocSeq.DocumentPaginator, printDialog.PageRange)

        End If

        ' Print to a new file.
        printDialog.PrintDocument(docPaginator, $"Printing {Path.GetFileName(xpsFilePath)}")

        Return True

    Catch e As Exception

        MessageBox.Show(e.Message)

        Return False

    End Try

End Function

' Extend the abstract DocumentPaginator class to support page range printing.
' This class is based on the following online resources:
' https://www.thomasclaudiushuber.com/2009/11/24/wpf-printing-how-to-print-a-pagerange-with-wpfs-printdialog-
' that-means-the-user-can-select-specific-pages-and-only-these-pages-are-printed/
' https://social.msdn.microsoft.com/Forums/vstudio/en-US/9180e260-0791-4f2d-962d-abcb22ba8d09/how-to-print-
' multiple-page-ranges-with-wpf-printdialog
' https://social.msdn.microsoft.com/Forums/en-US/841e804b-9130-4476-8709-0d2854c11582/exception-quotfixedpage-
' cannot-contain-another-fixedpagequot-when-printing-to-the-xps-document?forum=wpf
Public Class DocPaginator
    Inherits DocumentPaginator

    Private ReadOnly _documentPaginator As DocumentPaginator
    Private ReadOnly _startPageIndex As Integer
    Private ReadOnly _endPageIndex As Integer
    Private ReadOnly _pageCount As Integer

    Public Sub New(documentPaginator As DocumentPaginator, pageRange As PageRange)

        ' Set document paginator.
        _documentPaginator = documentPaginator

        ' Set page indices.
        _startPageIndex = pageRange.PageFrom - 1
        _endPageIndex = pageRange.PageTo - 1

        ' Validate And set page count.
        If _startPageIndex >= 0 AndAlso
            _endPageIndex >= 0 AndAlso
            _startPageIndex <= _documentPaginator.PageCount - 1 AndAlso
            _endPageIndex <= _documentPaginator.PageCount - 1 AndAlso
            _startPageIndex <= _endPageIndex Then
            _pageCount = _endPageIndex - _startPageIndex + 1
        End If

    End Sub

    Public Overrides ReadOnly Property IsPageCountValid As Boolean
        Get
            Return True
        End Get
    End Property

    Public Overrides ReadOnly Property PageCount As Integer
        Get
            Return _pageCount
        End Get
    End Property

    Public Overrides ReadOnly Property Source As IDocumentPaginatorSource
        Get
            Return _documentPaginator.Source
        End Get
    End Property

    Public Overrides Property PageSize As Size
        Get
            Return _documentPaginator.PageSize
        End Get
        Set(value As Size)
            _documentPaginator.PageSize = value
        End Set
    End Property

    Public Overrides Function GetPage(pageNumber As Integer) As DocumentPage

        Dim documentPage As DocumentPage = _documentPaginator.GetPage(_startPageIndex + pageNumber)

        ' Workaround for "FixedPageInPage" exception.
        If documentPage.Visual.GetType() Is GetType(FixedPage) Then

            Dim fixedPage As FixedPage = documentPage.Visual
            Dim containerVisual = New ContainerVisual()

            For Each child As Object In fixedPage.Children
                Dim childClone = CType(child.[GetType]().GetMethod("MemberwiseClone", BindingFlags.Instance Or BindingFlags.NonPublic).Invoke(child, Nothing), UIElement)
                Dim parentField As FieldInfo = childClone.[GetType]().GetField("_parent", BindingFlags.Instance Or BindingFlags.NonPublic)

                If parentField IsNot Nothing Then
                    parentField.SetValue(childClone, Nothing)
                    containerVisual.Children.Add(childClone)
                End If
            Next

            Return New DocumentPage(containerVisual, documentPage.Size, documentPage.BleedBox, documentPage.ContentBox)

        End If

        Return documentPage

    End Function

End Class

Dica

Embora você possa usar o método PrintDocument para imprimir sem abrir a caixa de diálogo de impressão, por motivos de desempenho, é melhor usar o método AddJob ou um dos muitos métodos Write e WriteAsync do XpsDocumentWriter. Para obter mais informações sobre isso, consulte Como imprimir um arquivo XPS e Visão geral dos documentos de impressão.

Consulte também