用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。 考虑到这一点,我的修改建议是:

  • 创建一个数据结构来包含每条消息的来源。
  • 在数据结构上创建一个属性,该属性从消息源“实时”创建FlowDocument
  • 将ListBox绑定到所述数据结构的集合。
  • 使用FlowDocumentScrollViewer定义ListBox的ItemTemplate,其中Document属性绑定到上述数据结构的属性。
  • 编辑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?