【问题标题】:Silent print HTML file in C# using WPF使用 WPF 在 C# 中静默打印 HTML 文件
【发布时间】:2015-05-07 11:58:34
【问题描述】:

编辑:完全重写问题并增加赏金。

基于许多教程和 stackoverflow 问题,我现在可以:

  • 将多页作为一个文档一起打印。
  • 打印实际内容。
  • 在页面上正确对齐内容。
  • 打印正确尺寸的文档。

解决方案要求HTML文档底部有一些空白,样式标签html{overflow:hidden; } - 隐藏滚动条并允许滚动用于分页 - 但我可以接受。

唯一剩下的问题是 WPF 不会渲染不在屏幕上的网页浏览器部分。

这意味着如果我倾斜我的电脑屏幕,我可以正确打印,但如果我不这样做,文档会切断下部。

我尝试渲染为位图,但是当我将生成的图像作为视觉对象打印时,页面是空的。

如果您知道如何强制 WPF 完全渲染或如何正确渲染为位图,请帮助我。

打印窗口 XAML:(打印 WPF 仅适用于 UI 线程,否则不会呈现任何内容...)

<Window x:Class="CardLoader2000.PrintWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="PrintWindow" Height="1139" Width="820">
    <Grid x:Name="grid">
        <!--The alignment and size of the webbrowser is reflected in the print. If larger than document
        it will be cut. The width here corresponds to A4 paper width with a little margin-->
        <WebBrowser x:Name="webBrowser" Height="1089" Width="770" VerticalAlignment="Top" Margin="0,10,0,0"/>
    </Grid>
</Window>

打印窗口代码隐藏:

public partial class PrintWindow : Window
    {
        public PrintWindow(string path)
        {
            InitializeComponent();

            FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read);

            webBrowser.NavigateToStream(fs);

            ContentRendered += OnContentRendered;
        }

        private void OnContentRendered(object sender, EventArgs eventArgs)
        {
            PrintDialog pd = new PrintDialog
            {
                PrintTicket = new PrintTicket
                {
                    Duplexing = Duplexing.TwoSidedLongEdge,
                    OutputColor = OutputColor.Monochrome,
                    PageOrientation = PageOrientation.Portrait,
                    PageMediaSize = new PageMediaSize(794, 1122),
                    InputBin = InputBin.AutoSelect
                }
            };

            //Ok, final TODO: Page only renders what is on the PC screen...
            WebPaginator paginator = new WebPaginator(webBrowser, 1089, 1122, 794);

            pd.PrintDocument(paginator, "CustomerLetter");

            Close();
        }
    }

自定义分页器:

public class WebPaginator : DocumentPaginator
    {
        private readonly WebBrowser webBrowser;
        private readonly int pageScroll;
        private Size pageSize;

        public WebPaginator(WebBrowser webBrowser, int pageScroll, double pageHeight, double pageWidth)
        {
            this.webBrowser = webBrowser;
            this.pageScroll = pageScroll;
            pageSize = new Size(pageWidth, pageHeight);
        }

        public override DocumentPage GetPage(int pageNumber)
        {
            HTMLDocument htmlDoc = webBrowser.Document as HTMLDocument;
            if (htmlDoc != null) htmlDoc.parentWindow.scrollTo(0, pageScroll * pageNumber);
            Rect area = new Rect(pageSize);

            return new DocumentPage(webBrowser, pageSize, area, area);
        }

        public override bool IsPageCountValid
        {
            get { return true; }
        }

        /// <summary>
        /// Returns one less than actual length.
        /// Last page should be whitespace, used for scrolling.
        /// </summary>
        public override int PageCount
        {
            get
            {
                var doc = (IHTMLDocument2)webBrowser.Document;
                var height = ((IHTMLElement2)doc.body).scrollHeight;
                int tempVal = height*10/pageScroll;
                tempVal = tempVal%10 == 0
                    ? Math.Max(height/pageScroll, 1)
                    : height/pageScroll + 1;
                return tempVal > 1 ? tempVal-1 : tempVal;
            }
        }

        public override Size PageSize
        {
            get
            {
                return pageSize;
            }
            set
            {
                pageSize = value;
            }
        }

        /// <summary>
        /// Can be null.
        /// </summary>
        public override IDocumentPaginatorSource Source
        {
            get
            {
                return null;
            }
        }
    }

【问题讨论】:

  • 使用 IHTMLDocument2 打印也会给出一个空文档并始终提示用户,即使您在 execCommand 调用中将 ShowUI 设置为 false。
  • 直接从 UI 打印是绝对要求吗?我的意思是来自WebBrowser?您是否考虑过将相同的 URL 独立地传递给为此目的而设计得更好的工具?比如princexml.com/doc/dotnet?最终,从 PDF 打印到纸张后会产生更加一致的结果...
  • 嗯,不,我开始意识到网络浏览器控制方法是完全不可能的。它不是真正的 WPF 控件,而是包装的 COM 控件。这意味着它不会做你告诉它做的事情。我设法让它认为屏幕比它大,但由于它是一个 COM 控件,它无论如何都没有渲染 - 只是黑色。我还尝试了 RenderRotateTransform,但控件中的内容保持未旋转。我会考虑你的链接/解决方案,但我现在有点厌倦了尝试随机的东西。我可能会完全改变我的方法。

标签: c# html wpf printing wpf-controls


【解决方案1】:

您可以使用标准 IE 的打印功能(通过ExecWB method),如下所示:

public partial class PrintWindow : Window
{
    public PrintWindow()
    {
        InitializeComponent();
        webBrowser.Navigate("http://www.google.com");
    }

    // I have added a button to demonstrate
    private void Button_Click(object sender, RoutedEventArgs e)
    {
        // NOTE: this works only when the document as been loaded
        IOleServiceProvider sp = webBrowser.Document as IOleServiceProvider;
        if (sp != null)
        {
            Guid IID_IWebBrowserApp = new Guid("0002DF05-0000-0000-C000-000000000046");
            Guid IID_IWebBrowser2 = new Guid("D30C1661-CDAF-11d0-8A3E-00C04FC9E26E");
            const int OLECMDID_PRINT = 6;
            const int OLECMDEXECOPT_DONTPROMPTUSER = 2;

            dynamic wb; // will be of IWebBrowser2 type, but dynamic is cool
            sp.QueryService(IID_IWebBrowserApp, IID_IWebBrowser2, out wb);
            if (wb != null)
            {
                // note: this will send to the default printer, if any
                wb.ExecWB(OLECMDID_PRINT, OLECMDEXECOPT_DONTPROMPTUSER, null, null);
            }
        }
    }

    [ComImport, Guid("6D5140C1-7436-11CE-8034-00AA006009FA"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    private interface IOleServiceProvider
    {
        [PreserveSig]
        int QueryService([MarshalAs(UnmanagedType.LPStruct)] Guid guidService, [MarshalAs(UnmanagedType.LPStruct)]  Guid riid, [MarshalAs(UnmanagedType.IDispatch)] out object ppvObject);
    }
}

【讨论】:

  • 我的赞成票,我认为很好的答案(可以根据您的说明直接从 webBrowser_LoadCompleted 调用),我也建议在这种情况下使用 IE 的打印功能,即使我的个人偏好是使用工具(如 SharpDevelopReporting)并从 PDF 打印(如上面的 cmets 中已经写的)
  • 这可行,但它会打印为双面/双面吗? (我现在无法使用我的打印机,只能使用 XPS 文件打印机)
  • 它将使用默认打印机和默认设置进行打印,无论它们是什么。
  • 是的,开箱即用。复制粘贴并检查打印机默认值 - 没问题:)
  • 有没有这样的答案,默认打印机无法更改且所需打印机不是默认打印机?
猜你喜欢
  • 2014-05-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-03
  • 2017-04-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多