搭建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();
最后打印以更新到源码中,欢迎大家下载。
                    
                
                
            
        
浙公网安备 33010602011771号