在WPF中打印A4纸
本文使用PrintDialog. PrintVisual方法进行打印,参考“在WPF中使用PrintDialog.PrintVisual方法进行打印”。
1、A4纸的分辨率
本节文字摘录于“打印A4纸图片需要多少像素和分辨率?”,算是相关介绍里比较早的一篇文章了。
一般来说,给铜版纸使用的300dpi就够用了,太小打印出来不一定清晰,太大的话耗费内存和CPU处理起来十分慢,所以300dpi是一个很好的分界线。
A4物理大小是21厘米×29.7厘米。
具体换算成像素还要看用的多大的分辨率:
分辨率是72像素/英寸时,A4纸的尺寸的图像的像素是595×842;
分辨率是96像素/英寸时,A4纸的尺寸的图像的像素是794×1123;
分辨率是120像素/英寸时,A4纸的尺寸的图像的像素是1487×2105;
分辨率是150像素/英寸时,A4纸的尺寸的图像的像素是1240×1754;
分辨率是300像素/英寸时,A4纸的尺寸的图像的像素是2480×3508;
其他的大小,一般标准印刷300dpi时:
A4纸的尺寸的图像的像素是2480×3508;
A3纸的尺寸的图像的像素是4960×3508;
B3纸的尺寸的图像的像素是3248×4300;
B4纸的尺寸的图像的像素是3248×2150。
2、打印分辨率
在WPF中打印A4纸时,由于一般电脑不缩放时的分辨率为96dpi,假设打印机使用的分辨率是300dpi,1 英寸=2.54厘米,因此对应的控件像素是:21 / 2.54 * 96 = 793.7,29.7 / 2.54 * 96 = 1122.52。
3、打印控件
在WPF中,将需要打印的控件宽高分别设置为793.7、1122.52,然后在此控件上添加布局,并进行数据绑定,将其作为参数传递给PrintDialog. PrintVisual方法,即可打印出A4大小的纸张。
4、连续打印
对于数据比较多的情况,需要进行连续打印。可以将打印控件放在容器中(比如StackPanel),然后使用ScrollViewer进行预览。这时候如果直接将StackPanel中的打印控件传递给PrintDialog. PrintVisual方法,则会从第2张开始打印空白页。很可能是由于打印是异步的,要打印的控件没有及时刷新,或者刷新后又会被StackPanel重新排列,导致没有显示的控件打印不出来。
无论上述猜想正确与否,使用控件构建DrawingVisual对象一定能够打印出控件的内容,代码如下所示。
//通过控件构建DrawingVisual private static DrawingVisual GetDrawingVisual(FrameworkElement element, int dpi = 96) { //强制刷新控件 var size = element.ActualWidth > 0 ? new Size(element.ActualWidth, element.ActualHeight) : new Size(element.Width, element.Height); element.Measure(size); element.Arrange(new Rect(new Point(0, 0), size)); //构造内存渲染图像 var bitmap = new RenderTargetBitmap( (int) size.Width, //width (int) size.Height, //height dpi, //dpi x dpi, //dpi y PixelFormats.Pbgra32 // pixelformat ); bitmap.Render(element); //构建DrawingVisual var drawingVisual = new DrawingVisual(); using (var drawingContext = drawingVisual.RenderOpen()) { drawingContext.DrawImage(bitmap.Clone(), new Rect {Width = size.Width, Height = size.Height}); } return drawingVisual; }
需要注意的是,上述方法将打印控件通过图片的形式封装为DrawingVisual对象,本质上是对图片的打印,因此打印时候会比较模糊。通过对控件遍历的改进,可以通过直接打印控件,使得打印质量得到保证。
for (var i = 0; i < StackPanelMain.Children.Count; i++) { if (StackPanelMain.Children[i] is PrintControl printControl) { //移除控件 StackPanelMain.Children.RemoveAt(i--); //强制刷新控件 var size = printControl.ActualWidth > 0 ? new Size(printControl.ActualWidth, printControl.ActualHeight) : new Size(printControl.Width, printControl.Height); printControl.Measure(size); printControl.Arrange(new Rect(new Point(0, 0), size)); //通知打印 PrintEvent?.Invoke(printControl); } }
打印方法也需要做细微调整。
/// <summary> /// 打印 /// </summary> /// <param name="visual">要打印的元素</param> /// <param name="printer">打印机名称</param> /// <param name="description">打印描述</param> /// <param name="copyCount">打印个数</param> public void Print(Visual visual, string printer, string description, int copyCount = 1) { var localPrintServer = new LocalPrintServer(); var printQueue = localPrintServer.GetPrintQueue(printer); if (printQueue.IsInError) { throw new Exception("打印机处于错误状态"); } var printDialog = new PrintDialog { PrintQueue = printQueue, //打印队列 PrintTicket = {CopyCount = copyCount} //打印个数 }; //设置纸张大小 var pageWidth = (int) Math.Ceiling(printDialog.PrintableAreaWidth); var pageHeight = (int) Math.Ceiling(printDialog.PrintableAreaHeight); printDialog.PrintTicket.PageMediaSize = new PageMediaSize(pageWidth, pageHeight); //打印 printDialog.PrintVisual(visual, description); }
5、总结
使用PrintDialog. PrintVisual进行A4纸打印时,首先需要设置打印控件的宽高,然后通过打印数据动态创建打印控件(可能有多个),之后从打印控件构建DrawingVisual对象,最后遍历所有控件执行打印操作。