Tom Ritter
Tom Ritter

Reputation: 101400

Displaying about a Meg of Text in WPF

I have a barebones WPF app that has about a Meg of ASCII text to display. I initially put a TextBlock in a WrapPanel in a ScrollViewer. This correctly scrolled and resized when I resized the window, but it was super slow! I needed something faster.

So I put the text in FormattedText, and rendered that using a custom control. That was much faster, but it didn't resize. So I made my custom control resize. But it would ReDraw too many times a second, so I made it only redraw every 100ms.

Much better. Rendering and Resizing still isn't great but it's much better than it was. But I lost scrolling.

Eventually I need a solution that does a lot - but for now I'm trying to have a solution that does a little: show a mem of text, wrap, have a scrollbar, and be performant. Eventually, I'd like it to scale to a gig of text, have colors inline, some mouseover/click events for portions of the text...

How can I make FormattedText (or perhaps more accurately, a DrawingVisual) have a Vertical Scrollbar?

Here's my FrameworkElement that shows my FormattedText:

using System;
using System.Windows;
using System.Windows.Media;

namespace Recall
{
    public class LightweightTextBox : FrameworkElement
    {
        private VisualCollection _children;
        private FormattedText _formattedText;
        private System.Threading.Timer _resizeTimer;
        private const int _resizeDelay = 100;

        public double MaxTextWidth
        {
            get { return this._formattedText.MaxTextWidth; }
            set { this._formattedText.MaxTextWidth = value; }
        }

        public LightweightTextBox(FormattedText formattedText)
        {
            this._children = new VisualCollection(this);
            this._formattedText = formattedText;

            DrawingVisual drawingVisual = new DrawingVisual();
            DrawingContext drawingContext = drawingVisual.RenderOpen();
            drawingContext.DrawText(this._formattedText, new Point(0, 0));
            drawingContext.Close();
            _children.Add(drawingVisual);

            this.SizeChanged += new SizeChangedEventHandler(LightweightTextBox_SizeChanged);
        }

        void LightweightTextBox_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            this.MaxTextWidth = e.NewSize.Width;

            if (_resizeTimer != null)
                _resizeTimer.Change(_resizeDelay, System.Threading.Timeout.Infinite);
            else
                _resizeTimer = new System.Threading.Timer(new System.Threading.TimerCallback(delegate(object state)
                {
                    ReDraw();
                    if (_resizeTimer == null) return;
                    _resizeTimer.Dispose();
                    _resizeTimer = null;
                }), null, _resizeDelay, System.Threading.Timeout.Infinite);
        }

        public void ReDraw()
        {
            this.Dispatcher.Invoke((Action)(() =>
                {
                    var dv = _children[0] as DrawingVisual;
                    DrawingContext drawingContext = dv.RenderOpen();
                    drawingContext.DrawText(this._formattedText, new Point(0, 0));
                    drawingContext.Close();
                }));
        }

        //===========================================================
        //Overrides

        protected override int VisualChildrenCount { get { return _children.Count; } }

        protected override Visual GetVisualChild(int index)
        {
            if (index < 0 || index >= _children.Count)
                throw new ArgumentOutOfRangeException();

            return _children[index];
        }
    }
}

Upvotes: 1

Views: 261

Answers (1)

brunnerh
brunnerh

Reputation: 185553

For simple text a readonly TextBox is pretty good. For more complex matters you can use FlowDocuments (which can be hosted in a FlowDocumentScrollViewer), TextBlocks also host flow content but are not intended for larger amounts.

MSDN:

TextBlock is not optimized for scenarios that need to display more than a few lines of content; for such scenarios, a FlowDocument coupled with an appropriate viewing control is a better choice than TextBlock, in terms of performance. After TextBlock, FlowDocumentScrollViewer is the next lightest-weight control for displaying flow content, and simply provides a scrolling content area with minimal UI. FlowDocumentPageViewer is optimized around "page-at-a-time" viewing mode for flow content. Finally, FlowDocumentReader supports the richest set functionality for viewing flow content, but is correspondingly heavier-weight.

Upvotes: 2

Related Questions