如何顯示列印對話框 (WPF .NET)
想要從您的應用程式列印嗎? 您可以使用 PrintDialog 類別來開啟標準的 Microsoft Windows 列印對話框。 方法如下。
注意
用於 WPF 並於此處討論的 System.Windows.Controls.PrintDialog 控制項,不得與 Windows Forms 的 System.Windows.Forms.PrintDialog 元件混淆。
PrintDialog 類別提供單一個控制項,以進行列印設定和列印工作提交。 控制項易於使用,且可使用 XAML 標記或程式碼來進行實例化。 下列範例使用程式碼來建立和顯示 PrintDialog
實例。
您可以使用列印對話框來設定列印選項,例如:
- 只列印特定頁面範圍。
- 從電腦上安裝的印表機中選取。 您可以使用 [Microsoft XPS Document Writer] 選項來建立下列檔案類型:
- XML 文件規格 (XPS)
- Open XML 文件規格 (OpenXPS)
列印整份文件
此範例會列印 XPS 文件的所有頁面。 根據預設,程式碼會:
- 開啟列印對話框視窗,其中提示使用者選取印表機並啟動列印工作。
- 使用 XPS 文件的內容來實例化 XpsDocument 物件。
- 使用
XpsDocument
物件來產生保存 XPS 文件所有頁面的 DocumentPaginator 物件。 - 呼叫 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 類別,以新增對頁面範圍的支援。 根據預設,程式碼會:
- 開啟列印對話框視窗,其中提示使用者選取印表機,指定頁面範圍並啟動列印工作。
- 使用 XPS 文件的內容來實例化 XpsDocument 物件。
- 使用
XpsDocument
物件來產生保存 XPS 文件所有頁面的預設 DocumentPaginator 物件。 - 建立支援頁面範圍的已擴充
DocumentPaginator
類別實例,傳入預設的DocumentPaginator
物件和 PrintDialog 所傳回的 PageRange 結構。 - 呼叫 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 的其中一個 Write 和 WriteAsync 方法。 如需詳細資訊,請參閱如何列印 XPS 檔案和列印文件概觀。