WPF开发之调用打印机打印文件

前言

本文主要是关于PDF文件的打印。

这里大体分为三种方案

  • 调用系统支持文件所对应的软件打印。

    这种方式可以支持所有可打印的文件,只要安装打开对应文件的软件即可。

    当软件需要支持多种文件打印的时候使用。

  • 使用特定的软件打印。

    这种方式只能打印特定格式,不会出现同样的文件打开方式被更改而导致打印效果不好的情况。

    当打印格式固定,但是要求效果精细的时候使用。

  • 使用DLL打印。

    这种方式在打印普通文件没问题,但是打印点阵的时候,因为内部会把点阵转为图片,所以会占用很大的内存,效果也不好。

    打印格式固定,文件效果不要求特别精细,但是不需要用户设置打印属性,自动打印的时候使用。

    Aspose.PDF、Spire.Pdf、PDFRender4NET都是这种形式,优缺点也都一样。

调用打印命令打印

这种方法会使用文件在系统中默认的打开工具打开进行打印。

这种方式

缺点在于

  1. 需要下载PDF阅读器,并且将其设置文件的默认打开方式。
  2. 必须设置默认的打印机(可以代码中设置)。
  3. 不能同时打印多份。
  4. 没有设置默认PDF的打开软件的时候会报错。

优点

  1. 不用安装额外的库。
  2. 如果打印的精度较高的情况下,调用其他库打印的效果没有这种调用专门软件打印的效果好。

工具类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
namespace z_pdf_printer.Utils
{
using System.Collections.Generic;
using System.Drawing.Printing;
using System.Runtime.InteropServices;

class ZPrintUtils
{
private static readonly PrintDocument FPrintDocument = new PrintDocument();

/// <summary>
/// 获取本机默认打印机名称
/// </summary>
/// <returns></returns>
public static string DefaultPrinter()
{
return FPrintDocument.PrinterSettings.PrinterName;
}

/// <summary>
/// 获取打印机列表
/// </summary>
/// <returns></returns>
public static List<string> GetLocalPrinters()
{
List<string> fPrinters = new List<string>();
fPrinters.Add(DefaultPrinter()); //默认打印机始终出现在列表的第一项
foreach (string fPrinterName in PrinterSettings.InstalledPrinters)
{
if (!fPrinters.Contains(fPrinterName))
{
fPrinters.Add(fPrinterName);
}
}
return fPrinters;
}

/// <summary>
/// 调用win api将指定名称的打印机设置为默认打印机
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
[DllImport("winspool.drv")]
public static extern bool SetDefaultPrinter(string name);

/// <summary>
/// 打印
/// </summary>
/// <param name="pdfPath">打印文件的路径</param>
/// <param name="printerName">打印机的名称</param>
public static void Print
(
string pdfPath,
string printerName
)
{
//设置默认打印机
SetDefaultPrinter(printerName);
System.Diagnostics.Process p = new System.Diagnostics.Process();
//不能启动进程是否显示错误弹窗,如果文件没有打开方式会提示选择文件的打开方式
p.StartInfo.ErrorDialog = true;
//不显示调用程序窗口,但是对于某些应用无效
p.StartInfo.CreateNoWindow = true;
p.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
//采用操作系统自动识别的模式
p.StartInfo.UseShellExecute = true;
//要打印的文件路径
p.StartInfo.FileName = pdfPath;
//指定执行的动作,是打印,即print,打开是 open
p.StartInfo.Verb = "print";
//开始打印
p.Start();
//等待15秒
p.WaitForExit(15000);
}
}
}

下载PDF查看软件

系统默认浏览器下载

1
2
string fileurl="https://xhkjedu.oss-cn-huhehaote.aliyuncs.com/runtime/AcroRdrDC1900820071_zh_CN.exe";
System.Diagnostics.Process.Start("explorer.exe", fileurl);

IE下载

1
2
string fileurl="https://xhkjedu.oss-cn-huhehaote.aliyuncs.com/runtime/readerdc_cn_ha_crd_install.exe";
System.Diagnostics.Process.Start("iexplore.exe", fileurl);

这里上传了两个安装包地址如下

在线安装包地址

https://xhkjedu.oss-cn-huhehaote.aliyuncs.com/runtime/readerdc_cn_ha_crd_install.exe

离线安装包地址

https://xhkjedu.oss-cn-huhehaote.aliyuncs.com/runtime/AcroRdrDC1900820071_zh_CN.exe

调用AcroRdrDC打印

上面的方式有个弊端,就是我们设置的默认软件可能被其他软件更换,就导致高精度打印的时候,使用的并不是我们设置的软件。

工具类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/// <summary>
/// 是否安装AcroRd32
/// </summary>
/// <returns></returns>
public static bool IsExistAcroRd32()
{
RegistryKey key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\AcroRd32.exe");
return key != null;
}

public static bool Print(string pdfPath, string printerName)
{
try
{
RegistryKey key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\AcroRd32.exe");
if (key != null)
{
string acroRd32Path = key.GetValue("").ToString();
string args = string.Format
(
"/t \"{0}\" \"{1}\" /p",
pdfPath,
printerName
);
Process.Start(acroRd32Path, args);
return true;
}
return false;
}
catch
{
// ignored
}
return false;
}

自动下载安装和重启

这种方式能自动下载AcroRd32并安装,但是部分软件会拦截应用自动安装软件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
/// <summary>
/// 安装并重启
/// </summary>
/// <returns></returns>
public static async void InstallAcroRd32Async()
{
// 解决下载文件报错:请求被中止: 未能创建 SSL/TLS 安全通道
ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072;
var webClient = new WebClient();
bool isDownload = false;
string downloadUrl = "https://xhkjedu.oss-cn-huhehaote.aliyuncs.com/runtime/AcroRdrDC1900820071_zh_CN.exe";
string desktopPath = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
string acroRd32Setup = System.IO.Path.Combine(desktopPath, "AcroRd32Install.exe");
try
{
webClient.DownloadFileCompleted += (s, e) =>
{
if (e.Error != null)
{
Console.WriteLine(@"下载失败:" + e.Error.Message);
}
else if (e.Cancelled)
{
Console.WriteLine(@"下载取消");
}
else
{
Console.WriteLine(@"下载成功");
isDownload = true;
}
};
await webClient.DownloadFileTaskAsync(downloadUrl, acroRd32Setup);
}
catch (Exception)
{
// ignored
}
if (!isDownload)
{
return;
}
await Task.Delay(300);
await Task.Run
(
() =>
{
Process.Start(acroRd32Setup, " /install")?.WaitForExit();
}
);
if (!IsExistAcroRd32())
{
return;
}
Process.Start(Application.ResourceAssembly.Location);
Application.Current.Shutdown();
}

只下载

这种方式只下载,让用户安装。

1
2
3
4
5
public static void InstallAcroRd32ByUser()
{
string downloadUrl = "https://xhkjedu.oss-cn-huhehaote.aliyuncs.com/runtime/AcroRdrDC1900820071_zh_CN.exe";
Process.Start(downloadUrl);
}

命令行

命令行文档

https://www.robvanderwoude.com/commandlineswitches.php#Acrobat

关闭GUI

1
taskkill /IM "acrord32.exe" /F

打印命令

1
AcroRd32.exe /t "C:\Path\To\Your\File.pdf" "PrinterName"

所有参数

以下是AcroRd32.exe打印的所有参数:

1
acrord32.exe /t path printername drivername monitorname [options]

其中:

  • path:要打印的文件的路径。

  • printername:指定要使用的打印机的名称。

  • drivername:指定要使用的驱动程序的名称。

  • monitorname:指定所使用的端口监视器的名称。

  • options:可选参数,用于设置打印作业的其他选项。可以使用以下任意一个选项:

    • /h:隐藏Acrobat Reader窗口。

    • /n:禁止Acrobat Reader显示打印对话框。

    • /s:打印作业无需确认。

    • /o:打印作业完成后退出Acrobat Reader。

    • /p:打印作业完成后立即退出Acrobat Reader。

静默打印

后边的第一个是文件路径 第二个是打印机名称

1
AcroRd32.exe /N /T "D:\Tools\微课打印PDF\空白.pdf" "Microsoft Print to PDF"

只打开文件

1
AcroRd32.exe "D:\Tools\微课打印PDF\空白.pdf"

弹出打印窗口(推荐)

这种方式能够打印多份和设置各种其他属性。

1
AcroRd32.exe /P "D:\Tools\微课打印PDF\空白.pdf"

使用Aspose.PDF打印

这种方式在打印高精度的PDF时直接崩溃了。其他PDF没问题。

安装

1
Install-Package Aspose.PDF -Version 19.1.0

工具类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
using System.IO;
using System;
using Aspose.Pdf;
using Aspose.Pdf.Facades;

namespace z_pdf_printer.Utils
{
public class ZPrintAspose
{
public static void Print(string pdfPath, string printerName)
{
byte[] license = Convert.FromBase64String
(
"PExpY2Vuc2U+CiAgPERhdGE+CiAgICA8TGljZW5zZWRUbz5TdXpob3UgQXVuYm94IFNvZnR3YXJlIENvLiwgTHRkLjwvTGljZW5zZWRUbz4KICAgIDxFbWFpbFRvPnNhbGVzQGF1bnRlYy5jb208L0VtYWlsVG8+CiAgICA8TGljZW5zZVR5cGU+RGV2ZWxvcGVyIE9FTTwvTGljZW5zZVR5cGU+CiAgICA8TGljZW5zZU5vdGU+TGltaXRlZCB0byAxIGRldmVsb3BlciwgdW5saW1pdGVkIHBoeXNpY2FsIGxvY2F0aW9uczwvTGljZW5zZU5vdGU+CiAgICA8T3JkZXJJRD4xOTA4MjYwODA3NTM8L09yZGVySUQ+CiAgICA8VXNlcklEPjEzNDk3NjAwNjwvVXNlcklEPgogICAgPE9FTT5UaGlzIGlzIGEgcmVkaXN0cmlidXRhYmxlIGxpY2Vuc2U8L09FTT4KICAgIDxQcm9kdWN0cz4KICAgICAgPFByb2R1Y3Q+QXNwb3NlLlRvdGFsIGZvciAuTkVUPC9Qcm9kdWN0PgogICAgPC9Qcm9kdWN0cz4KICAgIDxFZGl0aW9uVHlwZT5FbnRlcnByaXNlPC9FZGl0aW9uVHlwZT4KICAgIDxTZXJpYWxOdW1iZXI+M2U0NGRlMzAtZmNkMi00MTA2LWIzNWQtNDZjNmEzNzE1ZmMyPC9TZXJpYWxOdW1iZXI+CiAgICA8U3Vic2NyaXB0aW9uRXhwaXJ5PjIwMjAwODI3PC9TdWJzY3JpcHRpb25FeHBpcnk+CiAgICA8TGljZW5zZVZlcnNpb24+My4wPC9MaWNlbnNlVmVyc2lvbj4KICAgIDxMaWNlbnNlSW5zdHJ1Y3Rpb25zPmh0dHBzOi8vcHVyY2hhc2UuYXNwb3NlLmNvbS9wb2xpY2llcy91c2UtbGljZW5zZTwvTGljZW5zZUluc3RydWN0aW9ucz4KICA8L0RhdGE+CiAgPFNpZ25hdHVyZT53UGJtNUt3ZTYvRFZXWFNIY1o4d2FiVEFQQXlSR0pEOGI3L00zVkV4YWZpQnd5U2h3YWtrNGI5N2c2eGtnTjhtbUFGY3J0c0cwd1ZDcnp6MytVYk9iQjRYUndTZWxsTFdXeXNDL0haTDNpN01SMC9jZUFxaVZFOU0rWndOQkR4RnlRbE9uYTFQajhQMzhzR1grQ3ZsemJLZFZPZXk1S3A2dDN5c0dqYWtaL1E9PC9TaWduYXR1cmU+CjwvTGljZW5zZT4="
);
new License().SetLicense(new MemoryStream(license));
Document doc = new Document(pdfPath);
PdfViewer viewer = new PdfViewer();
viewer.BindPdf(doc);
viewer.PrinterJobName = System.IO.Path.GetFileName(doc.FileName);
viewer.Resolution = 600;
// Set attributes for printing
viewer.AutoResize = false; // Print the file with adjusted size
viewer.AutoRotate = false; // Print the file with adjusted rotation
viewer.PrintPageDialog = true; // Do not produce the page number dialog when printing
viewer.RenderingOptions.UseNewImagingEngine = false;
// Create objects for printer and page settings and PrintDocument
System.Drawing.Printing.PrinterSettings ps = new System.Drawing.Printing.PrinterSettings();
System.Drawing.Printing.PageSettings pgs = new System.Drawing.Printing.PageSettings();
// Set printer name
ps.PrinterName = printerName;
pgs.PaperSize = new System.Drawing.Printing.PaperSize
(
"A4",
827,
1169
);
pgs.Margins = new System.Drawing.Printing.Margins
(
0,
0,
0,
0
);

//Print PDF document
viewer.PrintDocumentWithSettings(pgs, ps);
//Close PDF file
viewer.Close();
}
}
}

使用Spire.Pdf库打印

这种方式

缺点

  1. 需要引用库。
  2. 精度较高的文件打印的效果不好。
  3. 进度较高的文件在打印机队列里数据量太大,达到200-300多M。

优点

  1. 可以设置打印的数量及其他打印参数。

添加依赖

1
Install-Package Spire.PDF -Version 9.6.0

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public static void Print(string pdfPath, string printerName)
{
//加载需要打印的PDF文档
Spire.Pdf.PdfDocument doc = new Spire.Pdf.PdfDocument();
doc.LoadFromFile(pdfPath);

//获取原文档第一页的纸张大小,这里的单位是Point
SizeF size = doc.Pages[0].Size;

//实例化PaperSize对象,设置其宽高
//需要特别注意的是这里涉及到单位的转换,PaperSize的宽高参数默认单位是百英寸
PaperSize paper = new PaperSize
(
"Custom",
(int)size.Width / 72 * 100,
(int)size.Height / 72 * 100
) { RawKind = (int)PaperKind.Custom };

//设置打印的纸张大小为原来文档的大小
doc.PrintSettings.PaperSize = paper;
doc.PrintSettings.PrinterName = printerName;

//设置打印份数为1份
doc.PrintSettings.Copies = 1;

//需要选择ActualSize打印模式
doc.PrintSettings.SelectSinglePageLayout(PdfSinglePageScalingMode.ActualSize, true);
//打印
doc.Print();
}

设置自适应方式

按实际尺寸打印

1
doc.PrintSettings.SelectSinglePageLayout(PdfSinglePageScalingMode.ActualSize, true);

自适应

1
2
//需要选择FitSize打印模式
doc.PrintSettings.SelectSinglePageLayout(PdfSinglePageScalingMode.FitSize, true);

设置打印页面范围

1
doc.PrintSettings.SelectPageRange(1, 5);

打印不连续的页面

1
doc.PrintSettings.SelectSomePages(new int[] { 1, 3, 5, 7 });

静默打印

1
doc.PrintSettings.PrintController = new StandardPrintController();

双面打印

1
2
3
4
5
6
7
8
//判断打印机是否支持双面打印
if (doc.PrintSettings.CanDuplex)
{
//如果支持则设置双面打印模式,可选:Default/Simplex/Horizontal/Vertical
doc.PrintSettings.Duplex = Duplex.Default;
}
//打印PDF文档
doc.Print();

黑白打印

1
2
//黑白打印PDF文档
pdf.PrintSettings.Color = false;

打印多份

1
2
//设置打印份数为2份
doc.PrintSettings.Copies = 2;

一页为多页/多页为一页

调用PdfPrintSettings类的SelectMultiPageLayout方法将一个PDF文档的多张页面打印到一张纸上。

1
2
//将PDF文档的每两张页面打印到一张纸上(排版格式为1行,2列)
pdf.PrintSettings.SelectMultiPageLayout(1, 2);

PdfPrintSettings类的SelectSplitPageLayout方法支持将一个PDF文档的单张页面打印到多张纸上。

该方法是根据A4纸的标准尺寸595pt*842pt对PDF页面进行拆分,超过该大小的页面,打印时其超过部分将会被打印到下一张纸。

1
2
//将PDF文档的单张页面根据标准页面大小进行拆分并打印
pdf.PrintSettings.SelectSplitPageLayout();

自定义纸张大小

保持原来页面大小打印到PDF

1
2
3
4
5
6
7
8
9
10
//获取原文档第一页的纸张大小,这里的单位是Point
SizeF size = doc.Pages[0].Size;

//实例化PaperSize对象,设置其宽高
//需要特别注意的是这里涉及到单位的转换,PaperSize的宽高参数默认单位是百英寸
PaperSize paper = new PaperSize("Custom", (int)size.Width/72*100, (int)size.Height/72*100);
paper.RawKind = (int)PaperKind.Custom;

//设置打印的纸张大小为原来文档的大小
doc.PrintSettings.PaperSize = paper;

将原来A4的文档打印成A3的大小。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//加载需要打印的PDF文档
Spire.Pdf.PdfDocument doc = new Spire.Pdf.PdfDocument();
doc.LoadFromFile(FileName);

PaperSize p = null;
//实例化一个PrintDocument对象来获取当前打印机的纸盒信息
PrintDocument printDoc = new PrintDocument();

//遍历打印机纸盒里面的纸张,找到需要的A3
foreach (PaperSize ps in printDoc.PrinterSettings.PaperSizes)
{
if (ps.PaperName.Equals("A3"))
{
p = ps;
break;
}
}

//设置打印的纸张大小为A3
doc.PrintSettings.PaperSize = p;

//打印
doc.PrintSettings.SelectSinglePageLayout(PdfSinglePageScalingMode.FitSize, true);
doc.Print();

使用PDFRender4NET打印

这一种和前面的方式优缺点一致

需要引用O2S.Components.PDFRender4NET.dll

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
using O2S.Components.PDFRender4NET;
private void Print3(string PDFPath, string PrinterName, short PrinterNum = 1)
{
PDFFile file = PDFFile.Open(PDFPath);
PrinterSettings settings = new PrinterSettings();

settings.PrinterName = PrinterName;
settings.PrintToFile = false;

//设置纸张大小(可以不设置,取默认设置)3.90 in, 8.65 in
PaperSize ps = new PaperSize("test", 4, 9);

ps.RawKind = 9; //如果是自定义纸张,就要大于118,(A4值为9,详细纸张类型与值的对照请看http://msdn.microsoft.com/zh-tw/library/system.drawing.printing.papersize.rawkind(v=vs.85).aspx)

O2S.Components.PDFRender4NET.Printing.PDFPrintSettings pdfPrintSettings = new O2S.Components.PDFRender4NET.Printing.PDFPrintSettings(settings)
{
PaperSize = ps,
PageScaling = O2S.Components.PDFRender4NET.Printing.PageScaling.FitToPrinterMarginsProportional
};

pdfPrintSettings.PrinterSettings.Copies = PrinterNum;

try
{
file.Print(pdfPrintSettings);
}
catch (Exception)
{
}
finally
{
file.Dispose();
}
}

ComboBox数据源及事件

在C#中,可以通过以下步骤将数据源绑定到ComboBox控件:
定义数据源,可以是列表、数组或其他集合:

创建ComboBox并设置数据源:

1
2
List<string> dataSource = new List<string> { "Item1", "Item2", "Item3" };
MyCb.ItemsSource = dataSource;

这时选中的值和显示的值一样

1
2
3
4
//选中的值
Console.WriteLine(MyCb.SelectedValue.ToString());
//显示的值
Console.WriteLine(MyCb.Text);

也可以设置显示字段和值字段,如果数据源是对象列表:

1
2
3
4
5
6
7
List<Product> products = new List<Product> { 
new Product { Name = "Apple", Id = 1 },
new Product { Name = "Orange", Id = 2 }
};
MyCb.ItemsSource = products;
MyCb.DisplayMemberPath = "Name";
MyCb.SelectedValuePath = "Id";

绑定SelectionChanged事件来获取选择的值:

1
2
3
4
5
6
7
8
9
MyCb.SelectionChanged +=
(
s,
e
) =>
{
string text = MyCb.Text;
// ...
};

也可以在代码中设置选中项:

1
2
MyCb.SelectedIndex = 0; // 选中第一项
MyCb.SelectedValue = 2; // 选中Id为2的项

以上就是在C#中将数据源绑定到ComboBox并获取选择值的主要步骤。除此之外,也可以设置ComboBox的各种属性,如是否可编辑、是否下拉显示选项列表等。
总结一下主要的属性和事件:

  • ItemsSource: 设置数据源
  • DisplayMemberPath: 显示字段
  • SelectedValuePath: 值字段
  • SelectedIndex/SelectedValue: 获取或设置选中项