好吧,我可以建议一种有点老套的方法。
首先,请注意您可以将 UI 元素放入 FlowDocument。所以这使得这样的事情成为可能:
<RichTextBox>
<FlowDocument>
<Paragraph>
<InlineUIContainer>
<TextBlock>This is your label: </TextBlock>
</InlineUIContainer>
<Run>And this is the editable text.</Run>
</Paragraph>
</FlowDocument>
</RichTextBox>
现在问题变成了阻止用户编辑InlineUIContainer。这确实是两个问题。
第一个问题是阻止用户选择它。为此,您必须处理SelectionChanged 事件。在事件中,找到RTB的文档中的第一个InlineUIContainer,如果Selection.Start在那个之前,则更改它。
private void RichTextBox_SelectionChanged(object sender, RoutedEventArgs e)
{
RichTextBox rtb = (RichTextBox) sender;
if (rtb == null) return;
InlineUIContainer c = rtb.Document
.Blocks
.Where(x => x is Paragraph)
.Cast<Paragraph>()
.SelectMany(x => x.Inlines)
.Where(x => x is InlineUIContainer)
.Cast<InlineUIContainer>()
.FirstOrDefault();
if (c == null) return;
if (rtb.Selection.Start.CompareTo(c.ElementEnd) < 0)
{
rtb.Selection.Select(c.ElementEnd, rtb.Selection.End);
}
}
可能有一种更简单的方法来制定 LINQ 查询,但我有点喜欢它。这不是 100% 完美的。如果您在文本内选择并向左拖动TextBlock,它将失去选择。我确信这可以修复。但它工作得很好。它甚至可以处理用户使用箭头键导航的情况。
就这么多,您几乎可以一路走到那里。不过,另一件可能会让您感到困惑的事情是,如果用户将光标放在文本的最开头并按 BACKSPACE。
需要类似的处理:将插入符号的位置与第一个 InlineUIElement 的末尾进行比较,如果插入符号位于该位置,则取消 BACKSPACE(通过将事件标记为已处理):
private void RichTextBox_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key != Key.Back)
{
return;
}
RichTextBox rtb = (RichTextBox)sender;
if (rtb == null) return;
InlineUIContainer c = rtb.Document
.Blocks
.Where(x => x is Paragraph)
.Cast<Paragraph>()
.SelectMany(x => x.Inlines)
.Where(x => x is InlineUIContainer)
.Cast<InlineUIContainer>()
.FirstOrDefault();
if (c == null) return;
if (rtb.CaretPosition.CompareTo(c.ElementEnd.GetInsertionPosition(LogicalDirection.Forward)) <= 0)
{
e.Handled = true;
}
}