在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对象,最后遍历所有控件执行打印操作。

posted @ 2021-09-14 11:56  xhubobo  阅读(1330)  评论(0编辑  收藏  举报