WPF中的内联文本框标签

5

我正在尝试在WPF应用程序中复制一些纸质表格的布局。文本框的标签应该与文本框内容“内联”,而不是像普通Windows表单那样“外部”。因此,使用一个Xxxxxx标签:

+-----------------------------+
| Xxxxxx: some text written   |
| in the multiline input.     |
|                             |
| another paragraph continues |
| without indentation.        |
|                             |
|                             |
+-----------------------------+

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

我尝试的一个解决方案是将文本块部分覆盖在输入框上,然后使用文本缩进来缩进可编辑文本,但这会导致以下段落出现问题,因为它们也被缩进了。我不知道如何只缩进第一段落。调整文本以使其对齐需要一些摆弄 - 更可靠的设置将是理想的。

所以,请问如何设置这个呢?

谢谢

1个回答

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查询,但我喜欢这种方式。虽然这不是百分之百完美的;如果你在文本中选择并向左拖动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;
    }            
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接