WPF实战案例-打印
程序员文章站
2022-04-24 13:18:32
在前段时间做了一下打印,因为需要支持的格式比较多,所以wpf能打印的有限分享一下几种格式的打印(.xls .xlsx .doc .docx .png .jpg .bmp .pdf) 首先为了保证excel和word的格式问题,excel和word是调用的office进行打印的。 获取所有打印机的方法 ......
在前段时间做了一下打印,因为需要支持的格式比较多,所以wpf能打印的有限分享一下几种格式的打印(.xls .xlsx .doc .docx .png .jpg .bmp .pdf)
首先为了保证excel和word的格式问题,excel和word是调用的office进行打印的。
获取所有打印机的方法:
localprintserver print = new localprintserver(); var printers = print.getprintqueues(); foreach (var item in printers) { //打印机名称 item.name; }
excel打印。三个参数 :1.打印机名称 2要打印的文件路径+名称 3要打印的sheet页名称(可以不写就打印全部sheet)
static excel.application application { get; set; } public static void printexcel(string printname, string filename, list<string> sheetnames = null) { if (application == null) application = new excel.application(); application.visible = false; //application.calculation = excel.xlcalculation.xlcalculationmanual; var book = application.workbooks.open(filename); if (sheetnames == null) { sheetnames = new list<string>(); excel.sheets sheets = book.sheets; for (int i = 1; i <= sheets.count; i++) { excel.worksheet worksheet = sheets.item[i]; if (worksheet.visible != excel.xlsheetvisibility.xlsheethidden) sheetnames.add(worksheet.name); } } foreach (var item in sheetnames) { excel.worksheet worksheet = (excel.worksheet)book.worksheets[item]; //------------------------打印页面相关设置-------------------------------- worksheet.pagesetup.papersize = excel.xlpapersize.xlpapera4;//纸张大小 //worksheet.pagesetup.orientation = excel.xlpageorientation.xllandscape;//页面横向 worksheet.pagesetup.zoom = 75; //打印时页面设置,缩放比例百分之几 worksheet.pagesetup.zoom = false; //打印时页面设置,必须设置为false,页高,页宽才有效 worksheet.pagesetup.fittopageswide = 1; //设置页面缩放的页宽为1页宽 worksheet.pagesetup.fittopagestall = false; //设置页面缩放的页高自动 //worksheet.pagesetup.leftheader = "nigel";//页面左上边的标志 //worksheet.pagesetup.centerfooter = "第 &p 页,共 &n 页";//页面下标 worksheet.pagesetup.firstpagenumber = (int)excel.constants.xlautomatic; worksheet.pagesetup.order = excel.xlorder.xldownthenover; worksheet.pagesetup.printgridlines = true; //打印单元格网线 worksheet.pagesetup.topmargin = 1.5 / 0.035; //上边距为2cm(转换为in) worksheet.pagesetup.bottommargin = 1.5 / 0.035; //下边距为1.5cm worksheet.pagesetup.leftmargin = 2 / 0.035; //左边距为2cm worksheet.pagesetup.rightmargin = 2 / 0.035; //右边距为2cm worksheet.pagesetup.centerhorizontally = true; //文字水平居中 //------------------------打印页面设置结束-------------------------------- worksheet.printoutex(missing.value, missing.value, missing.value, missing.value, printname); } //book.printoutex(missing.value, missing.value, missing.value, missing.value, printname); //直接打印 book.close(false); //关闭工作空间 }
excel有时候关不干净,贡献一个强制关闭的方法,可以在excel操作完成后调用:
[dllimport("user32.dll")] public static extern int getwindowthreadprocessid(intptr hwnd, out int processid); public static void excelclose() { if (application != null) { application.quit(); int iid = 0; intptr intptr = new intptr(application.hwnd); system.diagnostics.process p = null; try { getwindowthreadprocessid(intptr, out iid); p = system.diagnostics.process.getprocessbyid(iid); if (p != null) { p.kill(); p.dispose(); } application = null; } catch (exception e) { throw e; } } system.gc.collect(); }
word打印(打印机名称,要打印的文件路径+名称)
public static void printword(string printname, string filename) { word.application appword = new word.application(); appword.visible = false; appword.displayalerts = word.wdalertlevel.wdalertsnone; word.document doc = appword.documents.open(filename); appword.activeprinter = printname; doc.printout(true); doc.close(false); appword.quit(); }
图片打印wpf(支持格式 png jpg bmp)
public static string printimage(string printname, string filename) { system.windows.controls.printdialog dialog = new system.windows.controls.printdialog();//开始打印 var printers = new localprintserver().getprintqueues(); var selectedprinter = printers.firstordefault(d => d.name == printname); if (selectedprinter == null) { return "没有找到打印机"; } dialog.printqueue = selectedprinter; dialog.printdocument(new testdocumentpaginator(new bitmapimage(new uri(filename))), "image"); return ""; }
public class testdocumentpaginator : documentpaginator { #region 字段 private size _pagesize; private imagesource image; #endregion #region 构造 public testdocumentpaginator(bitmapimage img) { image = img; //我们使用a3纸张大小 var pagemediasize = localprintserver.getdefaultprintqueue() .getprintcapabilities() .pagemediasizecapability .firstordefault(x => x.pagemediasizename == pagemediasizename.isoa4); if (pagemediasize != null) { _pagesize = new size((double)pagemediasize.width, (double)pagemediasize.height); } } #endregion #region 重写 /// <summary> /// /// </summary> /// <param name="pagenumber">打印页是从0开始的</param> /// <returns></returns> public override documentpage getpage(int pagenumber) { var visual = new drawingvisual(); using (drawingcontext dc = visual.renderopen()) { double imgwidth = 0; double imgheight = 0; if (image.height > _pagesize.height) { double h = _pagesize.height / image.height; imgwidth = image.width * h; imgheight = image.height * h; } if (image.width > _pagesize.width) { double w = _pagesize.width / image.width; imgwidth = image.width * w; imgheight = image.height * w; } if (image.width < _pagesize.width && image.height < _pagesize.height) { double h = _pagesize.height / image.height; double w = _pagesize.width / image.width; if (h > w) { imgwidth = image.width * w; imgheight = image.height * w; } else { imgwidth = image.width * h; imgheight = image.height * h; } } double left = math.abs((_pagesize.width - imgwidth) <= 0 ? 2 : (_pagesize.width - imgwidth)) / 2; double top = math.abs((_pagesize.height - imgheight) <= 0 ? 2 : (_pagesize.height - imgheight)) / 2; dc.drawimage(image, new rect(left, top, imgwidth, imgheight)); } return new documentpage(visual, _pagesize, new rect(_pagesize), new rect(_pagesize)); } public override bool ispagecountvalid { get { return true; } } public override size pagesize { get { return _pagesize; } set { _pagesize = value; } } public override idocumentpaginatorsource source { get { return null; } } public override int pagecount { get { return 1; } } #endregion }
testdocumentpaginator内部的重写方法是计算将图片全部居中显示。
关于图片打印多说一句:在之前我是使用image加载图片,然后通过printvisual打印控件的方式打印的,但是这种方式我发现在win7机器上打印出来的是空白纸张,还没明白是为什么,所以就用这种方式了。
pdf打印(这是调用windows api去打印,不需要乱起八糟的第三方):
// structure and api declarions: [structlayout(layoutkind.sequential, charset = charset.ansi)] public class docinfoa { [marshalas(unmanagedtype.lpstr)] public string pdocname; [marshalas(unmanagedtype.lpstr)] public string poutputfile; [marshalas(unmanagedtype.lpstr)] public string pdatatype; } [dllimport("winspool.drv", entrypoint = "openprintera", setlasterror = true, charset = charset.ansi, exactspelling = true, callingconvention = callingconvention.stdcall)] public static extern bool openprinter([marshalas(unmanagedtype.lpstr)] string szprinter, out intptr hprinter, intptr pd); [dllimport("winspool.drv", entrypoint = "closeprinter", setlasterror = true, exactspelling = true, callingconvention = callingconvention.stdcall)] public static extern bool closeprinter(intptr hprinter); [dllimport("winspool.drv", entrypoint = "startdocprintera", setlasterror = true, charset = charset.ansi, exactspelling = true, callingconvention = callingconvention.stdcall)] public static extern bool startdocprinter(intptr hprinter, int32 level, [in, marshalas(unmanagedtype.lpstruct)] docinfoa di); [dllimport("winspool.drv", entrypoint = "enddocprinter", setlasterror = true, exactspelling = true, callingconvention = callingconvention.stdcall)] public static extern bool enddocprinter(intptr hprinter); [dllimport("winspool.drv", entrypoint = "startpageprinter", setlasterror = true, exactspelling = true, callingconvention = callingconvention.stdcall)] public static extern bool startpageprinter(intptr hprinter); [dllimport("winspool.drv", entrypoint = "endpageprinter", setlasterror = true, exactspelling = true, callingconvention = callingconvention.stdcall)] public static extern bool endpageprinter(intptr hprinter); [dllimport("winspool.drv", entrypoint = "writeprinter", setlasterror = true, exactspelling = true, callingconvention = callingconvention.stdcall)] public static extern bool writeprinter(intptr hprinter, intptr pbytes, int32 dwcount, out int32 dwwritten); // sendbytestoprinter() // when the function is given a printer name and an unmanaged array // of bytes, the function sends those bytes to the print queue. // returns true on success, false on failure. public static bool sendbytestoprinter(string szprintername, intptr pbytes, int32 dwcount) { int32 dwerror = 0, dwwritten = 0; intptr hprinter = new intptr(0); docinfoa di = new docinfoa(); bool bsuccess = false; // assume failure unless you specifically succeed. di.pdocname = "my c#.net raw document"; di.pdatatype = "raw"; // open the printer. if (openprinter(szprintername.normalize(), out hprinter, intptr.zero)) { // start a document. if (startdocprinter(hprinter, 1, di)) { // start a page. if (startpageprinter(hprinter)) { // write your bytes. bsuccess = writeprinter(hprinter, pbytes, dwcount, out dwwritten); endpageprinter(hprinter); } enddocprinter(hprinter); } closeprinter(hprinter); } // if you did not succeed, getlasterror may give more information // about why not. if (bsuccess == false) { dwerror = marshal.getlastwin32error(); } return bsuccess; } public static bool sendfiletoprinter(string szprintername, string szfilename) { // open the file. filestream fs = new filestream(szfilename, filemode.open); // create a binaryreader on the file. binaryreader br = new binaryreader(fs); // dim an array of bytes big enough to hold the file's contents. byte[] bytes = new byte[fs.length]; bool bsuccess = false; // your unmanaged pointer. intptr punmanagedbytes = new intptr(0); int nlength; nlength = convert.toint32(fs.length); // read the contents of the file into the array. bytes = br.readbytes(nlength); // allocate some unmanaged memory for those bytes. punmanagedbytes = marshal.alloccotaskmem(nlength); // copy the managed byte array into the unmanaged array. marshal.copy(bytes, 0, punmanagedbytes, nlength); // send the unmanaged bytes to the printer. bsuccess = sendbytestoprinter(szprintername, punmanagedbytes, nlength); // free the unmanaged memory that you allocated earlier. marshal.freecotaskmem(punmanagedbytes); return bsuccess; } public static bool sendstringtoprinter(string szprintername, string szstring) { intptr pbytes; int32 dwcount; // how many characters are in the string? dwcount = szstring.length; // assume that the printer is expecting ansi text, and then convert // the string to ansi text. pbytes = marshal.stringtocotaskmemansi(szstring); // send the converted ansi string to the printer. sendbytestoprinter(szprintername, pbytes, dwcount); marshal.freecotaskmem(pbytes); return true; } public static void printpdf(string printname, string filename) { sendfiletoprinter(printname, filename); }
以上就是wpf常用的打印,目前看起来格式还是可以达到要求的,更多技术讨论请关注页面下方的qq群。