web-dev-qa-db-ja.com

プログラムでWPFRichTextBox(FlowDocument)のテキストの範囲を選択します

このWPFRichTextBoxがあり、プログラムで指定された範囲の文字/単語を選択して強調表示したいと思います。私はこれを試しましたが、おそらくいくつかの隠されたFlowDocumentタグなどを考慮していないために機能しません。たとえば、文字3〜8を選択したいのですが、2〜6が選択されます):

var start = MyRichTextBox.Document.ContentStart;
var startPos = start.GetPositionAtOffset(3);
var endPos = start.GetPositionAtOffset(8);
var textRange = new TextRange(startPos,endPos);
textRange.ApplyPropertyValue(TextElement.ForegroundProperty,
    new SolidColorBrush(Colors.Blue));
textRange.ApplyPropertyValue(TextElement.FontWeightProperty, 
    FontWeights.Bold);

RichTextBoxの処理が思ったより少し難しいことに気づきました:)

更新:MSDNフォーラムでいくつかの回答を得ました: このスレッド ここで "dekurver" seid:

指定するオフセットは、文字オフセットではなく、シンボルオフセットです。あなたがする必要があるのは、テキストに隣接していることがわかっているTextPointerを取得することです。次に、文字オフセットを追加できます。

そして「LesterLobo」は言った:

特定のテキストのすべての外観に適用するには、段落とインラインをループして[次へ]を見つけ、次にそれらのオフセットをループで見つける必要があります。テキストを編集すると移動しますが、ハイライトはテキストではなくオフセットに関連付けられているため移動しないことに注意してください。ただし、カスタムランを作成して、そのハイライトを提供することはできます...

誰かがFlowDocumentsの周りの方法を知っていれば、これのサンプルコードを見るのが大好きです...

[〜#〜]編集[〜#〜]Kratzのバージョンを入手しましたVBコードが機能しているようです、見た目このような:

private static TextPointer GetPoint(TextPointer start, int x)
{
    var ret = start;
    var i = 0;
    while (i < x && ret != null)
    {
        if (ret.GetPointerContext(LogicalDirection.Backward) == 
TextPointerContext.Text ||
            ret.GetPointerContext(LogicalDirection.Backward) == 
TextPointerContext.None)
            i++;
        if (ret.GetPositionAtOffset(1, 
LogicalDirection.Forward) == null)
            return ret;
        ret = ret.GetPositionAtOffset(1, 
LogicalDirection.Forward);
    }
    return ret;
}

そして私はそれを次のように使用します:

Colorize(item.Offset, item.Text.Length, Colors.Blue);

private void Colorize(int offset, int length, Color color)
{
    var textRange = MyRichTextBox.Selection;
    var start = MyRichTextBox.Document.ContentStart;
    var startPos = GetPoint(start, offset);
    var endPos = GetPoint(start, offset + length);

    textRange.Select(startPos, endPos);
    textRange.ApplyPropertyValue(TextElement.ForegroundProperty, 
new SolidColorBrush(color));
    textRange.ApplyPropertyValue(TextElement.FontWeightProperty, 
FontWeights.Bold);
}
17
Johan Danforth
Public Function GoToPoint(ByVal start As TextPointer, ByVal x As Integer) As TextPointer
    Dim out As TextPointer = start
    Dim i As Integer = 0
    Do While i < x
        If out.GetPointerContext(LogicalDirection.Backward) = TextPointerContext.Text Or _
             out.GetPointerContext(LogicalDirection.Backward) = TextPointerContext.None Then
            i += 1
        End If
        If out.GetPositionAtOffset(1, LogicalDirection.Forward) Is Nothing Then
            Return out
        Else
            out = out.GetPositionAtOffset(1, LogicalDirection.Forward)
        End If


    Loop
    Return out
End Function

これを試してください。これにより、指定された文字オフセットのテキストポインタが返されます。 (VBで申し訳ありませんが、それが私が取り組んでいることです...)

11
Kratz

それを試してください:

var textRange = MyRichTextBox.Selection;
var start = MyRichTextBox.Document.ContentStart;
var startPos = start.GetPositionAtOffset(3);
var endPos = start.GetPositionAtOffset(8);
textRange.Select(startPos, endPos);
textRange.ApplyPropertyValue(TextElement.ForegroundProperty, new SolidColorBrush(Colors.Blue));
textRange.ApplyPropertyValue(TextElement.FontWeightProperty, FontWeights.Bold);
8
Thomas Levesque

KratzVBによって投稿されたソリューションを使用してみましたが、改行を無視していることがわかりました。\rおよび\ nシンボルをカウントする場合は、次のコードが機能するはずです。

private static TextPointer GetPoint(TextPointer start, int x)
{

        var ret = start;
        var i = 0;
        while (ret != null)
        {
            string stringSoFar = new TextRange(ret, ret.GetPositionAtOffset(i, LogicalDirection.Forward)).Text;
            if (stringSoFar.Length == x)
                    break;
            i++;
            if (ret.GetPositionAtOffset(i, LogicalDirection.Forward) == null)
                return ret.GetPositionAtOffset(i-1, LogicalDirection.Forward)

        }
        ret=ret.GetPositionAtOffset(i, LogicalDirection.Forward);
        return ret;
}
7
cave_dweller

Cave_dwellerのバージョンに基づく私のバージョン

_private static TextPointer GetPositionAtCharOffset(TextPointer start, int numbertOfChars)
{
    var offset = start;
    int i = 0;
    string stringSoFar="";
    while (stringSoFar.Length < numbertOfChars)
    {
        i++;
        TextPointer offsetCandidate = start.GetPositionAtOffset(
                i, LogicalDirection.Forward);

        if (offsetCandidate == null)
            return offset; // ups.. we are to far

        offset = offsetCandidate;
        stringSoFar = new TextRange(start, offset).Text;
    }

    return offset;
}
_

一部の文字を省略するには、ループ内に次のコードを追加します。

_stringSoFar = stringSoFar.Replace("\r\n", "")
                         .Replace(" ", "")
_

これの代わりに(遅い):

_var startPos = GetPoint(start, offset);
var endPos = GetPoint(start, offset + length);
_

あなたはこれを(より速く)するべきです

_var startPos = GetPoint(start, offset);
var endPos = GetPoint(startPos, length);
_

または、TextRangeを取得するための別のメソッドを作成します。

_private static TextRange GetTextRange(TextPointer start, int startIndex, int length)
{
    var rangeStart = GetPositionAtCharOffset(start, startIndex);
    var rangeEnd = GetPositionAtCharOffset(rangeStart, length);
    return new TextRange(rangeStart, rangeEnd);
}
_

Select()ingなしでテキストをフォーマットできるようになりました。

_var range = GetTextRange(Document.ContentStart, 3, 8);
range.ApplyPropertyValue(
    TextElement.BackgroundProperty, 
    new SolidColorBrush(Colors.Aquamarine));
_
2
CoperNick

長い間、この問題に対する許容可能なパフォーマンスソリューションを備えたソリューションを見つけることができませんでした。次のサンプルは、私の場合、最高のパフォーマンスで動作します。それが誰かにも役立つことを願っています。

TextPointer startPos = rtb.Document.ContentStart.GetPositionAtOffset(searchWordIndex, LogicalDirection.Forward);
startPos = startPos.CorrectPosition(searchWord, FindDialog.IsCaseSensitive);
if (startPos != null)
{
    TextPointer endPos = startPos.GetPositionAtOffset(textLength, LogicalDirection.Forward);
    if (endPos != null)
    {
         rtb.Selection.Select(startPos, endPos);
    }
}

public static TextPointer CorrectPosition(this TextPointer position, string Word, bool caseSensitive)
{
   TextPointer start = null;
   while (position != null)
   {
       if (position.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.Text)
       {
           string textRun = position.GetTextInRun(LogicalDirection.Forward);

           int indexInRun = textRun.IndexOf(Word, caseSensitive ? StringComparison.InvariantCulture : StringComparison.InvariantCultureIgnoreCase);
           if (indexInRun >= 0)
           {
               start = position.GetPositionAtOffset(indexInRun);
               break;
           }
       }

       position = position.GetNextContextPosition(LogicalDirection.Forward);
   }

   return start; 
}
2

ちなみに(そしてこれは私以外のすべての人にとって学術的かもしれません)、FocusManager.IsFocusScope = "True"をRichTextBoxのコンテナ、たとえばグリッドに設定した場合、

<Grid FocusManager.IsFocusScope="True">...</Grid>

次に、ApplyPropertyValueを2回呼び出さなくても、Johan DanforthのColorizeメソッドを使用できるはずです。また、RichTextBoxは、デフォルトの選択であるBackgroundとForegroundを使用して選択を強調表示する必要があります。

private void Colorize(int offset, int length, Color color)
{
    var textRange = MyRichTextBox.Selection;
    var start = MyRichTextBox.Document.ContentStart;
    var startPos = GetPoint(start, offset);
    var endPos = GetPoint(start, offset + length);

    textRange.Select(startPos, endPos);
}

RichTextBoxで試したことはありませんが、FlowDocumentReaderでテキストボックスの検索をテンプレート化する場合は非常にうまく機能します。念のために設定することもできます

<RichTextBox FocusManager.FocusedElement="{Binding RelativeSource={RelativeSource Self}}">...</RichTextBox>

richTextBoxがフォーカススコープ内にフォーカスを持っていることを確認します。

もちろん、これの欠点は、ユーザーがRichTextBox内でクリックまたは選択を実行すると、選択が消えることです。

1
Benjamin
    private void SelectText(int start, int length)
    {
        TextRange textRange = new TextRange(richTextBox.Document.ContentStart, richTextBox.Document.ContentEnd);
        TextPointer pointerStart = textRange.Start.GetPositionAtOffset(start, LogicalDirection.Forward);
        TextPointer pointerEnd = textRange.Start.GetPositionAtOffset(start + length, LogicalDirection.Backward);

        richTextBox.Selection.Select(pointerStart, pointerEnd);
    }
0
bully
    private TextPointer GetPoint(TextPointer start, int pos)
    {
        var ret = start;
        int i = 0;
        while (i < pos)
        {
            if (ret.GetPointerContext(LogicalDirection.Forward) ==
    TextPointerContext.Text)
                i++;
            if (ret.GetPositionAtOffset(1, LogicalDirection.Forward) == null)
                return ret;
            ret = ret.GetPositionAtOffset(1, LogicalDirection.Forward);
        }
        return ret;
    }
0