搭建Wpf框架(3) —— Wpf实现打印报表
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();
最后打印以更新到源码中,欢迎大家下载。