Inline text fields with WPF

I am trying to reproduce the layout of some paper forms in a WPF application. Shortcuts for text fields should be "embedded" with the contents of the text fields, and not "outside", like regular Windows forms. So, labeled Xxxxxx:

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

Xxxxxx cannot be editable if the user selects the entire contents of the text field, the label must remain unselected, I need to be able to erase the text color / formatting the label separately when there is no text in the text field, but it has focus, the carriage should flash immediately after the label, and I need the text sources in the text box and a shortcut for alignment.

One solution I tried was to partially place the text block above the input, and then indent the text to indent the edited text, although this caused problems with the following paragraphs, as they were also indented. I am not sure how to retreat only the first paragraph. In order for the text to align, it took several attempts - a more reliable setting would be ideal.

So, any suggestions for customization?

thanks

+4
source share
1 answer

Well, I can offer a somewhat hacky way to do this.

First, note that UI elements can be placed in a FlowDocument . So this does something like this:

 <RichTextBox> <FlowDocument> <Paragraph> <InlineUIContainer> <TextBlock>This is your label: </TextBlock> </InlineUIContainer> <Run>And this is the editable text.</Run> </Paragraph> </FlowDocument> </RichTextBox> 

Now the problem is that the user does not edit InlineUIContainer . These are really two problems.

The first problem is the user's choice of user. To do this, you need to handle the SelectionChanged event. In the case, find the first InlineUIContainer in the RTB document, and if Selection.Start before that, change it.

 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); } } 

Probably an easier way to formulate this LINQ query, but I kind of. And this is not 100% excellent; if you select inside the text and drag left over the TextBlock , it will lose the selection. I am sure that this can be fixed. But it works very well. It even handles the case when the user moves with arrows.

It just so happens that you are almost all the way. Another thing that can ruin you if the user positions the cursor at the very beginning of the text and presses BACKSPACE.

Processing requires something like this: compare the caret position with the end of the first InlineUIElement and cancel BACKSPACE (marking the event as processed) if the caret is in this position:

 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; } } 
+1
source

Source: https://habr.com/ru/post/1305273/


All Articles