搭建Wpf框架(3) —— Wpf实现打印报表
Wpf流文档自带打印功能,那么使用MVVM轻松可以实现打印功能。
1.新建一个窗体,放置流文档的父容器
1 2 3 4 5 6 7 8 9 10 11 12 | <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的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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模式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | <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如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | <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代码中添加数据行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | 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();
最后打印以更新到源码中,欢迎大家下载。
分类:
Wpf / Wpf-Client
标签:
Wpf Client
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· AI与.NET技术实操系列(六):基于图像分类模型对图像进行分类