现在的位置: 首页 > 综合 > 正文

用WPF打印页眉和页脚

2013年04月25日 ⁄ 综合 ⁄ 共 2752字 ⁄ 字号 评论关闭

在《用WPF实现打印和打印预览》一文中,我提供的程序有个问题,就是不能方便地打印页眉和页脚,而很多时候我们需要加入页眉也页脚,比如我们给客户打印一个订单列表,我们希望在输出的每页中加入公司名称和页码数,这个如何实现呢?

这个看起来似乎比较难:想知道要在什么地方插入页眉也页脚,我们就需要知道在哪里分页,而插入页眉和页脚势必会占用一些页面空间,使得打印页面变得比之前狭窄,分页点也会随之发生变化……这貌似是一个鸡生蛋,蛋生鸡的死循环。后来参考了一些国外的blog,终于找到了解决方案,大家先看看这是我的效果图:

页眉和页脚都有了。它的实现原理其实并不复杂,看下图就很清楚了:

我们当然是用WPF默认的流文档分页器来对我们要打印的内容进行分页(在前一篇我提到过自己实现一个分页算法几乎是Mission Impossible),然后把默认要打印的各个页面“压缩”一下,在上面腾出一点空间来给页眉,在下面腾出一点空间来给页脚。

我们在代码中要做的事情就是自行实现一个Paginator,取代FlowDocument默认的DocumentPaginator,override其GetPage方法,在这个方法里做点手脚,就是前面提到的把页面“压缩”一下,上插页眉,下插页脚。代码如下:

    public class PaginatorHeaderFooter : DocumentPaginator
    {
        readonly DocumentPaginator m_paginator;

        public PaginatorHeaderFooter(DocumentPaginator paginator)
        {
            m_paginator = paginator;
        }

        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("上海ABCD信息技术有限公司",
                    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);
        }

        //省略掉一些代码
    }

代码我想已经很清晰了,唯一需要说一下的是“压缩”原始页面所使用的那个MatrixTransform,其实学过计算机图形学的同学一定不会陌生,但对我这个非科班出身的人来说,一开始还有些迷惑,过去学线性代数的时候老师也从来不说那些知识是可以用于图形学上的图形变换(糟糕的中国教育啊……),这个3*3的矩阵可用于对2D图形的缩放,平移和旋转,具体可以参考这篇文章:点击打开

在之前的那个流文档模版里加入一个资源,True表示打印页眉和页脚,False表示不打印。

<FlowDocument xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
              ColumnWidth="400" FontSize="14" FontFamily="宋体"
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
              xmlns:core="clr-namespace:System;assembly=mscorlib" TextOptions.TextFormattingMode="Display">
    <FlowDocument.Resources>
        <core:Boolean x:Key="PrintHeaderAndFooter">True</core:Boolean>
    </FlowDocument.Resources>
    <!-- 省略 -->
</FlowDocument>

本来我还打算把页眉和页脚的内容整合到流文档模版里,(现在的做法是写死在Paginator中的)但貌似有些难度,这个任务你可以尝试一下。

代码下载:完整代码(VS2010)

抱歉!评论已关闭.