zoukankan      html  css  js  c++  java
  • 搭建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;
            }
        }
    }
    View Code

    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>
    View Code

    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);
            }
        }
    }
    View Code

    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);
                    }
                }
            }
        }
    }
    View Code

    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>
    View Code

    6.使用的地方

     PrintPreviewWindow previewWnd = new PrintPreviewWindow("/AIStudio.Wpf.Base_Manage;component/Views/Base_UserFlowDocument.xaml", Data, new Base_UserDocumentRenderer());
                previewWnd.ShowDialog();

    最后打印以更新到源码中,欢迎大家下载。

    作者:竹天笑
    互相学习,提高自己。
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.
  • 相关阅读:
    Java Web Action DAO Service层次理解
    JSP/Servlet Web 学习笔记 DaySix —— EL表达式
    JSP/Servlet Web 学习笔记 DayFive
    JSP/Servlet Web 学习笔记 DayFour —— 实现一个简单的JSP/Servlet交互
    JSP/Servlet Web 学习笔记 DayFour
    如何快速创建百万级测试数据
    公司线上虚拟机大量GC导致STW和CPU飙升--抽丝剥茧定位的过程
    这10道springboot常见面试题你需要了解下
    这是一篇来源于阿里内部技术论坛的文章
    一文彻底弄懂如何选择抽象类还是接口
  • 原文地址:https://www.cnblogs.com/akwkevin/p/14560284.html
Copyright © 2011-2022 走看看