简体   繁体   English

在DocumentViewer中快速显示

[英]“Fast” Displaying in a 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. 我正在构建一个WPF应用程序,其中需要显示文档预览,例如使用DocumentViewer和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). 但是,事实证明,将报表转换为XPS并将其加载到DocumentViewer时,速度非常慢(因为我需要显示的是普通报表)。

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 . 这使我开始思考,可能有某种方法可以开始显示报表的前几页,而其余页面正被“加载”到DocumentViewer中- 基本上是在创建页面时加载/显示它们

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. 为了完全公开,在这种情况下,我需要显示的报告是用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. 我知道我需要将其转换为XPS才能使用DocumentViewer,但是我提出来是因为如果任何人都有快速的显示HTML的方法,请随时提出。 I can't use a WebBrowser control as I have to have the display in a 'print preview' type of mode. 我不能使用WebBrowser控件,因为我必须使显示处于“打印预览”类型的模式。 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. 一个决定如何“分页” HTML站点的好的算法可能会导致我找到该问题的解决方案,然后我可以创建一个自定义控件来显示它。 I'd use a DocumentPaginator, but then the outputted file is XPS and then I'm back to the DocumentViewer issue. 我将使用DocumentPaginator,但是输出的文件是XPS,然后回到DocumentViewer问题。

Again, any help is greatly appreciated. 同样,任何帮助是极大的赞赏。 Thank you! 谢谢!

Ok, I think I've got something... 好吧,我想我有...

Once again I found a better URL to reference. 我再次找到了一个更好的URL来引用。 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 这个不是直接为我加载的,因此我从Google缓存中获取了它: http ://webcache.googleusercontent.com/search?q=cache:LgceMCkJBrsJ: joshclose.net/%3Fp%3D247

Define the IViewObject interface as described in each article: 定义每篇文章中所述的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);
    }

Create an HtmlPaginator class that screenshots the browser's document (as described) but then crops it into pages / frames: 创建一个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;
        }
    }

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. 我从HtmlPaginator准备好页面后将它们添加到集合中,并使用按钮将下一张图像添加到图片框。

The magic numbers are your desired width and height. 幻数是您所需的宽度和高度。 I used 800x600 but you probably have different dimensions you want. 我使用800x600,但您可能需要不同的尺寸。

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. 缺点是您仍在等待WebBrowser呈现HTML,但我真的不知道替代解决方案如何减少时间-首先必须解释和绘制HTML。 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. 我确实尝试过使用IViewObject.Draw来查看是否可以直接渲染页面框架而不是具有裁剪循环,但这对我来说不起作用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM