吵吵   2016-05-05  阅读:1,175

在delphi或者c#时代,我们做打印程序就像是在控制一个画笔,把每个内容都绘制出来,于是乎你要精确的去计算字体的大小、输出内容左右的距离等等,恩确实是个烦心的事情。

WPF的渲染机制让WPF的打印变的相当方便,渲染引擎在控件绘制中是咋样的,打印机上就是怎么画的。

我们先看简单的控件打印的代码:

PrintDialog printDialog = new PrintDialog();
            if (printDialog.ShowDialog() == true)
            {
                printDialog.PrintVisual(Mainwindow, "123");
            }

我们看到,一个PrintVisual函数就完成了窗口或者说控件的打印,非常的简洁和方便。

但是当我们打印的是一个有滚动条的控件呢?例如DataGrid或者ListView,如果依旧用printVisual打印出来的就只是当前的一页而已。

这个问题如何解决,在网上找了半天的解决办法,终于搞明白了。

1、第一步是要撑开控件,让控件的实际高度ActuralHeight变成一个大于一页纸高度的数值。如果只是单纯的用datagrid,它实际的高度还是很小。

因此我们需要先用scrollviewer把它给包括起来。因为scrollviewer是可以被撑开的,所以里面的元素就可以完整的布满了,比如里面放一个StackPanel或者是没有滚动条的DataGrid,那么stackPanel的实际高度就会很大。

2、撑开控件之后,利用控件的高度除以打印页面的高度,就可以计算出来打印的页数了。但是我们还是有个问题没有解决,怎么告诉打印机应该打印哪一页呢?

答案是DocumentPaginator,可以理解为一个分页打印的容器,这个类至少要告诉打印机一共要打印多少页,每页打印的内容是什么?

每页打印的内容是由public override DocumentPage GetPage(int pageNumber)重载函数完成的。

而交给打印机打印的内容叫做DocumentPage。

DocumentPage的创建是需要传入一个Visual控件的,例如:
var page = new DocumentPage(FrameworkElement element);

于是乎就有一个问题来了,我打印的控件是同一页啊,即便我告诉打印机我要打印3页,但是每次传入的控件都是同一个stackpanel,那打印出来的东西不就是一模一样?

3、RenderTransform就是解决这个问题的关键之处了。

我们可以传入一个相同的控件,但是我们可以对它渲染引擎进行变幻,比如高度3000的stackpanel,我们可以让它从高度1000的地方开始展示,因此实际上就实现了分页的功能了。

RenderTransform有多种变幻的方式,比如放大缩小,比如旋转,比如进行平移等等。

如果既要缩小,又要平移怎么办?

用TransformGroup解决。

 public class ProgramPaginator : DocumentPaginator
    {
        private FrameworkElement element;
        private Size printerSize;
        private Size scaleControlSize;

        public ProgramPaginator(FrameworkElement element,Size printerSize) { 
            this.element = element;
            this.printerSize = printerSize;
            //根据宽度进行缩放
            double scaleX = printerSize.Width / element.ActualWidth;
            double scaleY = scaleX;
            this.scaleControlSize = new Size(element.ActualWidth*scaleX,element.ActualHeight*scaleY); 
             

        }

        public override DocumentPage GetPage(int pageNumber)
        {
            TransformGroup tfg = new TransformGroup();

            //缩放变幻
            double scaleX =printerSize.Width/ element.ActualWidth ;
            
            ScaleTransform stf=new ScaleTransform(scaleX,scaleX);
            tfg.Children.Add(stf);

            //平移变幻
            TranslateTransform ttf=new TranslateTransform(0, -PageSize.Height * (pageNumber ));
            tfg.Children.Add(ttf);
           
            element.RenderTransform = tfg;


            
            Size elementSize =new Size(element.ActualWidth, element.ActualHeight);
            element.Measure(elementSize);
            element.Arrange(new Rect(new Point(0, 0), elementSize));

            var page = new DocumentPage(element);
            element.RenderTransform = null;

            return page;
        }

        public override bool IsPageCountValid { get { return true; } }

        

      

        public override int PageCount
        {
            get { return (int)Math.Ceiling(this.scaleControlSize.Height / PageSize.Height); }
        }

        public override Size PageSize
        {
            get { return printerSize; }
            set {printerSize = value; }
        }

        public override IDocumentPaginatorSource Source
        {
            get { return null; }
        }
    }

以上代码就是对控件进行了打印宽度的缩放之后实现的分页打印。

相信聪明如你一定能够看得懂。

但是问题又来了,分页打印是按照图像来进行分割,如果内容刚好在分割线上,那就是前一页打印一半,后一页打印一半,就很不好看了!

肿么办?

吵吵微信朋友圈,请付款实名加入:

吵吵 吵吵

发表评论

电子邮件地址不会被公开。 必填项已用*标注