다음을 통해 공유


인쇄 대화 상자를 표시하는 방법(WPF .NET)

애플리케이션에서 인쇄하고 싶으신가요? PrintDialog 클래스를 사용하여 표준 Microsoft Windows 인쇄 대화 상자를 열 수 있습니다. 방법은 다음과 같습니다.

참고 항목

WPF에 사용되고 여기서 설명하는 System.Windows.Controls.PrintDialog 컨트롤은 Windows Forms의 System.Windows.Forms.PrintDialog 구성 요소와 혼동해서는 안 됩니다.

PrintDialog 클래스는 인쇄 구성 및 인쇄 작업 제출을 위한 단일 컨트롤을 제공합니다. 이 컨트롤은 사용하기 쉽고 XAML 태그 또는 코드를 사용하여 인스턴스화할 수 있습니다. 다음 예제는 코드를 사용하여 PrintDialog 인스턴스를 만들고 표시합니다.

인쇄 대화 상자를 사용하여 다음과 같은 인쇄 옵션을 구성할 수 있습니다.

  • 특정 페이지 범위만 인쇄합니다.
  • 컴퓨터에 설치된 프린터 중에서 선택합니다. Microsoft XPS Document Writer 옵션을 사용하여 다음과 같은 문서 유형을 만들 수 있습니다.
    • XPS(XML Paper Specification)
    • Open XML Paper Specification(OpenXPS)

이 예제는 XPS 문서의 모든 페이지를 인쇄합니다. 기본적으로 코드는 다음을 수행합니다.

  1. 사용자에게 프린터를 선택하고 인쇄 작업을 시작하라는 메시지를 표시하는 인쇄 대화 상자 창을 엽니다.
  2. XPS 문서의 콘텐츠를 사용하여 XpsDocument 개체를 인스턴스화합니다.
  3. XpsDocument 개체를 사용하여 XPS 문서의 모든 페이지를 포함하는 DocumentPaginator 개체를 생성합니다.
  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 개체와 PrintDialog에서 반환하는 PageRange 구조체를 전달합니다.
  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 메서드 또는 XpsDocumentWriter의 여러 WriteWriteAsync 메서드 중하나를 사용하는 것이 좋습니다. 이에 대한 자세한 내용은 XPS 파일을 인쇄하는 방법문서 인쇄 개요를 참조하세요.

참고 항목