您的位置:首页 > 其它

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

2018-01-30 16:13 218 查看
做应用特别是企业应用和网站一般都少不了打印功能,打印也是项特别麻烦的事情,要实现打印功能根据不能的技术也有不同的实现方案。

在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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  打印 wpf