用WPF显示流式富文本
我有一个WPF应用程序,通过套接字连接到设备,并获取流文本数据(每秒约1条消息)。 这些数据然后显示在用户界面上。 用户可以创建像“如果数据包含'abc'突出显示行”或“...使其变为粗体”这样的规则,那么纯文本输出将不会执行,它需要是“丰富”文本。
我目前的解决方案是在我的ViewModel中包含格式化输出的FlowDocument。 该视图具有绑定到ViewModel中的FlowDocument的FlowDocumentScrollViewer。
这是有效的,但是当FlowDocument变大(约6000行)时,性能开始下降。 目前的算法将行数限制在10,000,但事情变得更糟,直到应用程序无法使用。 一旦它达到10,000行,然后我删除每行的添加行,导致FlowDocumentScrollViewer获取每个新行的2个更新通知。
我试图找到一种批量删除的方法(当我们达到10,000行删除最早的1000行时),但是FlowDocument上没有批量删除。 循环1,000次,并执行删除操作,导致1000次更新通知并锁定用户界面。
这是我的问题,这是我的问题:
用WPF显示富文本内容的最佳方式是什么? 我每秒收到1条消息,每条消息约150个字符,我想保留最后的10000条消息。 我是否以这种错误的方式去做? 还有其他控制/对象会表现更好吗?
编辑 :这里有一些更多的要求
性能崩溃似乎是由FlowDocument中的大量块造成的。 对于收到的每条消息,我都创建了一个Run,并将该Run添加到段落中,并将该段落添加到文档中。
我改变了算法,现在创建了一个段落,然后在该段落中增加了250个运行,然后创建了一个新的段落...增加了250个运行...等等。 这基本上将块的数量减半。
当我达到最大行数(10,000)时,这也有额外的好处。 我可以删除最老的段落,并立即删除最早的250行,而不是为添加的每条新行删除一行(并且钉住CPU)。
这种相对简单的改变使得性能很好地在可接受的范围内。 与钉住CPU并锁定用户界面不同,现在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控件是一个很好的工具来管理这个。 我在这里找到了一个描述VirtualizationStackPanel实现的系列。
显然,这意味着将消息缓冲区保持在一个单独的数据结构中。
编辑:我只是意识到标准ListBox控件在其实现中使用VirtualizingStackPanel。 考虑到这一点,我的修改建议是:
编辑2:关于打印/缩放:我无法帮助你在WPF中打印(涉及VisualBrush,也许?),但缩放应该很容易。 我创建了一个ZoomListBox来测试这个想法。 XAML看起来像这样:
<ListBox
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:Class="Test.ZoomListBox"
d:DesignWidth="640" d:DesignHeight="480"
x:Name="ThisControl">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel IsItemsHost="True">
<VirtualizingStackPanel.LayoutTransform>
<ScaleTransform ScaleX="{Binding ElementName=ThisControl, Path=Zoom}" ScaleY="{Binding ElementName=ThisControl, Path=Zoom}" />
</VirtualizingStackPanel.LayoutTransform>
</VirtualizingStackPanel>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
而后面的代码是这样的:
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));
}
以及使用它的一个例子:
<Grid x:Name="LayoutRoot">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<l:ZoomListBox x:Name="ZoomList">
<Button>Foo</Button>
<Button>Foo</Button>
<Button>Foo</Button>
<Button>Foo</Button>
<Button>Foo</Button>
<Button>Foo</Button>
<Button>Foo</Button>
</l:ZoomListBox>
<Slider Grid.Row="1" Value="{Binding ElementName=ZoomList, Path=Zoom}" Minimum="0.5" Maximum="5" />
</Grid>
链接地址: http://www.djcxy.com/p/50511.html
上一篇: Displaying streaming rich text with WPF
下一篇: How to automatically select all text on focus in WPF TextBox?