在《用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)