Freigeben über


Anzeigen eines Druckdialogfelds (WPF .NET)

Möchten Sie von Ihrer Anwendung aus drucken? Sie können die PrintDialog-Klasse verwenden, um ein standardmäßiges Microsoft Windows-Druckdialogfeld zu öffnen. Gehen Sie folgendermaßen vor:

Wichtig

Der Desktopleitfaden zu .NET 7 und .NET 6 ist in Bearbeitung.

Hinweis

Das für WPF verwendete und hier erläuterte System.Windows.Controls.PrintDialog-Steuerelement sollte nicht mit der System.Windows.Forms.PrintDialog-Komponente von Windows Forms verwechselt werden.

Die PrintDialog-Klasse bietet ein einzelnes Steuerelement für die Druckkonfiguration und Druckauftragsübermittlung. Das Steuerelement ist einfach zu verwenden und kann mithilfe von XAML-Markup oder Code instanziiert werden. In den folgenden Beispielen wird eine PrintDialog-Instanz mithilfe von Code erstellt und angezeigt.

Sie können das Druckdialogfeld verwenden, um Druckoptionen zu konfigurieren, z. B.:

  • Nur einen bestimmten Seitenbereich drucken.
  • Einen auf Ihrem Computer installierten Drucker auswählen. Sie können die Option Microsoft XPS Document Writer verwenden, um diese Dokumenttypen zu erstellen:
    • XML Paper Specification (XPS)
    • Open XML Paper Specification (OpenXPS)

In diesem Beispiel werden alle Seiten eines XPS-Dokuments gedruckt. Standardmäßig bewirkt der Code Folgendes:

  1. Ein Druckdialogfenster wird geöffnet, in dem der Benutzer aufgefordert wird, einen Drucker auszuwählen und einen Druckauftrag zu starten.
  2. Es wird ein XpsDocument-Objekt mit dem Inhalt des XPS-Dokuments instanziiert.
  3. Aus dem XpsDocument-Objekt wird ein DocumentPaginator-Objekt generiert, das alle Seiten des XPS-Dokuments enthält.
  4. Die PrintDocument-Methode wird aufgerufen und übergibt das DocumentPaginator-Objekt, um alle Seiten an den angegebenen Drucker zu senden.
/// <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

Manchmal möchten Sie nur einen bestimmten Seitenbereich in einem XPS-Dokument drucken. Dazu erweitern wir die abstrakte Klasse DocumentPaginator, um Unterstützung für Seitenbereiche hinzuzufügen. Standardmäßig bewirkt der Code Folgendes:

  1. Ein Druckdialogfenster wird geöffnet, in dem der Benutzer aufgefordert wird, einen Drucker sowie einen Seitenbereich auszuwählen und einen Druckauftrag zu starten.
  2. Es wird ein XpsDocument-Objekt mit dem Inhalt des XPS-Dokuments instanziiert.
  3. Aus dem XpsDocument-Objekt wird ein standardmäßiges DocumentPaginator-Objekt generiert, das alle Seiten des XPS-Dokuments enthält.
  4. Eine Instanz einer erweiterten DocumentPaginator-Klasse wird erstellt, die Seitenbereiche unterstützt, das Standardobjekt DocumentPaginator und die vom PrintDialog zurückgegebene PageRange-Struktur übergibt.
  5. Die PrintDocument-Methode wird aufgerufen und übergibt die Instanz der erweiterten DocumentPaginator-Klassen, um den angegebenen Seitenbereich an den angegebenen Drucker zu senden.
/// <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

Tipp

Obwohl Sie die PrintDocument-Methode verwenden können, um zu drucken, ohne das Druckdialogfeld zu öffnen, ist es aus Leistungsgründen besser, die AddJob-Methode oder eine der vielen Write- und WriteAsync-Methoden des XpsDocumentWriter zu nutzen. Weitere Informationen hierzu finden Sie unter Drucken einer XPS-Datei und Übersicht über das Drucken von Dokumenten.

Weitere Informationen