我正在尝试在WPF应用程序中重现某些纸张表单的布局。文本框的标签应与文本框的内容“内联”,而不是像普通Windows窗体一样“外部”。因此,使用Xxxxxx标签:
+-----------------------------+
| Xxxxxx: some text written |
| in the multiline input. |
| |
| another paragraph continues |
| without indentation. |
| |
| |
+-----------------------------+
Xxxxxx无法编辑,如果用户选择文本框的所有内容,标签必须保持未选中状态,我需要能够分别设置标签的文本颜色/格式,如果没有文字文本框,但它有焦点,插入符号应该在标签后面闪烁,我需要文本框中的文本基线和标签排列。
我尝试过的一个解决方案是将一个文本块部分放在输入上,然后使用文本缩进来缩进可编辑文本,尽管这会导致以下段落出现问题,因为它们也是缩进的。我不确定如何缩进第一段。它需要一些摆弄才能使文本排成一行 - 更可靠的设置将是理想的。
那么,有关如何设置它的任何建议吗?
由于
答案 0 :(得分:2)
好吧,我可以提出一种有点过时的方法。
首先,请注意您可以将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;
}
}