I have a C# WPF .NET 4.6 program that creates HTML files and I would like to print them automatically (without dialog) with a known, non-default printer. Of course this includes rendering the HTML first. Since the program creates these files, the HTML data can come from a MemoryStream
, FileStream
, or directly from string.
The program has settings that allows the user to specify which printer to print to in advance, using System.Drawing.Printing.PrinterSettings.InstalledPrinters
, as each file may require a different printer. At printing time, the printer's name is known, but will likely be different from the Windows default printer.
I have researched a lot of other projects, but they do not seem to account for the printer being different from the default. Changing the default printer would be anti-social and would cause a world of threading-related pain. This seems to be the #1 accepted solution, though cannot be the best solution??
Research and solutions looked at:
Printing the contents of a WPF WebBrowser and Silently print HTML from WPF WebBrowser and corresponding MSDN forum discussions are insufficient as the COM ExecWB function only prints to the default printer (?)
The MSDN example only uses the Print() command on the WebBrowser
, which again uses the default printer.
So I went down the route of trying to change the printer options. Programmatically changing the destination printer for a WinForms WebBrowser control was asked, but has a rather unsatisfactory answer, as it has a broken link, and I don't know what external programs the running computer has, so I cannot guarantee Adobe, OpenOffice, etc. OP mentioned that they parsed the ActiveX COM without going into detail. Sounds tricky.
Perhaps I could gleam something from writing to a RichTextBox like this project does, and hide the box?
I thought Silent print HTML file in C# using WPF is on a good path, however the original post has hard-coded numbers for screen dimensions‽ and OP mentioned that the printer cut off the document. The accepted (and bountied) answer again use the ExecWB default printer settings method.
execCommand("Print", false, IDon'tUnderstandThisArgument) also showed promise, as its answer was an updated MSDN answer, but the filestream sent to the printer does not allow HTML, nor does the DocumentStream
from WebBrowser
appear to work (the printer prints a single blank page).
How do I programatically change printer settings with the WebBrowser control? has very similar requirements to me, except changes the registry as a solution.
Other than researching how others have done it, I also tried printing the WPF WebBrowser
directly as it is a Visual
control:
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;
}
however this does not print anything (because the WebBrowser
is not visible?).
And tried a PrintDocument
as the updated MSDN article suggested:
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;
}
}
with no joy.
I've also used the PRINT DOS command:
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();
}
}
but the print command seems to only accept text files.