原文:用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实现打印及打印预览的更多相关文章

  1. Lodop中特殊符号¥打印设计和预览不同

    Lodop中¥符号样式改变问题 Lodop中对超文本样式的解析,虽然说是按照调用的本机ie引擎,但是调用的ie版本可能不同,导致在ie下是一种样式,预览又是另一种样式.可能是有些样式没有具体设置,走的 ...

  2. JS Web打印,实现预览新样式

    问题描述:     JS实现Web打印,要求打印前一种样式,打印预览时新样式 问题解决:         (1)设置打印时的css样式,设置打印前的css样式 注:         以上为print. ...

  3. asp.net调用Lodop实现页面打印或局部打印,可进行打印设置或预览

    <%@ Page Language="C#" AutoEventWireup="true" CodeFile="WebPrint.aspx.cs ...

  4. WPF使用AForge实现Webcam预览(二)

    本文主要介绍如何让摄像头预览界面的宽高比始终在16:9. 首先我们需要修改一下上一篇随笔实现的UI界面,让Grid变成一个3*3的九宫格,预览界面位于正中间.Xaml示例代码如下: <Windo ...

  5. WPF使用AForge实现Webcam预览(一)

    本文简略地介绍一下如果使用AForge来实现前置/后置摄像头的预览功能. 要使用AForge,就需要添加AForge NuGet相关包的引用,这些包依赖的其他包会自动安装. AForge.Contro ...

  6. css去掉使用bootstrap框架后打印网页时预览效果下的超链接

    在我们写网页的时候,超链接是链接各个页面的桥梁,也是搜索引擎爬虫(spider)收录网站页面的关键,因此,在每个网页中会有许多的超链. 今天,一个同行妹妹在使用了bootstrap框架来搭建自己的网站 ...

  7. java原装代码完成pdf在线预览和pdf打印及下载

    这是我在工作中,遇到这样需求,完成需求后,总结的成果,就当做是工作笔记,以免日后忘记,当然,能帮助到别人是最好的啦! 下面进入正题: 前提准备: 1. 项目中至少需要引入的jar包,注意版本: a)  ...

  8. Lodop打印设计矩形重合预览线条变粗

    LODOP中的打印设计是辅助进行开发的,实际打印效果应以预览为准,很多效果都是在设计界面显示不出来,或设计和预览界面有差异.例如add_print_text文本的字间距.行间距,旋转,还有允许标点溢出 ...

  9. Winform中使用FastReport实现自定义PDF打印预览

    场景 Winform中使用FastReport实现简单的自定义PDF导出: https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/1009 ...

随机推荐

  1. POJ 2823 Sliding Window 线段树

    http://poj.org/problem?id=2823 出太阳啦~^ ^被子拿去晒了~晚上还要数学建模,刚才躺在床上休息一下就睡着了,哼,还好我强大,没有感冒. 话说今年校运会怎么没下雨!!!说 ...

  2. POJ 3211 Washing Clothes 0-1背包

    题目大意: xxx很懒,但他有个漂亮又勤奋的女友 (尼玛能不能不刺激我,刚看到这题的时候发现自己的衣服没洗!!!) 可以帮他洗衣服. 洗衣服的时候要求不同的颜色的衣服不能同时洗.一人洗一件的话,问最短 ...

  3. [React] Setup 'beforeunload' listener

    In this lesson we'll show how to take a beforeUnload call and convert it to a declarative React Comp ...

  4. Cocos2d-x使用Javascript开发js绑定C++&lt;代码演示样例&gt;

    class IOSiAPDelegate{ public: virtual ~IOSiAPDelegate() {} }; class IOSAlipay{ public: IOSAlipay(); ...

  5. 关于spyder的一些用法

    目前还不会用spyder,感觉不习惯,也没怎么用MATLAB 能记住几点算几点吧 1,双击程序左侧栏,加断点 1,按住Ctrl,点击函数,进入函数

  6. angular之Http服务

    原文 https://www.jianshu.com/p/53e4a4bfad7d 大纲 1.什么是angular服务 2.服务的类别 3.认识angular的Http请求 4.简单实例 5.angu ...

  7. HDU 1210 Eddy's 洗牌问题(foj1062) || FOJ1050 Number lengths水

    麻痹,感冒了. ------------------------------------------------感冒了的分割线------------------------------------- ...

  8. event.relatedTarget、event.fromElement、event.toElement

    在标准DOM中,mouseover和mouseout所发生的元素可以通过event.target来访问,相关元素通过event.relatedTarget属性来访问.event.relatedTarg ...

  9. Linux环境下Apache ActiveMQ 基本安装

    原文链接:https://www.jianshu.com/p/1c017088aa95 在linux上安装mq,并映射到外网.1.Apache ActiveMQ安装基本条件请参考链接:2.下载Apac ...

  10. 怎样用O2O去改变充满谎言、疑虑和愤慨的维修行业

    为什么千亿级的维修服务市场出不了行业巨头?   据相关统计,我国的整个维修服务市场规模可达每年数千亿元之巨(当中仅家电维修就可达近千亿规模,更遑论手机.数码.家具等维修). 相同是千亿级规模的服务行业 ...