«Быстрое» отображение в DocumentViewer - PullRequest
0 голосов
/ 21 апреля 2011

Я создаю приложение WPF, в котором мне нужно отобразить предварительный просмотр документов, например, что достижимо с помощью DocumentViewer и DocumentPaginator. Однако преобразование отчета в XPS и загрузка его в DocumentViewer оказалось очень медленным, когда отчет большой (как обычный отчет, который мне нужно отобразить).

Это привело меня к мысли, что, возможно, есть какой-то способ начать показ первых нескольких страниц отчета, в то время как остальные страницы «загружаются» в DocumentViewer - в основном загрузка / отображение страниц как они созданы .

Кто-нибудь знает, возможно ли что-то подобное? И, если так, как бы вы предложили мне начать работать? Я потратил несколько часов на то, чтобы найти решение для более быстрого отображения отчета, но ничего не нашел.

Ради полного раскрытия, в этом случае отчет, который мне нужно отобразить, создается в HTML. Я знаю, что мне нужно преобразовать его в XPS, чтобы использовать DocumentViewer, но я привожу это к сведению, потому что, если у кого-то есть быстрый способ отображения HTML, пожалуйста, не стесняйтесь поднять это тоже. Я не могу использовать элемент управления WebBrowser, так как мне нужно, чтобы экран отображался в режиме «Предварительный просмотр». Хороший алгоритм для принятия решения о том, как «разбить на страницы» HTML-сайт, вероятно, приведет меня к решению этой проблемы, а также я смогу создать собственный элемент управления для его отображения. Я бы использовал DocumentPaginator, но тогда выводимый файл - это XPS, а затем я вернулся к проблеме DocumentViewer.

Опять же, любая помощь очень приветствуется. Спасибо!

1 Ответ

1 голос
/ 21 апреля 2011

Хорошо, я думаю, что у меня есть кое-что ...

Еще раз я нашел лучший 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, чтобы посмотреть, могу ли я просто визуализировать фреймы страницы, а не цикл обрезки, но это не сработало для меня.

...