带WPF的内联文本框标签

Dou*_*las 5 wpf user-interface textbox richtextbox

我正在尝试重现WPF应用程序中某些纸张表单的布局.文本框的标签应与文本框的内容"内联",而不是像普通Windows窗体一样"外部".因此,使用Xxxxxx标签:

+-----------------------------+
| Xxxxxx: some text written   |
| in the multiline input.     |
|                             |
| another paragraph continues |
| without indentation.        |
|                             |
|                             |
+-----------------------------+
Run Code Online (Sandbox Code Playgroud)

Xxxxxx无法编辑,如果用户选择文本框的所有内容,标签必须保持未选中状态,我需要能够在文本框中没有文本时单独设置标签的文本颜色/格式. ,但它有焦点,插入符号应该在标签后面闪烁,我需要文本框中的文本基线和标签排列.

我尝试过的一个解决方案是将一个文本块部分放在输入上,然后使用文本缩进来缩进可编辑文本,尽管这会导致以下段落出现问题,因为它们也是缩进的.我不确定如何缩进第一段.它需要一些摆弄才能使文本排成一行 - 更可靠的设置将是理想的.

那么,关于如何设置它的任何建议?

谢谢

Rob*_*ney 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>
Run Code Online (Sandbox Code Playgroud)

现在的问题是阻止用户编辑InlineUIContainer. 这确实是两个问题。

第一个问题是阻止用户选择它。为此,您必须处理该SelectionChanged事件。如果出现这种情况,请InlineUIContainer在 RTB 文档中查找第一个,如果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);
    }
}
Run Code Online (Sandbox Code Playgroud)

可能有一种更简单的方法来制定 LINQ 查询,但我有点喜欢它。这并不是 100% 完美;如果您在文本内部进行选择并将其向左拖动到 上TextBlock,则会丢失选择。我确信这个问题可以解决。但效果很好。它甚至可以处理用户使用箭头键导航的情况。

就这么多,你几乎就可以到达那里了。不过,另一件事可能会让您感到困惑,那就是用户是否将光标定位在文本的开头并按退格键。

处理这需要类似的操作:将插入符号位置与第一个的末尾进行比较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;
    }            
}
Run Code Online (Sandbox Code Playgroud)