在 WPF 中显示一段文本
我有一个准系统的 WPF 应用程序,它有大约 1 兆的 ASCII 文本要显示。我最初将 TextBlock 放在 ScrollViewer 的 WrapPanel 中。当我调整窗口大小时,它正确地滚动并调整大小,但速度非常慢!我需要更快的东西。
因此,我将文本放入 FormattedText 中,并使用自定义控件呈现该文本。这速度快得多,但没有调整大小。所以我调整了自定义控件的大小。但每秒重绘次数太多,所以我只每 100 毫秒重绘一次。
好多了。渲染和调整大小仍然不是很好,但比以前好多了。但我失去了滚动。
最终我需要一个能做很多事情的解决方案 - 但现在我正在尝试有一个能做一点事情的解决方案:显示文本内存、换行、有滚动条并且高性能。最终,我希望它能够缩放到一大段文本,具有内联颜色,部分文本的一些鼠标悬停/单击事件...
我怎样才能使FormattedText(或者更准确地说,DrawingVisual)具有垂直滚动条?
这是我的 FrameworkElement,显示了我的 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];
}
}
}
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];
}
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
对于简单文本,只读
TextBox
非常好。对于更复杂的问题,您可以使用 FlowDocuments(可以托管在 FlowDocumentScrollViewer 中),TextBlocks 还托管 流动内容,但不适合大量内容。MSDN:
For simple text a readonly
TextBox
is pretty good. For more complex matters you can useFlowDocuments
(which can be hosted in aFlowDocumentScrollViewer
),TextBlocks
also host flow content but are not intended for larger amounts.MSDN: