WPF的打印原理 实现打印页眉页脚和打印预览

简单但不平凡

于 2018-01-30 16:13:04 发布

2990
 收藏 5
分类专栏: wpf 文章标签: 打印 wpf
版权

wpf
专栏收录该内容
1 篇文章0 订阅
订阅专栏
做应用特别是企业应用和网站一般都少不了打印功能,打印也是项特别麻烦的事情,要实现打印功能根据不能的技术也有不同的实现方案。

在MFC中手动实现打印功能很麻烦,如果不需要打印预览功能,可以直接用office文件作为打印模板,在程序中直接打印文件,这样实现简单方便,这种方案对于经常需要更改模板内容的话,还是一个不错的选择。
在Winform中实现打印功能一般都是用水晶报表,现在微软不再带报表功能需要自己单独安装 CrystalReport组件包,具体实现和布局页面类似,先在报表设计器中把页眉页脚和内容字段添加好,然后根据自己的需求更新字段,实现打印,带有打印预览和数据导出。
在WPF中实现简单的打印功能要比上面的两种方式方便了不少。wpf中只要是继承了Visual的都可以直接打印出来,另外就是用流文档方式打印。   

1.直接打印

 <Grid>
        <Canvas x:Name="printArea">
            <Ellipse Canvas.Left="137" Canvas.Top="92" Height="100" 
                     Stroke="Black" Width="200">
                <Ellipse.Fill>
                    <LinearGradientBrush>
                        <GradientStop Color="#FFAD0FC7" Offset="0" />
                        <GradientStop Color="#FF3359AD" Offset="1" />
                    </LinearGradientBrush>
                </Ellipse.Fill>
            </Ellipse>
        </Canvas>
        <Button Content="Print" Click="Button_Click" Height="23" 
                Margin="195,268,190,20" />
    </Grid>
在按钮事件中
private void Button_Click(object sender, RoutedEventArgs e)
{
    PrintDialog dialog = new PrintDialog();
    if (dialog.ShowDialog() == true)
    { 
        dialog.PrintVisual(printArea, "Print Test"); 
    }
}

2.自定义打印
实现流程为 Flow Document -》XPS固定文档-》DocumentViewer。Flow Document是xaml文件但是vs不支持预览,所以需要放到一个容器中显示,这个容器就是DocumentViewer。
第一步:加载流文档和字段
 public FlowDocument LoadDocumentAndRender(string strTmplName, Object data, IDocumentRenderer renderer = null)
        {
            FlowDocument doc = (FlowDocument)Application.LoadComponent(new Uri(strTmplName, UriKind.RelativeOrAbsolute));
            doc.PagePadding = new Thickness(70);//可以修改页边距
            doc.DataContext = data;
            if (renderer != null)
            {
                renderer.Render(doc, data);
            }
            return doc;
        }

第二步:写入到xps
  public DocumentPaginator GetPaginator(FlowDocument doc)
        {
            bool? bPrintHeaderAndFooter = doc.Resources["PrintHeaderAndFooter"] as bool?;
            if (bPrintHeaderAndFooter == true)
            {
                return new PaginatorHeaderFooter(((IDocumentPaginatorSource)doc).DocumentPaginator);
            }
            else
            {
                return ((IDocumentPaginatorSource)doc).DocumentPaginator;
            }
        }
 
        public void LoadXps()
        {
            //构造一个基于内存的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(GetPaginator(m_doc));
 
            //获取这个基于内存的xps document的fixed document
            docViewer.Document = xpsDocument.GetFixedDocumentSequence();
 
            //关闭基于内存的xps document
            xpsDocument.Close();
        }

第三步:显示
  PrintPreviewWindow previewWnd = new PrintPreviewWindow("ReportDocument.xaml", his, new DocumentRenderer());
            previewWnd.Owner = this;
            previewWnd.ShowInTaskbar = false;
            previewWnd.ShowDialog();   

3.页眉和页脚
打印中的分页是特别复杂麻烦的,需要非常复杂的算法,自己手动是几乎不能实现的,所以要用到流文档自带的分页器DocumentPaginator,DocumentPaginator中有很多方法,可以通过重写这些方法实现自定义打印,像增加页眉页脚 打印范围页 获得打印机和选择纸张类型。其中很重要的一个方法就是GetPage.通过DrawingVisual把自定义的内容画出来再添加到ContainerVisual中实现的。
  public override DocumentPage GetPage(int pageNumber)
        {
            DocumentPage page = m_paginator.GetPage(pageNumber);
            ContainerVisual newpage = new ContainerVisual();
 
            页眉:公司名称
            DrawingVisual header = new DrawingVisual();
            using (DrawingContext ctx = header.RenderOpen())
            {
                FormattedText text = new FormattedText("XX材料研究院",
                    System.Globalization.CultureInfo.CurrentCulture, FlowDirection.LeftToRight,
                    new Typeface("Courier New"), 14, Brushes.Black);
                ctx.DrawText(text, new Point(page.ContentBox.Left, page.ContentBox.Top));
                ctx.DrawLine(new Pen(Brushes.Black, 0.5), new Point(page.ContentBox.Left, page.ContentBox.Top + 16), new Point(page.ContentBox.Right, page.ContentBox.Top + 16));
            }
 
            //页脚:第几页
            DrawingVisual footer = new DrawingVisual();
            using (DrawingContext ctx = footer.RenderOpen())
            {
                FormattedText text = new FormattedText("第" + (pageNumber + 1) + "页",
                    System.Globalization.CultureInfo.CurrentCulture, FlowDirection.RightToLeft,
                    new Typeface("Courier New"), 14, Brushes.Black);
                ctx.DrawText(text, new Point(page.ContentBox.Right, page.ContentBox.Bottom - 20));
            }
 
            //将原页面微略压缩(使用矩阵变换)
            ContainerVisual mainPage = new ContainerVisual();
            mainPage.Children.Add(page.Visual);
            mainPage.Transform = new MatrixTransform(1, 0, 0, 0.95, 0, 0.025 * page.ContentBox.Height);
 
            //在现页面中加入原页面,页眉和页脚
            newpage.Children.Add(mainPage);
            //newpage.Children.Add(header);
            newpage.Children.Add(footer);
 
            return new DocumentPage(newpage, page.Size, page.BleedBox, page.ContentBox);
        }
以上参考了博文:
http://www.cnblogs.com/guogangj/archive/2013/03/12/2955537.html
https://www.cnblogs.com/HelloMyWorld/p/4149969.html
————————————————
版权声明:本文为CSDN博主「简单但不平凡」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/c914620529/article/details/79205064/

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值