0

我正在构建一个 WPF 应用程序,我需要在其中显示文档预览,例如使用 DocumentViewer 和 DocumentPaginator 可以实现的内容。然而,事实证明,当报表很大时,将报表转换为 XPS 并将其加载到 DocumentViewer 中会非常慢(就像我需要显示的常见报表一样)。

这让我开始思考,可能有某种方法可以开始显示报告的前几页,而其余页面正在“加载”到 DocumentViewer 中——基本上是在创建页面时加载/显示页面。

有谁知道这样的事情是否可能?如果是这样,你会建议我如何开始尝试让它发挥作用?我花了几个小时在网上四处寻找更快显示报告的解决方案,但没有提出任何建议。

为了充分披露,在这种情况下,我需要显示的报告是用 HTML 创建的。我知道我需要将其转换为 XPS 才能使用 DocumentViewer,但我提出这个是因为如果有人有快速显示 HTML 的方法,也请随时提出。我不能使用 WebBrowser 控件,因为我必须在“打印预览”类型的模式下显示。 决定如何对 HTML 站点进行“分页”的一个好的算法可能会引导我解决这个问题,然后我可以创建一个自定义控件来显示它。我会使用 DocumentPaginator,但是输出的文件是 XPS,然后我回到 DocumentViewer 问题。

再次,非常感谢任何帮助。谢谢!

4

1 回答 1

1

好吧,我想我有东西...

我又一次找到了一个更好的参考网址。这个并没有直接为我加载,所以我从谷歌缓存中抓取了它:http ://webcache.googleusercontent.com/search?q=cache:LgceMCkJBrsJ:joshclose.net/%3Fp%3D247

按照每篇文章中的描述定义 IViewObject 接口:

    [ComVisible(true), ComImport()]
    [GuidAttribute("0000010d-0000-0000-C000-000000000046")]
    [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
    public interface IViewObject
    {
        [return: MarshalAs(UnmanagedType.I4)]
        [PreserveSig]
        int Draw(
            [MarshalAs(UnmanagedType.U4)] UInt32 dwDrawAspect,
            int lindex,
            IntPtr pvAspect,
            [In] IntPtr ptd,
            IntPtr hdcTargetDev,
            IntPtr hdcDraw,
            [MarshalAs(UnmanagedType.Struct)] ref Rectangle lprcBounds,
            [MarshalAs(UnmanagedType.Struct)] ref Rectangle lprcWBounds,
            IntPtr pfnContinue,
            [MarshalAs(UnmanagedType.U4)] UInt32 dwContinue);
        [PreserveSig]
        int GetColorSet([In, MarshalAs(UnmanagedType.U4)] int dwDrawAspect,
           int lindex, IntPtr pvAspect, [In] IntPtr ptd,
            IntPtr hicTargetDev, [Out] IntPtr ppColorSet);
        [PreserveSig]
        int Freeze([In, MarshalAs(UnmanagedType.U4)] int dwDrawAspect,
                        int lindex, IntPtr pvAspect, [Out] IntPtr pdwFreeze);
        [PreserveSig]
        int Unfreeze([In, MarshalAs(UnmanagedType.U4)] int dwFreeze);
        void SetAdvise([In, MarshalAs(UnmanagedType.U4)] int aspects,
          [In, MarshalAs(UnmanagedType.U4)] int advf,
          [In, MarshalAs(UnmanagedType.Interface)] IAdviseSink pAdvSink);
        void GetAdvise([In, Out, MarshalAs(UnmanagedType.LPArray)] int[] paspects,
          [In, Out, MarshalAs(UnmanagedType.LPArray)] int[] advf,
          [In, Out, MarshalAs(UnmanagedType.LPArray)] IAdviseSink[] pAdvSink);
    }

创建一个 HtmlPaginator 类,用于截取浏览器的文档(如前所述),然后将其裁剪为页面/框架:

    class HtmlPaginator
    {
        public event EventHandler<PageImageEventArgs> PageReady;

        protected virtual void OnPageReady(PageImageEventArgs e)
        {
            EventHandler<PageImageEventArgs> handler = this.PageReady;
            if (handler != null)
                handler(this, e);
        }

        public class PageImageEventArgs : EventArgs
        {
            public Image PageImage { get; set; }
            public int PageNumber { get; set; }
        }

        public void GeneratePages(string doc)
        {
            Bitmap htmlImage = RenderHtmlToBitmap(doc);

            int pageWidth = 800;
            int pageHeight = 600;

            int xLoc = 0;
            int yLoc = 0;
            int pages = 0;

            do
            {
                int remainingHeightOrPageHeight = Math.Min(htmlImage.Height - yLoc, pageHeight);
                int remainingWidthOrPageWidth = Math.Min(htmlImage.Width - xLoc, pageWidth);
                Rectangle cropFrame = new Rectangle(xLoc, yLoc, remainingWidthOrPageWidth, remainingHeightOrPageHeight);

                Bitmap page = htmlImage.Clone(cropFrame, htmlImage.PixelFormat);

                pages++;
                PageImageEventArgs args = new PageImageEventArgs { PageImage = page, PageNumber = pages };
                OnPageReady(args);

                yLoc += pageHeight;

                if (yLoc > htmlImage.Height)
                {
                    xLoc += pageWidth;

                    if (xLoc < htmlImage.Width)
                    {
                        yLoc = 0;
                    }
                }
            } 
            while (yLoc < htmlImage.Height && xLoc < htmlImage.Width);
        }

        private static Bitmap RenderHtmlToBitmap(string doc)
        {
            Bitmap htmlImage = null;

            using (var webBrowser = new WebBrowser())
            {
                webBrowser.ScrollBarsEnabled = false;
                webBrowser.ScriptErrorsSuppressed = true;
                webBrowser.DocumentText = doc;

                while (webBrowser.ReadyState != WebBrowserReadyState.Complete)
                {
                    Application.DoEvents();
                }

                webBrowser.Width = webBrowser.Document.Body.ScrollRectangle.Width;
                webBrowser.Height = webBrowser.Document.Body.ScrollRectangle.Height;

                htmlImage = new Bitmap(webBrowser.Width, webBrowser.Height);
                using (Graphics graphics = Graphics.FromImage(htmlImage))
                {
                    var hdc = graphics.GetHdc();

                    var rect1 = new Rectangle(0, 0, webBrowser.Width, webBrowser.Height);
                    var rect2 = new Rectangle(0, 0, webBrowser.Width, webBrowser.Height);

                    var viewObject = (IViewObject)webBrowser.Document.DomDocument;
                    viewObject.Draw(1, -1, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, hdc, ref rect1, ref rect2, IntPtr.Zero, 0);

                    graphics.ReleaseHdc(hdc);
                }
            }

            return htmlImage;
        }
    }

像这样称呼它:

        WebBrowser browser = new WebBrowser();
        browser.Navigate("http://www.stackoverflow.com");

        while (browser.ReadyState != WebBrowserReadyState.Complete)
        {
            Application.DoEvents();
        }

        HtmlPaginator pagr = new HtmlPaginator();

        pagr.PageReady += new EventHandler<HtmlPaginator.PageImageEventArgs>(pagr_PageReady);

        pagr.GeneratePages(browser.DocumentText);

为了测试它,我实现了一个带有按钮、图片框和列表集合的基本表单。当页面从 HtmlPaginator 准备好时,我将页面添加到集合中,并使用按钮将下一个图像添加到图片框。

神奇的数字是您想要的宽度和高度。我使用了 800x600,但您可能需要不同的尺寸。

这里的缺点是您仍在等待 WebBrowser 呈现 HTML,但我真的不知道替代解决方案将如何减少该时间 - 首先必须解释和绘制 HTML。我猜写你自己的网络浏览器。:)

我确实尝试过使用 IViewObject.Draw 来查看是否可以让它直接渲染页面框架而不是裁剪循环,但它对我不起作用。

于 2011-04-21T19:18:53.090 回答