使用WPF显示流式富文本

我有一个WPF应用程序,它通过套接字连接到设备并获取流文本数据(每秒约1条消息)。 然后,该数据显示在UI上。 用户可以创建规则,如“如果数据包含’abc’突出显示行”“…使其变为粗体” ,那么纯文本输出将不会执行,它需要是“丰富”文本。

我目前的解决方案是在我的ViewModel中包含一个包含格式化输出的FlowDocument 。 View有一个FlowDocumentScrollViewer ,它绑定到ViewModel中的FlowDocument。

这样可行,但是当FlowDocument变大(~6,000行)时,性能开始下降。 当前算法将行数限制为10,000,但事情变得更糟,直到应用程序无法使用。 一旦它达到10,000行,然后我为每一行添加一行,导致FlowDocumentScrollViewer获得每个新行的2个更新通知。

我试图找到批量删除的方法(当我们达到10,000行删除最旧的1,000行时),但FlowDocument上没有批量删除。 循环1,000次并执行删除会导致1,000个更新通知并锁定UI。

这是我的问题,这是我的问题:

使用WPF显示流式富文本内容的最佳方法是什么? 我每秒得到~1条消息,每条消息大约150个字符,我想保留最后10000条消息。 我是以错误的方式来做这件事的吗? 是否有其他控件/对象表现更好?

编辑 :这里有一些更多的要求

  • 需要能够打印输出文本
  • 需要能够选择并复制输出文本,以便将其粘贴到另一个文档中

性能崩溃似乎是由FlowDocument中的大量块引起的。 对于收到的每条消息,我创建了一个Run,将该run添加到Paragraph并将该段添加到文档中。

我更改了算法,所以现在创建一个段落,然后向该段落添加250个运行,然后创建一个新的段落…添加250个运行……依此类推。 这实质上将块数减少了一半。

当达到最大行数(10,000)时,这也有额外的好处。 我可以删除最旧的段落,并立即删除最旧的250行,而不是为每个添加的新行删除一行(并固定CPU)。

这种相对简单的改变使性能在可接受的范围内。 而不是固定CPU并锁定UI,现在CPU保持相对较低,峰值约为15%。

由于能够在列等中显示内容,FlowDocumentScrollViewers可能会有开销。是否有正常WPF RichTextBox不起作用的原因? 另外,你有.NET 3.5 SP1吗? 以下链接表明SP1中的FlowDocuments有很大的性能改进: http : //social.msdn.microsoft.com/Forums/en-US/wpf/thread/a116da54-ce36-446a-8545-3f34e9b9038d 。

这个想法使事情变得非常复杂,但我的想法是为每个消息创建一个查看器,并且只创建显示当前可见消息所需的尽可能多的查看器。 我认为VirtualizingStackPanel控件是一个很好的工具来管理它。 我在这里找到了一个描述VirtualizingStackPanel实现的系列文章。

显然,这意味着将消息缓冲区维护在单独的数据结构中。

编辑:我刚刚意识到标准的ListBox控件在其实现中使用了VirtualizingStackPanel。 考虑到这一点,我修改后的建议是:

  1. 创建一个数据结构以包含每条消息的来源。
  2. 在数据结构上创建一个属性,该属性从消息源“动态”创建FlowDocument
  3. 将ListBox绑定到所述数据结构的集合。
  4. 使用FlowDocumentScrollViewer为ListBox定义ItemTemplate,其中Document属性绑定到上述数据结构的属性。

编辑2:关于打印/缩放:我对WPF中的打印帮助不大(可能涉及VisualBrush的东西?),但缩放应该很容易。 我创建了一个ZoomListBox来测试这个想法。 XAML看起来像这样:

           

而背后的代码是这样的:

 public partial class ZoomListBox { public ZoomListBox() { this.InitializeComponent(); } public double Zoom { get { return (double)GetValue(ZoomProperty); } set { SetValue(ZoomProperty, value); } } // Using a DependencyProperty as the backing store for Zoom. This enables animation, styling, binding, etc... public static readonly DependencyProperty ZoomProperty = DependencyProperty.Register("Zoom", typeof(double), typeof(ZoomListBox), new UIPropertyMetadata(1.0)); } 

以及使用它的一个例子: