用WPF实现打印及打印预览
应该说,WPF极大地简化了我们的打印输出工作,想过去使用VC++做开发的时候,打印及预览可是一件极麻烦的事情,而现在我不会再使用C++来做Windows的桌面应用了——性价比实在太低。
WPF的打印功能是很强大而简便的,它甚至能够直接打印界面上的内容,包括各种控件的显示内容,例如你在界面上摆放了一个datagrid控件,画了一个五角星,或写了一段文字,都可以直接打印出来,这里有一篇文章很简单明了地说明了这个功能:
http://www.cnblogs.com/gnielee/archive/2010/07/02/wpf-print-sample.html
这种做法是非常直截了当的,但恐怕不是很适合我们一般的应用,我们更多的时候需要自适应纸张,表格输出,自动分页,还有分页预览……
自己设计分页是非常麻烦的事情,没做过的人恐怕没法理解为什么,我这里插点题外话提一下,为什么分页难做?那是因为:你不真正把文档打印出来,你就不知道到底要在什么地方分页。举个最简单的例子,就一大段文本,给你打印,你认为到第几个字要另开一页?你估算了一下:一行20个字,我的打印纸一共20行,所以到第400个字的时候分页。——Too simple,你没考虑一行的字数根本就是不确定的(字符非等宽),也没考虑回车换行所产生的空行,更没考虑字体大小,行间距等影响因素,另外还有单词自适应因素,最后还有纸张大小……Oh,my god,这简直没法做,是的,自己很难做的,一个比较笨但有效的方法是“模拟打印”,用二分法找到开始分页的那个点,我以前做过的一个手机看书软件就是这么干的,而真实的分页算法是很复杂的,所幸的是这次不需要我们来做了。下面是我写的一个demo。

这是打印预览效果:

代码并不多。设计的思路就是:文档模版(xaml)+数据(.net对象)=打印输出
文档模版可以单独创建,右击你的WPF工程,Add - New Item - Flow Document(WPF),Visual Studio并没有提供这个xaml的预览,这点不得不说是个缺陷,微软的理由是这种Flow Document的显示需要一个容器,单独的Flow Document(流文档)是没法预览的,你必须把它放在一个容器中才可以,流文档的容器有FlowDocumentScrollViewer,FlowDocumentPageViewer,FlowDocumentReader,另外还有DocumentViewer,这个只支持固定流文档(只读)。关于流文档及其打印方面的技术在《WPF编程宝典》一书中都有具体讲述,建议大家要详细了解的话先去阅读一下此书,下面主要是一些书中没有的内容。
打印预览,我们这次选择了DocumentViewer,因为它直接就带有很好的分页功能,我们只需要生成固定文档(XPS),然后交给它,它就能很好的将内容预览出来——太棒了。
现在我们大致看看这个流文档模版的内容:

<Table FontSize="16">
<Table.Columns>
<TableColumn Width="200"></TableColumn>
<TableColumn Width="600"></TableColumn>
</Table.Columns>
<TableRowGroup>
<TableRow>
<TableCell>
<Paragraph>
订单号
</Paragraph>
</TableCell>
<TableCell>
<Paragraph>
<Run Text="{Binding OrderNo}"></Run>
</Paragraph>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Paragraph>
客户名称
</Paragraph>
</TableCell>
<TableCell>
<Paragraph>
<Run Text="{Binding CustomerName}"></Run>
</Paragraph>
</TableCell>
</TableRow>
<!-- 省略一大段 -->
</TableRowGroup>
</Table>

我把多余的内容去掉了,现在注意看“<Run Text="{Binding OrderNo}"></Run>”这个地方,我将这个Run的Text属性绑定到DataContext的OrderNo去了,也就是说,它会根据数据的内容,渲染出不同的结果。
这里一切OK,但最大的问题来了:流文档的Table却不能跟UIElement的DataGrid控件那样能动态地根据数据的条目数渲染出相应的行!也就是说Table的行数是固定的,流文档上的对象是静态的,所以我们只能用后台代码来手工改变它了,这是相当不方便的地方……我定义了这么一个接口来做这种工作:
public interface IDocumentRenderer
{
void Render(FlowDocument doc, Object data);
}
创建一个对象,实现这个接口,然后根据data的内容,往doc里对应的地方插入行。
另外还需要特别说明的是代码中使用了一些BeginInvoke,也许大家不太了解那是什么意思,为什么需要这么麻烦?其实,那是因为你给Document的DataContext赋值的时候,Document的内容并不是马上改变的,不信你可以把我写的这些BeginInvoke改为直接调用,然后看看打印预览的文档内容,是不是哪些binding的地方还是空白的?所以需要一个“延后”调用。关于BeginInvoke的内容可以看我这篇blog:
http://www.cnblogs.com/guogangj/archive/2013/01/22/2870590.html
最后,主界面上的“直接打印”为了防止用户连续点击,需要在点了一下之后把它变灰,然后过几秒钟之后再把它变亮。
最后的最后:完整代码下载
用WPF实现打印及打印预览的更多相关文章
- Lodop中特殊符号¥打印设计和预览不同
Lodop中¥符号样式改变问题 Lodop中对超文本样式的解析,虽然说是按照调用的本机ie引擎,但是调用的ie版本可能不同,导致在ie下是一种样式,预览又是另一种样式.可能是有些样式没有具体设置,走的 ...
- JS Web打印,实现预览新样式
问题描述: JS实现Web打印,要求打印前一种样式,打印预览时新样式 问题解决: (1)设置打印时的css样式,设置打印前的css样式 注: 以上为print. ...
- asp.net调用Lodop实现页面打印或局部打印,可进行打印设置或预览
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="WebPrint.aspx.cs ...
- WPF使用AForge实现Webcam预览(二)
本文主要介绍如何让摄像头预览界面的宽高比始终在16:9. 首先我们需要修改一下上一篇随笔实现的UI界面,让Grid变成一个3*3的九宫格,预览界面位于正中间.Xaml示例代码如下: <Windo ...
- WPF使用AForge实现Webcam预览(一)
本文简略地介绍一下如果使用AForge来实现前置/后置摄像头的预览功能. 要使用AForge,就需要添加AForge NuGet相关包的引用,这些包依赖的其他包会自动安装. AForge.Contro ...
- css去掉使用bootstrap框架后打印网页时预览效果下的超链接
在我们写网页的时候,超链接是链接各个页面的桥梁,也是搜索引擎爬虫(spider)收录网站页面的关键,因此,在每个网页中会有许多的超链. 今天,一个同行妹妹在使用了bootstrap框架来搭建自己的网站 ...
- java原装代码完成pdf在线预览和pdf打印及下载
这是我在工作中,遇到这样需求,完成需求后,总结的成果,就当做是工作笔记,以免日后忘记,当然,能帮助到别人是最好的啦! 下面进入正题: 前提准备: 1. 项目中至少需要引入的jar包,注意版本: a) ...
- Lodop打印设计矩形重合预览线条变粗
LODOP中的打印设计是辅助进行开发的,实际打印效果应以预览为准,很多效果都是在设计界面显示不出来,或设计和预览界面有差异.例如add_print_text文本的字间距.行间距,旋转,还有允许标点溢出 ...
- Winform中使用FastReport实现自定义PDF打印预览
场景 Winform中使用FastReport实现简单的自定义PDF导出: https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/1009 ...
随机推荐
- generating permunation——全排列(算法汇总)
本文一共提供了4种全排列的方法,包括递归非字典序版本.递归字典序版本.标准库版本和BFS字典序版本,当然BFS非字典序实现相对于BFS字典序版本更加简洁,稍加修改即可. 说明:递归版本基于网上现有代码 ...
- Java NIO的基本概念与使用
public class TestBuffer { /** * 一. 缓冲区 (Buffer):Java Nio中负责数据的 存取+缓冲就是数组.用于存储不同类型的数据 * * 根据类型不同(bool ...
- java.sql.SQLException:Column count doesn't match value count at row 1
1.错误描写叙述 java.sql.SQLException:Column count doesn't match value count at row 1 2.错误原因 在插入数据时,插入的 ...
- AngularJS之基本指令(init、repeat)
<h3>ng-init初始化变量</h3> <div ng-init="name='aurora';age='18'"> <p ng-bi ...
- Uncaught SyntaxError: Unexpected end of input 解决办法
Unexpected end of input 的英文意思是"意外的终止输入" 他通常表示我们浏览器在读取我们的js代码时,碰到了不可预知的错误,导致浏览器 无语进行下面的读取 ...
- 【codeforces 765D】Artsem and Saunders
[题目链接]:http://codeforces.com/contest/765/problem/D [题意] 给你一个函数f(x); 要让你求2个函数g[x]和h[x],使得g[h[x]] = x对 ...
- 学maven
跟着刚哥深入学maven 前言:目前所有的项目都在使用maven,可是一直没有时间去整理学习,这两天正好有时间,好好的整理一下. 一.为什么使用Maven这样的构建工具[why] ① 一个项目就是 ...
- 小强的HTML5移动开发之路(21)—— PhoneGap
一.PhoneGap是什么 PhoneGap 是一个用基于 HTML,CSS 和 JavaScript 的,创建移动跨平台移动应用程序的快速开发框架.它使开发者能够利用 iPhone,Android, ...
- sql server中触发器
触发器是一种特殊类型的存储过程,它不同于之前的我们介绍的存储过程.触发器主要是通过事件进行触发被自动调用执行的.而存储过程可以通过存储过程的名称被调用. Ø 什么是触发器 触发器对表进行插入.更新.删 ...
- radio实现第一次点击选中第二次点击取消
Jquery代码如下: $("#add_form .radio input").bind("click",function(){ var $radio = $( ...