共用方式為


如何顯示列印對話方塊 (WPF .NET)

想要從您的應用程式列印嗎? 您可以使用 類別 PrintDialog 開啟標準 Microsoft Windows 列印對話方塊。 方法如下。

重要

.NET 7 和 .NET 6 的桌面指南檔正在建置中。

注意

System.Windows.Controls.PrintDialog用於 WPF 和此處討論的控制項不應與 System.Windows.Forms.PrintDialog Windows Forms 的元件混淆。

類別 PrintDialog 提供列印組態和列印工作提交的單一控制項。 控制項很容易使用,而且可以使用 XAML 標記或程式碼來具現化。 下列範例會使用程式碼建立和顯示 PrintDialog 實例。

您可以使用 [列印] 對話方塊來設定列印選項,例如:

  • 只列印特定頁面範圍。
  • 從電腦上安裝的印表機中選取 。 您可以使用 Microsoft XPS 檔寫入器 選項來建立這些檔案類型:
    • XML 文件規格 (XPS)
    • Open XML 紙張規格 (OpenXPS)

本範例會列印 XPS 檔的所有頁面。 根據預設,程式碼會:

  1. 開啟列印對話方塊視窗,提示使用者選取印表機並啟動列印工作。
  2. 使用 XPS 檔的內容具現化 XpsDocument 物件。
  3. XpsDocument使用 物件來產生 DocumentPaginator 物件,以保存 XPS 檔的所有頁面。
  4. PrintDocument呼叫 方法,傳入 DocumentPaginator 物件,以將所有頁面傳送至指定的印表機。
/// <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

有時候,您只想列印 XPS 檔中的特定頁面範圍。 若要這樣做,我們會擴充抽象 DocumentPaginator 類,以新增對頁面範圍的支援。 根據預設,程式碼會:

  1. 開啟列印對話方塊視窗,提示使用者選取印表機、指定頁面範圍,然後啟動列印工作。
  2. 使用 XPS 檔的內容具現化 XpsDocument 物件。
  3. XpsDocument使用 物件來產生保留 XPS 檔所有頁面的預設 DocumentPaginator 物件。
  4. 建立擴充類別的 DocumentPaginator 實例,此類別支援頁面範圍、傳入預設 DocumentPaginator 物件和 PageRangePrintDialog 傳回的結構。
  5. PrintDocument呼叫 方法,傳入擴充 DocumentPaginator 類別的實例,以將指定的頁面範圍傳送至指定的印表機。
/// <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

提示

雖然您可以使用 PrintDocument 方法來列印而不開啟列印對話方塊,但基於效能考慮,最好使用 AddJob 方法,或 其中一個 WriteWriteAsync 方法 XpsDocumentWriter 。 如需詳細資訊,請參閱 如何列印 XPS 檔案 列印檔案概觀

另請參閱