“快”在文档查看器中显示

发布于 2024-11-02 10:23:47 字数 677 浏览 4 评论 0原文

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

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

有谁知道这样的事情是否可能?如果是这样,您建议我如何开始努力使其发挥作用?我花了几个小时在网上寻找更快地显示报告的解决方案,但还没有想出任何办法。

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

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

I'm building a WPF application in which I need to display document previews such as what is achievable with a DocumentViewer and DocumentPaginator. However, converting the report to XPS and loading it into a DocumentViewer has proven to be very slow when the report is large (as a common report I'll need to display is).

This lead me to start thinking that there is probably some way to start showing the first few pages of the report while the rest of the pages are being 'loaded' into the DocumentViewer -- basically loading/showing the pages as they're created.

Does anyone know if something like this is possible? And, if so, how would you suggest I get started trying to make it work? I've spent a few hours looking around online for a solution to display the report faster, but haven't come up with anything.

For the sake of full disclosure, in this case the report I need to display is being created in HTML. I know that I need to convert it to XPS in order to use the DocumentViewer, but I bring this up because if anyone has a fast way of displaying the HTML, please feel free to bring that up too. I can't use a WebBrowser control as I have to have the display in a 'print preview' type of mode. A good algorithm for deciding how to 'paginate' an HTML site would probably lead me to a solution to this problem as well as then I could create a custom control to display it. I'd use a DocumentPaginator, but then the outputted file is XPS and then I'm back to the DocumentViewer issue.

Again, any help is greatly appreciated. Thank you!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

甜柠檬 2024-11-09 10:23:47

好吧,我想我已经找到了一些东西......

我再次找到了一个更好的 URL 来参考。这个并没有直接为我加载,所以我从 Google 缓存中获取了它: 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 来看看是否可以让它直接渲染页面框架而不是裁剪循环,但它对我不起作用。

Ok, I think I've got something...

Once again I found a better URL to reference. This one wasn't loading for me straight up so I grabbed it from the Google cache: http://webcache.googleusercontent.com/search?q=cache:LgceMCkJBrsJ:joshclose.net/%3Fp%3D247

Define the IViewObject interface as described in each article:

    [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);
    }

Create an HtmlPaginator class that screenshots the browser's document (as described) but then crops it into pages / frames:

    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;
        }
    }

Call it like so:

        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);

To test it I implemented a basic form with a button and a picture box and a List collection. I add pages to the collection as they're ready from the HtmlPaginator and use the button to add the next image to the picturebox.

The magic numbers are your desired width and height. I used 800x600 but you probably have different dimensions you want.

The downside here is you're still waiting for the WebBrowser to render the HTML but I really don't see how an alternate solution is going to reduce that time - something has to interpret and draw the HTML in the first place. Write your own web browser I guess. :)

I did try playing with IViewObject.Draw to see if I could just have it render the page frames directly rather than have the cropping loop, but it wasn't working for me.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文