私は同様の問題を抱えていました - 基本的にレポートを表す多数のプレゼンターに対してテキスト検索を実装しようとしました。レポートはもともと文字列に書き込まれており、FlowDocumentViewer の組み込みを利用しctrlてFいました。これはあまり良くなく、いくつかの奇妙なオプションがありますが、十分でした。
そのようなものが必要な場合は、次のことができます。
<FlowDocumentScrollViewer>
<FlowDocument>
<Paragraph FontFamily="Lucida Console" FontSize="12">
<Run Text="{Binding Content, Mode=OneWay}"/>
</Paragraph>
</FlowDocument>
</FlowDocumentScrollViewer>
レポートはプログラムの残りの部分と同期しており、基本的に編集ごとに変更されるため、レポート全体を毎回再作成する必要があるため、書き直すことにしました。これは非常に遅いことを意味します。必要なビットを更新するモデルに移行することでこれを改善したかったのですが、それを適切な方法で実行できるようにするには、(単なる文字列ではなく) ビュー モデルが必要でした! ただし、レポートを交換する前に検索機能を保持し、「現在の」検索位置を 1 つの色で強調表示し、他の検索ヒットを別の色で強調表示したいと考えました。
これが私のソリューションの簡略版です。そこから派生するクラスはTextBlock
、 Type の依存関係プロパティを追加しますHighlightingInformation
。名前空間と使用法は機密であるため、含めていません。
public class HighlightingTextBlock : TextBlock
{
public static readonly DependencyProperty HighlightingProperty =
DependencyProperty.Register("Highlighting", typeof (HighlightingInformation), typeof (HighlightingTextBlock));
public HighlightingInformation Highlighting
{
get { return (HighlightingInformation)GetValue(HighlightingProperty); }
set { SetValue(HighlightingProperty, value); }
}
public HighlightingTextBlock()
{
AddValueChangedCallBackTo(HighlightingProperty, UpdateText);
}
private void AddValueChangedCallBackTo(DependencyProperty property, Action updateAction)
{
var descriptor = DescriptorFor(property);
descriptor.AddValueChanged(this, (src, args) => updateAction());
}
private DependencyPropertyDescriptor DescriptorFor(DependencyProperty property)
{
return DependencyPropertyDescriptor.FromProperty(property, GetType());
}
private void UpdateText()
{
var highlighting = Highlighting;
if (highlighting == null)
return;
highlighting.SetUpdateMethod(UpdateText);
var runs = highlighting.Runs;
Inlines.Clear();
Inlines.AddRange(runs);
}
}
このクラスをバインドできる型は、テキストの場合に update メソッドを使用し、ハイライトのリストが変更されて Run のリストが更新されます。ハイライト自体は次のようになります。
public class Highlight
{
private readonly int _length;
private readonly Brush _colour;
public int Start { get; private set; }
public Highlight(int start, int length,Brush colour)
{
Start = start;
_length = length;
_colour = colour;
}
private string TextFrom(string currentText)
{
return currentText.Substring(Start, _length);
}
public Run RunFrom(string currentText)
{
return new Run(TextFrom(currentText)){Background = _colour};
}
}
ハイライトの正しいコレクションを生成することは別の問題です。私は基本的に、コンテンツを再帰的に検索するツリーとしてプレゼンターのコレクションを扱うことで解決しました.リーフノードはコンテンツを持ち、他のノードは子を持つノードです. 深さ優先で検索すると、期待どおりの順序で検索できます。次に、基本的に結果のリストの周りにラッパーを記述して、位置を追跡できます。このためのすべてのコードを投稿するつもりはありません-ここでの私の回答は、MVPスタイルでwpfに多色の強調表示をさせる方法を文書化することです.
変更をマルチキャストにする必要がなかったため、ここではINotifyPropertyChanged
orを使用していません (たとえば、1 人のプレゼンターが複数のビューを持っている場合など)。CollectionChanged
最初に、テキスト用のイベント変更通知とリスト用のイベント変更通知を追加することで、これを実行しようとしました (INotifyCollectionChanged
イベントを手動でサブスクライブする必要もあります)。しかし、イベントのサブスクリプションからのメモリ リークが懸念され、テキストとハイライトの更新が同時に行われなかったことが問題でした。
このアプローチの 1 つの欠点は、このコントロールの Text プロパティにバインドしてはならないことです。実際のバージョンでは、人々がこれを行うのを止めるためにいくつかのチェックと例外のスローを追加しましたが、わかりやすくするために例から省略しました!