我有一个C#WPF .NET 4.6程序,可以创建HTML文件,我想用一个已知的非默认打印机自动打印它们(没有对话框)。当然,这包括首先呈现HTML。由于程序会创建这些文件,因此HTML数据可以来自MemoryStream
,FileStream
,也可以直接来自字符串。
该程序具有允许用户使用System.Drawing.Printing.PrinterSettings.InstalledPrinters
指定要打印到哪个打印机的设置,因为每个文件可能需要不同的打印机。在打印时,打印机的名称是已知的,但可能与Windows默认打印机不同。
我已经研究了很多其他项目,但它们似乎并没有说明打印机与默认设置不同。更改默认打印机将是反社会的,并将导致与线程相关的痛苦世界。这似乎是#1接受的解决方案,虽然不是最好的解决方案??
研究和解决方案着眼于:
Printing the contents of a WPF WebBrowser和Silently print HTML from WPF WebBrowser和 corresponding MSDN forum discussions 由于COM ExecWB功能仅打印到默认打印机(?)
,因此不够用 MSDN example仅使用WebBrowser
上的Print()命令,该命令再次使用默认打印机。
所以我走了试图改变打印机选项的路线。 Programmatically changing the destination printer for a WinForms WebBrowser control被问到,但答案相当令人不满意,因为链接断了,我不知道正在运行的计算机有哪些外部程序,所以我不能保证Adobe,OpenOffice等OP提到他们解析了ActiveX COM而没有详细说明。听起来很棘手。
也许我可以通过写入this project之类的RichTextBox来解决一些问题,并隐藏这个框?
我认为Silent print HTML file in C# using WPF处于良好的路径上,但原始帖子的屏幕尺寸有硬编码,而OP提到打印机切断了文档。接受(和重新计算)的答案再次使用ExecWB默认打印机设置方法。
execCommand("Print", false, IDon'tUnderstandThisArgument)也显示了承诺,因为其答案是更新后的MSDN answer,但发送到打印机的文件流不允许HTML,DocumentStream
也不允许WebBrowser
似乎工作(打印机打印一个空白页)。
How do I programatically change printer settings with the WebBrowser control?对我的要求非常相似。
除了研究其他人如何做到这一点之外,我还尝试直接打印WPF WebBrowser
,因为它是Visual
控件:
public static bool Print(string printer, Visual objToPrint)
{
if (string.IsNullOrEmpty(printer))
{
return false;
}
var dlg = new PrintDialog
{
PrintQueue = new PrintServer().GetPrintQueue(printer)
};
dlg.PrintTicket.CopyCount = 1;
dlg.PrintTicket.PageOrientation = PageOrientation.Portrait;
dlg.PrintTicket.PagesPerSheet = 1;
dlg.PrintVisual(objToPrint, "Print description");
return true;
}
然而,这不会打印任何内容(因为WebBrowser
不可见?)。
并尝试了PrintDocument
作为更新的MSDN article建议:
public static async Task<bool> PrintHTMLAsync(string printer, string html)
{
bool result;
using (var webBrowser = new System.Windows.Forms.WebBrowser())
{
webBrowser.DocumentCompleted += ((sender, e) => browserReadySemaphore.Release());
byte[] buffer = Encoding.UTF8.GetBytes(html);
webBrowser.DocumentStream = new MemoryStream(buffer);
// Wait until the page loads.
await browserReadySemaphore.WaitAsync();
try
{
using (PrintDocument pd = new PrintDocument())
{
pd.PrinterSettings.PrinterName = printer;
pd.PrinterSettings.Collate = false;
pd.PrinterSettings.Copies = 1;
pd.PrinterSettings.FromPage = 1;
pd.PrinterSettings.ToPage = 1;
pd.Print();
result = true;
}
}
catch (Exception ex)
{
result = false;
Debug.WriteLine(ex);
}
return result;
}
}
没有快乐。
我还使用了PRINT DOS命令:
public static string PerformSilentPrinting(string fileName, string printerName)
{
try
{
ProcessStartInfo startInfo = new ProcessStartInfo(fileName)
{
Arguments = string.Format("/C PRINT /D:\"{0}\" \"{1}\"", printerName, fileName),
FileName = "cmd.exe",
RedirectStandardOutput = true,
UseShellExecute = false,
WindowStyle = ProcessWindowStyle.Hidden,
};
// Will execute the batch file with the provided arguments
Process process = Process.Start(startInfo);
// Reads the output
return process.StandardOutput.ReadToEnd();
}
catch (Exception ex)
{
return ex.ToString();
}
}
但是打印命令似乎只接受文本文件。
答案 0 :(得分:1)
我找到的最佳解决方案是here
这样可以无声地打印。但是,这不允许您更改默认打印机。因此,作为解决方法,您可以将默认打印机设置为您要使用的打印机,然后在完成打印后再切换回来。
我会是这样的:
protected static class PrinterSetter
{
[DllImport("winspool.drv", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool SetDefaultPrinter(string Name);
}
然后在上面链接中引用的代码中添加:
if (wb != null)
{
PrinterSettings settings = new PrinterSettings();
string defaultPrinter = settings.PrinterName;
Printer.SetDefaultPrinter("Microsoft Print to PDF");
wb.ExecWB(OLECMDID_PRINT, OLECMDEXECOPT_DONTPROMPTUSER, null, null);
(new System.Action(() =>
{
Thread.Sleep(5000);
Printer.SetDefaultPrinter(defaultPrinter);
})).BeginInvoke(null, null);
}
答案 1 :(得分:0)
编辑:如果您只需打印一页A4,此解决方案效果很好。但是它只打印一页,并截断任何页面。
最后,我使用WinForms WebBrowser,将控件复制到位图中,然后使用PrintDialog
打印,System.Windows.Forms
也位于using Microsoft.Win32;
using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Printing;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
public static class PrintUtility
{
private static readonly SemaphoreSlim browserReadySemaphore = new SemaphoreSlim(0);
// A4 dimensions.
private const int DPI = 600;
private const int WIDTH = (int)(8.3 * DPI);
private const int HEIGHT = (int)(11.7 * DPI);
public static void Print(this Image image, string printer, bool showDialog = false)
{
if (printer == null)
{
throw new ArgumentNullException("Printer cannot be null.", nameof(printer));
}
using (PrintDialog printDialog = new PrintDialog())
{
using (PrintDocument printDoc = new PrintDocument())
{
printDialog.Document = printDoc;
printDialog.Document.DocumentName = "My Document";
printDialog.Document.OriginAtMargins = false;
printDialog.PrinterSettings.PrinterName = printer;
printDoc.PrintPage += (sender, e) =>
{
// Draw to fill page
e.Graphics.DrawImage(image, 0, 0, e.PageSettings.PrintableArea.Width, e.PageSettings.PrintableArea.Height);
// Draw to default margins
// e.Graphics.DrawImage(image, e.MarginBounds);
};
bool doPrint = !showDialog;
if (showDialog)
{
var result = printDialog.ShowDialog();
doPrint = (result == DialogResult.OK);
}
if (doPrint)
{
printDoc.Print();
}
}
}
}
public static async Task<bool> RenderAndPrintHTMLAsync(string html, string printer)
{
bool result = false;
// Enable HTML5 etc. (assuming we're running IE9+)
SetFeatureBrowserFeature("FEATURE_BROWSER_EMULATION", 9000);
// Force software rendering
SetFeatureBrowserFeature("FEATURE_IVIEWOBJECTDRAW_DMLT9_WITH_GDI", 1);
SetFeatureBrowserFeature("FEATURE_GPU_RENDERING", 0);
using (var webBrowser = new WebBrowser())
{
webBrowser.ScrollBarsEnabled = false;
webBrowser.Width = WIDTH;
webBrowser.Height = HEIGHT;
webBrowser.DocumentCompleted += ((s, e) => browserReadySemaphore.Release());
webBrowser.LoadHTML(html);
// Wait until the page loads.
await browserReadySemaphore.WaitAsync();
// Save the picture
using (var bitmap = webBrowser.ToBitmap())
{
bitmap.Save("WebBrowser_Bitmap.bmp");
Print(bitmap, printer);
result = true;
}
}
return result;
}
/// <summary>
/// Make a Bitmap from the Control.
/// Remember to dispose after.
/// </summary>
/// <param name="control"></param>
/// <returns></returns>
public static Bitmap ToBitmap(this Control control)
{
Bitmap bitmap = new Bitmap(control.Width, control.Height);
Rectangle rect = new Rectangle(0, 0, control.Width, control.Height);
control.DrawToBitmap(bitmap, new Rectangle(0, 0, control.Width, control.Height));
return bitmap;
}
/// <summary>
/// Required because of a bug where the WebBrowser only loads text once or not at all.
/// </summary>
/// <param name="webBrowser"></param>
/// <param name="htmlToLoad"></param>
/// <remarks>
/// http://stackoverflow.com/questions/5362591/how-to-display-the-string-html-contents-into-webbrowser-control/23736063#23736063
/// </remarks>
public static void LoadHTML(this WebBrowser webBrowser, string htmlToLoad)
{
webBrowser.Document.OpenNew(true);
webBrowser.Document.Write(htmlToLoad);
webBrowser.Refresh();
}
/// <summary>
/// WebBrowser Feature Control
/// </summary>
/// <param name="feature"></param>
/// <param name="value"></param>
/// <remarks>
/// http://stackoverflow.com/questions/21697048/how-to-fix-a-opacity-bug-with-drawtobitmap-on-webbrowser-control/21828265#21828265
/// http://msdn.microsoft.com/en-us/library/ie/ee330733(v=vs.85).aspx
/// </remarks>
private static void SetFeatureBrowserFeature(string feature, uint value)
{
if (LicenseManager.UsageMode != LicenseUsageMode.Runtime)
{
return;
}
var appName = Path.GetFileName(System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName);
Registry.SetValue(
@"HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\Main\FeatureControl\" + feature,
appName,
value,
RegistryValueKind.DWord);
}
}
命名空间中。
package org.foo.bar