C# 以编程方式选择 WPF RichTextBox (FlowDocument) 中的文本范围
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1454440/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Select Range of Text in WPF RichTextBox (FlowDocument) Programmatically
提问by Johan Danforth
I have this WPF RichTextBox and I want to programmatically select a given range of letters/words and highlight it. I've tried this, but it doesn't work, probably because I'm not taking into account some hidden FlowDocument tags or similar. For example, I want to select letters 3-8 but 2-6 gets selected):
我有这个 WPF RichTextBox,我想以编程方式选择给定范围的字母/单词并突出显示它。我试过这个,但它不起作用,可能是因为我没有考虑一些隐藏的 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);
I've realised RichTextBox handling is a bit trickier than I thought :)
我意识到 RichTextBox 的处理比我想象的要复杂一些 :)
Update: I got a few answers on the MSDN forums: This threadwhere "dekurver" seid:
更新:我在 MSDN 论坛上得到了一些答案:“dekurver” seid 所在的线程:
The offsets you're specifying are not character offsets but symbol offsets. What you need to do is get a TextPointer that you know is adjacent to text, then you can add character offsets.
您指定的偏移量不是字符偏移量而是符号偏移量。您需要做的是获得一个您知道与文本相邻的 TextPointer,然后您可以添加字符偏移量。
And "LesterLobo" said:
“LesterLobo”说:
you will need to loop through the paragraphs and inlines to find the Next and then their offsets in a loop to apply for all appearances of the specific text. note that when you edit your text would move but your highlight wouldnt move as its associated with the offset not the text. You could however create a custom run and provide a highlight for it...
您将需要遍历段落和内联以找到 Next,然后在循环中找到它们的偏移量以应用特定文本的所有外观。请注意,当您编辑文本时,您的文本会移动,但您的突出显示不会移动,因为它与偏移量而非文本相关联。但是,您可以创建自定义运行并为其提供突出显示...
Would still LOVE to see some sample code for this if someone knows their way around FlowDocuments...
如果有人知道他们在 FlowDocuments 周围的方式,仍然希望看到一些示例代码......
EDITI got a version of Kratz VB code working, it looks like this:
编辑我有一个版本的 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;
}
And I use it like this:
我像这样使用它:
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);
}
采纳答案by Kratz
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
Try this, this should return a text pointer for the given char offset. (Sorry its in VB, but thats what I am working in...)
试试这个,这应该返回给定字符偏移的文本指针。(对不起,它在 VB 中,但这就是我正在从事的工作......)
回答by Thomas Levesque
Try that :
试试看:
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);
回答by Benjamin
Incidentally (and this may be academic to all but myself), if you set FocusManager.IsFocusScope="True" on the container of the RichTextBox, a Grid for example,
顺便说一句(这可能是学术性的,除了我自己),如果你在 RichTextBox 的容器上设置 FocusManager.IsFocusScope="True",例如一个网格,
<Grid FocusManager.IsFocusScope="True">...</Grid>
then you should be able to use Johan Danforth's Colorize method without the two invocations of ApplyPropertyValue, and the RichTextBox ought to use the default selection Background and Foreground to highlight the selection.
那么您应该能够使用 Johan Danforth 的 Colorize 方法而无需两次调用 ApplyPropertyValue,并且 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);
}
Have not tried it with the RichTextBox, but it works quite well when templating a find TextBox in a FlowDocumentReader. Just to be sure you might also set
尚未使用 RichTextBox 尝试过,但在 FlowDocumentReader 中模板化 find TextBox 时效果很好。只是为了确保您还可以设置
<RichTextBox FocusManager.FocusedElement="{Binding RelativeSource={RelativeSource Self}}">...</RichTextBox>
to ensure the RichTextBox has focus within its focus scope.
以确保 RichTextBox 在其焦点范围内具有焦点。
The downside of this, of course, is that if the user clicks or performs a selection within the RichTextBox, your selection disappears.
当然,这样做的缺点是,如果用户在 RichTextBox 中单击或执行选择,您的选择就会消失。
回答by cave_dweller
I tried using the solution posted by KratzVB but found that it was ignoring newlines. If you want to count \r and \n symbols then this code should work:
我尝试使用 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;
}
回答by CoperNick
My version based on cave_dweller's version
我的版本基于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;
}
To omit some characters add this code inside loop:
要省略某些字符,请在循环中添加此代码:
stringSoFar = stringSoFar.Replace("\r\n", "")
.Replace(" ", "")
Instead of this (slow):
而不是这个(慢):
var startPos = GetPoint(start, offset);
var endPos = GetPoint(start, offset + length);
You should do this (faster)
你应该这样做(更快)
var startPos = GetPoint(start, offset);
var endPos = GetPoint(startPos, length);
Or create separate method to get TextRange:
或者创建单独的方法来获取 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);
}
You can now format text without Select()
ing:
您现在可以在不使用Select()
ing 的情况下格式化文本:
var range = GetTextRange(Document.ContentStart, 3, 8);
range.ApplyPropertyValue(
TextElement.BackgroundProperty,
new SolidColorBrush(Colors.Aquamarine));
回答by Paul Solomenchuk
Could not find a solution with acceptable performance solution to this problem for a long time. Next sample works in my case with the highest performance. Hope it will help somebody as well.
很长一段时间都找不到具有可接受性能的解决方案来解决此问题。下一个示例在我的情况下具有最高性能。希望它也能帮助某人。
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;
}
回答by RaccoonProgrammer
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;
}
回答by bully
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);
}