【问题标题】:Print WebBrowser without previewing i.e. single click print在不预览的情况下打印 WebBrowser,即单击打印
【发布时间】:2013-11-13 02:09:38
【问题描述】:

我有一个 c# .Net 应用程序,它打开一个自定义的打印预览表单(破坏了一个显示 HTML 文件的 WebBrowser 表单控件和一个打印 Web 浏览器控件内容的按钮:

webBrowser.Print();

但是,我现在想在不打开此表单的情况下打印 HTML 文件。

我试图将 html 加载到 webbrowser 表单中而不显示表单并调用 webBrowser.Print() 但什么都不会打印。如果我显示表单并将 HTML 加载到控件中,我似乎只能打印。

【问题讨论】:

标签: c# .net printing webbrowser-control


【解决方案1】:

我有一个示例控制台应用程序,它使用 WinForms WebBrowser 打印一组 HTML 文件。您可以借用其中的 DoWorkAsync 部分用于 WinForms 应用程序中的打印任务,几乎无需任何更改:

// by Noseratio - http://stackoverflow.com/users/1768303/noseratio
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace ConsoleApplicationWebBrowser
{
    class Program
    {
        // Entry Point of the console app
        static void Main(string[] args)
        {
            try
            {
                // download each page and dump the content
                var task = MessageLoopWorker.Run(DoWorkAsync,
                    "http://www.example.com", "http://www.example.net", "http://www.example.org");
                task.Wait();
                Console.WriteLine("DoWorkAsync completed.");
            }
            catch (Exception ex)
            {
                Console.WriteLine("DoWorkAsync failed: " + ex.Message);
            }
            Console.WriteLine("Press Enter to exit.");
            Console.ReadLine();
        }

        // navigate WebBrowser to the list of urls in a loop
        static async Task<object> DoWorkAsync(object[] args)
        {
            Console.WriteLine("Start working.");

            var wb = new WebBrowser();
            wb.ScriptErrorsSuppressed = true;

            if (wb.Document == null && wb.ActiveXInstance == null)
                throw new ApplicationException("Unable to initialize the underlying WebBrowserActiveX");

            // get the underlying WebBrowser ActiveX object;
            // this code depends on SHDocVw.dll COM interop assembly,
            // generate SHDocVw.dll: "tlbimp.exe ieframe.dll",
            // and add as a reference to the project
            var wbax = (SHDocVw.WebBrowser)wb.ActiveXInstance;

            TaskCompletionSource<bool> loadedTcs = null;
            WebBrowserDocumentCompletedEventHandler documentCompletedHandler = (s, e) =>
                loadedTcs.TrySetResult(true); // turn event into awaitable task

            TaskCompletionSource<bool> printedTcs = null;
            SHDocVw.DWebBrowserEvents2_PrintTemplateTeardownEventHandler printTemplateTeardownHandler = (p) =>
                printedTcs.TrySetResult(true); // turn event into awaitable task

            // navigate to each URL in the list
            foreach (var url in args)
            {
                loadedTcs = new TaskCompletionSource<bool>();
                wb.DocumentCompleted += documentCompletedHandler;
                try
                {
                    wb.Navigate(url.ToString());
                    // await for DocumentCompleted
                    await loadedTcs.Task;
                }
                finally
                {
                    wb.DocumentCompleted -= documentCompletedHandler;
                }

                // the DOM is ready, 
                Console.WriteLine(url.ToString());
                Console.WriteLine(wb.Document.Body.OuterHtml);

                // print the document
                printedTcs = new TaskCompletionSource<bool>();
                wbax.PrintTemplateTeardown += printTemplateTeardownHandler;
                try
                {
                    wb.Print();
                    // await for PrintTemplateTeardown - the end of printing
                    await printedTcs.Task;
                }
                finally
                {
                    wbax.PrintTemplateTeardown -= printTemplateTeardownHandler;
                }
                Console.WriteLine(url.ToString() + " printed.");
            }

            wb.Dispose();
            Console.WriteLine("End working.");
            return null;
        }

    }

    // a helper class to start the message loop and execute an asynchronous task
    public static class MessageLoopWorker
    {
        public static async Task<object> Run(Func<object[], Task<object>> worker, params object[] args)
        {
            var tcs = new TaskCompletionSource<object>();

            var thread = new Thread(() =>
            {
                EventHandler idleHandler = null;

                idleHandler = async (s, e) =>
                {
                    // handle Application.Idle just once
                    Application.Idle -= idleHandler;

                    // return to the message loop
                    await Task.Yield();

                    // and continue asynchronously
                    // propogate the result or exception
                    try
                    {
                        var result = await worker(args);
                        tcs.SetResult(result);
                    }
                    catch (Exception ex)
                    {
                        tcs.SetException(ex);
                    }

                    // signal to exit the message loop
                    // Application.Run will exit at this point
                    Application.ExitThread();
                };

                // handle Application.Idle just once
                // to make sure we're inside the message loop
                // and SynchronizationContext has been correctly installed
                Application.Idle += idleHandler;
                Application.Run();
            });

            // set STA model for the new thread
            thread.SetApartmentState(ApartmentState.STA);

            // start the thread and await for the task
            thread.Start();
            try
            {
                return await tcs.Task;
            }
            finally
            {
                thread.Join();
            }
        }
    }
}

【讨论】:

  • 谢谢,我会试一试,因为看起来像我要找的东西
  • 谢谢,我实现了 DoWorkAsync 并打印成功,但在 task.Wait();您知道为什么会发生这种情况吗?
  • 我添加了一个超时,现在这似乎工作得更好(我正在处理本地 html 文件),但我会密切关注这一点。谢谢
  • @Belliez, task.Wait() 阻止您的应用程序 UI 线程的消息泵循环(在主线程上),而 WebBrowser 控制需要功能性消息泵。您在这里有两个选择:1)异步调用DoWorkAsynclike this; 2) 通过MessageLoopWorker.Run(DoWorkAsync, ...) 调用DoWorkAsync,就像上面的控制台应用程序一样。在后一种情况下,打印将在单独的线程上进行,因此您可以使用 task.Wait() 阻止 UI 线程,直到它完成(虽然这不是一个好的设计决策 - 您应该保持 UI 响应)。
【解决方案2】:

你正在尝试做的事情被称为。

静默打印

也许您应该根据您的技术寻找替代方案。实现在 Forms 和 WPF 之间有所不同。

【讨论】:

    【解决方案3】:

    我知道的唯一方法是将网站呈现为图像,然后静默打印。也许this 项目会对你有所帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多