Wpf流文檔自帶打印功能,那么使用MVVM輕松可以實現打印功能。
1.新建一個窗體,放置流文檔的父容器
<Window x:Class="AIStudio.Wpf.BasePage.Views.PrintPreviewWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:AIStudio.Wpf.BasePage.Views" mc:Ignorable="d" Title="打印預覽窗口" Height="736" Width="882"> <Grid> <DocumentViewer Name="docViewer"></DocumentViewer> </Grid> </Window>
2.DocumentViewer加載FlowDocument的方法
public static void LoadXps(FlowDocument m_doc, DocumentViewer docViewer) { //構造一個基於內存的xps document MemoryStream ms = new MemoryStream(); Package package = Package.Open(ms, FileMode.Create, FileAccess.ReadWrite); Uri DocumentUri = new Uri("pack://InMemoryDocument.xps"); PackageStore.RemovePackage(DocumentUri); PackageStore.AddPackage(DocumentUri, package); XpsDocument xpsDocument = new XpsDocument(package, CompressionOption.Fast, DocumentUri.AbsoluteUri); //將flow document寫入基於內存的xps document中去 XpsDocumentWriter writer = XpsDocument.CreateXpsDocumentWriter(xpsDocument); writer.Write(((IDocumentPaginatorSource)m_doc).DocumentPaginator); //獲取這個基於內存的xps document的fixed document docViewer.Document = xpsDocument.GetFixedDocumentSequence(); //關閉基於內存的xps document xpsDocument.Close(); }
3.繪制自己要打印的頁面MVVM模式。
<FlowDocument xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" ColumnWidth="400" FontSize="14" FontFamily="宋體" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" TextOptions.TextFormattingMode="Display"> <Paragraph TextAlignment="Center" FontSize="16" FontWeight="Bold"> <Run Text="{Binding DefFormName}"></Run> </Paragraph> <Paragraph> <Run Text="因"></Run> <Run Text="{Binding Text}" TextDecorations="Underline"></Run> <Run Text=",本人想"></Run> <Run Text="{Binding SubType}"></Run> <Run Text="{Binding Flag}" TextDecorations="Underline"></Run> <Run Text="{Binding Unit}"></Run> <Run Text=",望領導批准!"></Run> </Paragraph> <Paragraph TextAlignment="Right"> <Run Text="謝謝!"></Run> </Paragraph> <Paragraph TextAlignment="Right"> <Run Text="申請人:"></Run> <Run Text="{Binding ApplicantUserId}" TextDecorations="Underline"></Run> </Paragraph> </FlowDocument>
4.如果需要打印表格,那么xaml如下:
<FlowDocument xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:local="clr-namespace:AIStudio.Wpf.Base_Manage.Views" ColumnWidth="400" FontSize="14" FontFamily="宋體" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" TextOptions.TextFormattingMode="Display"> <FlowDocument.Resources> <Style TargetType="Table" x:Key="BorderedTable"> <Setter Property="CellSpacing" Value="0"></Setter> <Setter Property="BorderThickness" Value="1"></Setter> <Setter Property="BorderBrush" Value="#000"></Setter> </Style> <Style TargetType="TableCell" x:Key="BorderedCell"> <Setter Property="BorderThickness" Value="0.5"></Setter> <Setter Property="BorderBrush" Value="#000"></Setter> <Setter Property="Padding" Value="3"></Setter> </Style> </FlowDocument.Resources> <Table Style="{StaticResource BorderedTable}"> <Table.Columns> <TableColumn Width="*"></TableColumn> <TableColumn Width="*"></TableColumn> <TableColumn Width="*"></TableColumn> <TableColumn Width="*"></TableColumn> <TableColumn Width="*"></TableColumn> <TableColumn Width="*"></TableColumn> </Table.Columns> <TableRowGroup Name="rowsDetails"> <TableRow FontWeight="Bold" > <TableCell Style="{StaticResource BorderedCell}"> <Paragraph>用戶名</Paragraph> </TableCell> <TableCell Style="{StaticResource BorderedCell}"> <Paragraph>姓名</Paragraph> </TableCell> <TableCell Style="{StaticResource BorderedCell}"> <Paragraph>性別</Paragraph> </TableCell> <TableCell Style="{StaticResource BorderedCell}"> <Paragraph>出生日期</Paragraph> </TableCell> <TableCell Style="{StaticResource BorderedCell}"> <Paragraph>所屬部門</Paragraph> </TableCell> <TableCell Style="{StaticResource BorderedCell}"> <Paragraph>所屬角色</Paragraph> </TableCell> </TableRow> </TableRowGroup> </Table> </FlowDocument>
這個時候還需要在cs代碼中添加數據行
class Base_UserDocumentRenderer : IDocumentRenderer { public void Render(FlowDocument doc, object data) { if (data is ObservableCollection<Base_UserDTO> items) { TableRowGroup groupDetails = doc.FindName("rowsDetails") as TableRowGroup; Style styleCell = doc.Resources["BorderedCell"] as Style; foreach (var item in items) { TableRow row = new TableRow(); TableCell cell = new TableCell(new Paragraph(new Run(item.UserName))); cell.Style = styleCell; row.Cells.Add(cell); cell = new TableCell(new Paragraph(new Run(item.RealName))); cell.Style = styleCell; row.Cells.Add(cell); cell = new TableCell(new Paragraph(new Run(item.SexText))); cell.Style = styleCell; row.Cells.Add(cell); cell = new TableCell(new Paragraph(new Run(item.Birthday?.ToString("yyyy-MM-dd")))); cell.Style = styleCell; row.Cells.Add(cell); cell = new TableCell(new Paragraph(new Run(item.DepartmentName))); cell.Style = styleCell; row.Cells.Add(cell); cell = new TableCell(new Paragraph(new Run(item.RoleNames))); cell.Style = styleCell; row.Cells.Add(cell); groupDetails.Rows.Add(row); } } } }
最后整理一下:
1.PrintHelper

using System; using System.Collections.Generic; using System.IO; using System.IO.Packaging; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Markup; using System.Windows.Xps; using System.Windows.Xps.Packaging; namespace AIStudio.Core.Helpers { public class PrintHelper { public delegate void LoadManyXpsMethod(List<FlowDocument> m_doclist, DocumentViewer docViewer); public delegate void LoadXpsMethod(FlowDocument m_doclist, DocumentViewer docViewer); public static void LoadXps(FlowDocument m_doc, DocumentViewer docViewer) { //構造一個基於內存的xps document MemoryStream ms = new MemoryStream(); Package package = Package.Open(ms, FileMode.Create, FileAccess.ReadWrite); Uri DocumentUri = new Uri("pack://InMemoryDocument.xps"); PackageStore.RemovePackage(DocumentUri); PackageStore.AddPackage(DocumentUri, package); XpsDocument xpsDocument = new XpsDocument(package, CompressionOption.Fast, DocumentUri.AbsoluteUri); //將flow document寫入基於內存的xps document中去 XpsDocumentWriter writer = XpsDocument.CreateXpsDocumentWriter(xpsDocument); writer.Write(((IDocumentPaginatorSource)m_doc).DocumentPaginator); //獲取這個基於內存的xps document的fixed document docViewer.Document = xpsDocument.GetFixedDocumentSequence(); //關閉基於內存的xps document xpsDocument.Close(); } public static void LoadManyXps(List<FlowDocument> m_doclist, DocumentViewer docViewer) { //------------------定義新文檔的結構 FixedDocumentSequence newFds = new FixedDocumentSequence();//創建一個新的文檔 for (int i = 0; i < m_doclist.Count; i++) { //構造一個基於內存的xps document MemoryStream ms = new MemoryStream(); Package package = Package.Open(ms, FileMode.Create, FileAccess.ReadWrite); Uri DocumentUri = new Uri("pack://InMemoryDocument" + i.ToString() + ".xps"); PackageStore.RemovePackage(DocumentUri); PackageStore.AddPackage(DocumentUri, package); XpsDocument xpsDocument = new XpsDocument(package, CompressionOption.Fast, DocumentUri.AbsoluteUri); //將flow document寫入基於內存的xps document中去 XpsDocumentWriter writer = XpsDocument.CreateXpsDocumentWriter(xpsDocument); writer.Write(((IDocumentPaginatorSource)m_doclist[i]).DocumentPaginator); DocumentReference newDocRef = AddPage(xpsDocument);//加入第一個文件 newFds.References.Add(newDocRef); //關閉基於內存的xps document xpsDocument.Close(); } string newFile = "xpsShow.xps"; File.Delete(newFile); //xps寫入新文件 XpsDocument NewXpsDocument = new XpsDocument("xpsShow.xps", System.IO.FileAccess.ReadWrite); XpsDocumentWriter xpsDocumentWriter = XpsDocument.CreateXpsDocumentWriter(NewXpsDocument); xpsDocumentWriter.Write(newFds); //獲取這個基於內存的xps document的fixed document docViewer.Document = NewXpsDocument.GetFixedDocumentSequence(); NewXpsDocument.Close(); } public static DocumentReference AddPage(XpsDocument xpsDocument) { DocumentReference newDocRef = new DocumentReference(); FixedDocument newFd = new FixedDocument(); FixedDocumentSequence docSeq = xpsDocument.GetFixedDocumentSequence(); foreach (DocumentReference docRef in docSeq.References) { FixedDocument fd = docRef.GetDocument(false); foreach (PageContent oldPC in fd.Pages) { Uri uSource = oldPC.Source;//讀取源地址 Uri uBase = (oldPC as IUriContext).BaseUri;//讀取目標頁面地址 PageContent newPageContent = new PageContent(); newPageContent.GetPageRoot(false); newPageContent.Source = uSource; (newPageContent as IUriContext).BaseUri = uBase; newFd.Pages.Add(newPageContent);//將新文檔追加到新的documentRefences中 } } newDocRef.SetDocument(newFd); xpsDocument.Close(); return newDocRef; } public static DocumentReference AddPage(string fileName) { DocumentReference newDocRef = new DocumentReference(); FixedDocument newFd = new FixedDocument(); XpsDocument xpsDocument = new XpsDocument(fileName, FileAccess.Read); FixedDocumentSequence docSeq = xpsDocument.GetFixedDocumentSequence(); foreach (DocumentReference docRef in docSeq.References) { FixedDocument fd = docRef.GetDocument(false); foreach (PageContent oldPC in fd.Pages) { Uri uSource = oldPC.Source;//讀取源地址 Uri uBase = (oldPC as IUriContext).BaseUri;//讀取目標頁面地址 PageContent newPageContent = new PageContent(); newPageContent.GetPageRoot(false); newPageContent.Source = uSource; (newPageContent as IUriContext).BaseUri = uBase; newFd.Pages.Add(newPageContent);//將新文檔追加到新的documentRefences中 } } newDocRef.SetDocument(newFd); xpsDocument.Close(); return newDocRef; } } }
2.PrintPreviewWindow.xaml

<Window x:Class="AIStudio.Wpf.BasePage.Views.PrintPreviewWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:AIStudio.Wpf.BasePage.Views" mc:Ignorable="d" Title="打印預覽窗口" Height="736" Width="882"> <Grid> <DocumentViewer Name="docViewer"></DocumentViewer> </Grid> </Window>
3.PrintPreviewWindow.xaml.cs

using AIStudio.Core.Helpers; using System; using System.Collections.Generic; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Shapes; using System.Windows.Threading; namespace AIStudio.Wpf.BasePage.Views { /// <summary> /// PrintPreviewWindow.xaml 的交互邏輯 /// </summary> public partial class PrintPreviewWindow : Window { public PrintPreviewWindow() { InitializeComponent(); } public static FlowDocument LoadDocumentAndRender(string strTmplName, Object data, IDocumentRenderer renderer = null) { FlowDocument doc = (FlowDocument)Application.LoadComponent(new Uri(strTmplName, UriKind.RelativeOrAbsolute)); doc.PagePadding = new Thickness(50); doc.DataContext = data; if (renderer != null) { renderer.Render(doc, data); } DocumentPaginator paginator = ((IDocumentPaginatorSource)doc).DocumentPaginator; paginator.PageSize = new Size(595, 842); //doc.PagePadding = new Thickness(50, 50, 50, 50); doc.ColumnWidth = double.PositiveInfinity; return doc; } public PrintPreviewWindow(string strTmplName, Object data, IDocumentRenderer renderer = null) : this() { if (data is System.Collections.IList resultList) { var m_doclist = new List<FlowDocument>(); foreach (var result in resultList) { m_doclist.Add(LoadDocumentAndRender(strTmplName, result, renderer)); } Dispatcher.BeginInvoke(new PrintHelper.LoadManyXpsMethod(PrintHelper.LoadManyXps), DispatcherPriority.ApplicationIdle, m_doclist, docViewer); } else { var m_doc = LoadDocumentAndRender(strTmplName, data, renderer); Dispatcher.BeginInvoke(new PrintHelper.LoadXpsMethod(PrintHelper.LoadXps), DispatcherPriority.ApplicationIdle, m_doc, docViewer); } } public PrintPreviewWindow(string strTmplName, System.Collections.IList data, IDocumentRenderer renderer = null) : this() { var m_doc = LoadDocumentAndRender(strTmplName, data, renderer); Dispatcher.BeginInvoke(new PrintHelper.LoadXpsMethod(PrintHelper.LoadXps), DispatcherPriority.ApplicationIdle, m_doc, docViewer); } } }
4.Base_UserDocumentRenderer.cs

using AIStudio.Core.Helpers; using AIStudio.Wpf.Business.DTOModels; using System.Collections.ObjectModel; using System.Windows; using System.Windows.Documents; namespace AIStudio.Wpf.Base_Manage.ViewModels { class Base_UserDocumentRenderer : IDocumentRenderer { public void Render(FlowDocument doc, object data) { if (data is ObservableCollection<Base_UserDTO> items) { TableRowGroup groupDetails = doc.FindName("rowsDetails") as TableRowGroup; Style styleCell = doc.Resources["BorderedCell"] as Style; foreach (var item in items) { TableRow row = new TableRow(); TableCell cell = new TableCell(new Paragraph(new Run(item.UserName))); cell.Style = styleCell; row.Cells.Add(cell); cell = new TableCell(new Paragraph(new Run(item.RealName))); cell.Style = styleCell; row.Cells.Add(cell); cell = new TableCell(new Paragraph(new Run(item.SexText))); cell.Style = styleCell; row.Cells.Add(cell); cell = new TableCell(new Paragraph(new Run(item.Birthday?.ToString("yyyy-MM-dd")))); cell.Style = styleCell; row.Cells.Add(cell); cell = new TableCell(new Paragraph(new Run(item.DepartmentName))); cell.Style = styleCell; row.Cells.Add(cell); cell = new TableCell(new Paragraph(new Run(item.RoleNames))); cell.Style = styleCell; row.Cells.Add(cell); groupDetails.Rows.Add(row); } } } } }
5.Base_UserFlowDocument.xaml

<FlowDocument xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:local="clr-namespace:AIStudio.Wpf.Base_Manage.Views" ColumnWidth="400" FontSize="14" FontFamily="宋體" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" TextOptions.TextFormattingMode="Display"> <FlowDocument.Resources> <Style TargetType="Table" x:Key="BorderedTable"> <Setter Property="CellSpacing" Value="0"></Setter> <Setter Property="BorderThickness" Value="1"></Setter> <Setter Property="BorderBrush" Value="#000"></Setter> </Style> <Style TargetType="TableCell" x:Key="BorderedCell"> <Setter Property="BorderThickness" Value="0.5"></Setter> <Setter Property="BorderBrush" Value="#000"></Setter> <Setter Property="Padding" Value="3"></Setter> </Style> </FlowDocument.Resources> <Table Style="{StaticResource BorderedTable}"> <Table.Columns> <TableColumn Width="*"></TableColumn> <TableColumn Width="*"></TableColumn> <TableColumn Width="*"></TableColumn> <TableColumn Width="*"></TableColumn> <TableColumn Width="*"></TableColumn> <TableColumn Width="*"></TableColumn> </Table.Columns> <TableRowGroup Name="rowsDetails"> <TableRow FontWeight="Bold" > <TableCell Style="{StaticResource BorderedCell}"> <Paragraph>用戶名</Paragraph> </TableCell> <TableCell Style="{StaticResource BorderedCell}"> <Paragraph>姓名</Paragraph> </TableCell> <TableCell Style="{StaticResource BorderedCell}"> <Paragraph>性別</Paragraph> </TableCell> <TableCell Style="{StaticResource BorderedCell}"> <Paragraph>出生日期</Paragraph> </TableCell> <TableCell Style="{StaticResource BorderedCell}"> <Paragraph>所屬部門</Paragraph> </TableCell> <TableCell Style="{StaticResource BorderedCell}"> <Paragraph>所屬角色</Paragraph> </TableCell> </TableRow> </TableRowGroup> </Table> </FlowDocument>
6.使用的地方
PrintPreviewWindow previewWnd = new PrintPreviewWindow("/AIStudio.Wpf.Base_Manage;component/Views/Base_UserFlowDocument.xaml", Data, new Base_UserDocumentRenderer()); previewWnd.ShowDialog();
最后打印以更新到源碼中,歡迎大家下載。