Reputation: 101400
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
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