您好我正在开发一个涉及使用 RichTextBox 的WPF项目(使用/ C#)。我目前已完成大部分工作,但需要帮助通过鼠标选择获得多个块,例如设置TextAlignment或Margin。供您参考,以下内容将通过为 RichTextBox 插入符设置TextPointer,然后迭代Document.Blocks以获取一个 一个 块。个别街区。但我希望有一种方法可以让我获得的不仅仅是一个块。有人可以提供更多关于如何选择 多个 块(不同行上的每个块)的见解吗?
// XAML
<RichTextBox Name="rtb" HorizontalAlignment="Left">
<FlowDocument>
<Paragraph TextAlignment="Left" Margin="0.0">
<Run Text="Hello"/>
</Paragraph>
<Paragraph TextAlignment="Right" Margin="0.0">
<Run Text="World"/>
</Paragraph>
<Paragraph TextAlignment="Left" Margin="1.0">
<Run Text="Hello"/>
</Paragraph>
<Paragraph TextAlignment="Right" Margin="1.0">
<Run Text="World"/>
</Paragraph>
</FlowDocument>
</RichTextBox>
// Code Behind
var curCaret = rtb.CaretPosition;
Block curBlock = rtb.Document.Blocks.Where(x => x.ContentStart.CompareTo(curCaret) == -1 && x.ContentEnd.CompareTo(curCaret) == 1).FirstOrDefault();
答案 0 :(得分:1)
如果您只是想找到与当前Blocks
重叠的顶级selection range,您可以这样做:
public static class FlowDocumentHelper
{
public static bool Overlaps(this TextElement element, TextPointer start, TextPointer end)
{
return element.ContentEnd.CompareTo(start) > 0 && element.ContentStart.CompareTo(end) < 0;
}
}
然后
var blocks = richTextBox.Document.Blocks.Where(block => block.Overlaps(richTextBox.Selection.Start, richTextBox.Selection.End));
但是,如果您正在寻找与当前选择范围重叠的paragraphs,那么这将无效,因为它们可能深埋在文本元素层次结构中,例如figure内{在list内的table内{3}}。要实际发现与当前选择范围重叠的段落,您必须递归地遍历块的层次结构,而WPF没有提供直接的方法来执行此操作(尽管它们内部肯定有这些信息!)。
因此,可以为可能包含子节点的所有可能section类手工创建递归迭代器,或者遍历文档中的所有TextElement
对象并使用它们来发现层次结构。以下使用后一种策略:
public static class FlowDocumentHelper
{
public static IEnumerable<TTextElement> WalkTextRange<TTextElement>(this FlowDocument doc, TextPointer start, TextPointer end) where TTextElement : TextElement
{
var lastVisited = new Dictionary<int, TTextElement>();
foreach (var stack in doc.WalkTextHierarchy())
{
var element = stack.Peek() as TTextElement;
if (element != null)
{
TTextElement previous;
if (!lastVisited.TryGetValue(stack.Count - 1, out previous) || previous != element)
{
if (element.Overlaps(start, end))
yield return element;
lastVisited[stack.Count - 1] = element;
}
}
}
}
public static bool Overlaps(this TextElement element, TextPointer start, TextPointer end)
{
return element.ContentEnd.CompareTo(start) > 0 && element.ContentStart.CompareTo(end) < 0;
}
public static IEnumerable<Stack<DependencyObject>> WalkTextHierarchy(this FlowDocument doc)
{
if (doc == null)
throw new ArgumentNullException();
var stack = new Stack<DependencyObject>();
// Keep a TextPointer for FlowDocument.ContentEnd handy, so we know when we're done.
TextPointer docEnd = doc.ContentEnd;
// Keep going until the TextPointer is equal to or greater than ContentEnd.
for (var iterator = doc.ContentStart;
(iterator != null) && (iterator.CompareTo(docEnd) < 0);
iterator = iterator.GetNextContextPosition(LogicalDirection.Forward))
{
var parent = iterator.Parent;
// Identify the type of content immediately adjacent to the text pointer.
TextPointerContext context = iterator.GetPointerContext(LogicalDirection.Forward);
switch (context)
{
case TextPointerContext.ElementStart:
case TextPointerContext.EmbeddedElement:
case TextPointerContext.Text:
PushElement(stack, parent);
yield return stack;
break;
case TextPointerContext.ElementEnd:
break;
default:
throw new System.Exception("Unhandled TextPointerContext " + context.ToString());
}
switch (context)
{
case TextPointerContext.ElementEnd:
case TextPointerContext.EmbeddedElement:
case TextPointerContext.Text:
PopToElement(stack, parent);
break;
case TextPointerContext.ElementStart:
break;
default:
throw new System.Exception("Unhandled TextPointerContext " + context.ToString());
}
}
}
static int IndexOf<T>(Stack<T> source, T value)
{
int index = 0;
var comparer = EqualityComparer<T>.Default;
foreach (T item in source)
{
if (comparer.Equals(item, value))
return index;
index++;
}
return -1;
}
static void PopToElement<T>(Stack<T> stack, T item)
{
for (int index = IndexOf(stack, item); index >= 0; index--)
stack.Pop();
}
static void PushElement<T>(Stack<T> stack, T item)
{
PopToElement(stack, item);
stack.Push(item);
}
}
然后
var paragraphs = richTextBox.Document.WalkTextRange<Paragraph>(richTextBox.Selection.Start, richTextBox.Selection.End);
(注意 - 经过适度测试的非生产代码。)
最后,如果你想允许你的用户按Ctrl键选择多个不相邻的文本范围,就像在Word中所描述的那样:TextPointer
,那么我认为你已经出局了好运RichTextBox似乎不支持此用户交互。