2009-04-07 2 views
3

私は、ソケット経由でデバイスに接続し、ストリーミングテキストデータを取得するWPFアプリケーションを用意しています(毎秒約1メッセージ)。このデータはUIに表示されます。ユーザーはのようなルールを作成することができます。 "データに 'abc'が含まれている場合、"または"の文字列を太字にして" "にすると、プレーンテキスト出力はできません。WPFを使用したスト​​リーミングリッチテキストの表示

私の現在の解決策は、フォーマットされた出力を含む私のViewModelにFlowDocumentを持つことです。 Viewには、ViewModelのFlowDocumentにバインドされているFlowDocumentScrollViewerがあります。

これは機能しますが、FlowDocumentが大きくなると(〜6,000行)パフォーマンスが低下し始めます。現在のアルゴリズムでは10,000の行数を上限にしていますが、アプリが使用できないほど問題はさらに悪化します。それが10,000行に達すると、追加されるすべての行の行が削除され、FlowDocumentScrollViewerは新しい行ごとに2つの更新通知を取得します。

バッチ削除の方法を見つけようとしましたが(10,000行に達したときに最も古い1,000行を削除します)、FlowDocumentに一括削除はありません。 1000回ループして削除を実行すると、1,000件の更新通知が表示され、UIがロックされます。私の問題だ

は、ここに私の質問です:

WPFとストリーミングリッチテキストコンテンツを表示するための最良の方法は何ですか?私は毎秒〜1メッセージ、各メッセージは〜150文字です。最後の10000メッセージを残したいと思います。私はこれについて間違った方法をとっていますか?より良いパフォーマンスを発揮する他のコントロール/オブジェクトがありますか?

EDIT:ここではいくつかのより多くの要件

  • が、それは別の文書に貼り付けることができるよう、出力テキストを選択してコピーできるようにする必要があり、出力テキスト
  • を印刷できるようにする必要がありますされています
+0

似たようなシナリオを私のマシンで使用するには、劇的なperf問題(例:10Kの段落を追加するのに2秒未満)が必要です。 FlowDocumentの最後にどのようにコンテンツを追加していますか? – kvb

+0

データが文字列として私に届きます。私は文字列で文字列を作成し、次に文字列で文字列を指定します。それから、私はthis.MyFlowDoc.Blocks.Add(段落)を呼び出します。 –

答えて

5

パフォーマンスの内訳は、に起因すると思われたXAMLは、このようになりますFlowDocument内の多数のブロック。受信したすべてのメッセージに対して、私はRunを作成して、その実行を段落に追加し、その段落を文書に追加していました。

アルゴリズムを変更して段落を作成し、その段落に250個のランを追加し、新しい段落を作成して250個のランを追加します。これは本質的にブロックの数を半分に削減する。

これは、最大行数(10,000)に達すると、さらに利点があります。追加された行ごとに1行を削除するのではなく、最も古い段落を削除するだけで、最も古い250行を即座に削除することができます。

この比較的単純な変更により、パフォーマンスは許容範囲内に十分に収まりました。 CPUを固定してUIをロックするのではなく、CPUが15%前後のスパイクで比較的低く抑えられるようになりました。

0

FlowDocumentScrollViewersには、コンテンツを列などに表示する機能があるため、オーバーヘッドが発生することがあります。通常のWPF RichTextBoxが機能しない理由はありますか?また、.NET 3.5 SP1をお持ちですか?次のリンクは、SP1のFlowDocumentsのパフォーマンスが大幅に向上したことを示しています。http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/a116da54-ce36-446a-8545-3f34e9b9038d

+0

それはもともとはRichTextBoxだったが、私はそれを印刷したりズームしたりするために変更した。無料で。私が行ったテストによれば、パフォーマンスヒットの約半分はFlowDocumentScrollViewerから、残りの半分はFlowDocument自体から取得されます。 –

+0

あなたはどのバージョンのフレームワークを使用していますか?さらにテキストの最適化のヒントについては、http://msdn.microsoft.com/en-us/library/bb613560.aspxを参照してください。 – kvb

+0

私は3.5 SP1を使用しています –

0

このアイデアはかなり複雑ですが、私の考えは、メッセージごとに1つのビューアを作成し、現在表示されているメッセージを表示するのに必要なだけのビューアを作成することです。私はVirtualizingStackPanelコントロールがこれを管理する良いツールだと思います。私は、VirtualizingStackPanel hereの実装を説明するシリーズを見つけました。明らかに、これは、メッセージバッファを別個のデータ構造に維持することを意味する。

EDIT:私は、標準のListBoxコントロールがその実装でVirtualizingStackPanelを使用していることに気付きました。これを念頭に置いて、私の修正提案は次のとおりです。

  1. 各メッセージの送信元を含むデータ構造を作成します。
  2. "オンザフライ"でメッセージソースからFlowDocumentを作成するプロパティを作成します。
  3. このデータ構造のコレクションにListBoxをバインドします。
  4. Documentプロパティが上記のデータ構造のプロパティにバインドされているFlowDocumentScrollViewerを持つListBoxのItemTemplateを定義します。

EDIT 2:印刷/ズームに関して:(?多分、たVisualBrushを含むものを)私は、WPFでの印刷に多くのお手伝いをすることはできませんが、ズームを行うのはかなり簡単なはずです。このアイデアをテストするためにZoomListBoxを作成しました。

<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> 
+0

興味深いアイデア、 "ボックスの外"を考えてくれてありがとう。私はそのようなことを調べますが、出力領域からデータを選択/コピーできるようにする必要があります。また、今では印刷とズームを無料で行っているので、その解決策を考え出す必要があります。 –

関連する問題