How to insert inline content from one FlowDocument into another?

c#, flowdocument, richtextbox, textrange, wpf

Solution

Your immediate problem is that you are using `TextFormat.Xaml` instead of `TextFormat.XamlPackage`.

The property that controls whether or not lines are merged when documents are combined is the `Section.HasTrailingParagraphBreakOnPaste` property. This property is only effective when loading or saving the `XamlPackage` text format. When using `Xaml` text format instead, the property is omitted during `Save()` and ignored during `Load()`.

So the simple fix is to simply change the Load and Save calls:

tr.Save(ms, DataFormats.XamlPackage); 
ms.Seek(0, SeekOrigin.Begin); 
tr = new TextRange(target.CaretPosition, target.CaretPosition); 
tr.Load(ms, DataFormats.XamlPackage); 

Note that this also fixes another problem you would eventually run into: Embedded bitmaps will not be copied properly when using `DataFormats.Xaml` because there is nowhere to put the image bits. With `DataFormats.XamlPackage` an entire package is built so bitmaps and other package items will come across ok.

Once you make this change you may discover another fact that may or may not be an issue for you: Your sample code uses `document.ContentStart` and `document.ContentEnd`. If this is your actual code you will discover that any range from `document.ContentStart` to `document.ContentEnd` necessarily consists of full paragraphs, so copying it will always insert a paragraph break at the end of the insertion. If this is a problem, use something like `RichTextBox.Selection` (if this is UI driven) or use `TextPointer` to back up `ContentEnd` to before the implicit paragraph mark, for example:

var tr = new TextRange(document.ContentStart,
                       document.ContentEnd.GetInsertionPosition(
                                                  LogicalDirection.Backward));

Problem

I'm building an application that needs to allow a user to insert text from one `RichTextBox` at the current caret position in another one. I spent a lot of time screwing around with the `FlowDocument`'s object model before running across this technique - `source` and `target` are both `FlowDocument`s: ``` using (MemoryStream ms = new MemoryStream()) { TextRange tr = new TextRange(source.ContentStart, source.ContentEnd); tr.Save(ms, DataFormats.Xaml); ms.Seek(0, SeekOrigin.Begin); tr = new TextRange(target.CaretPosition, target.CaretPosition); tr.Load(ms, DataFormats.Xaml); } ``` This works remarkably well. The only problem I'm having with it now is that it always inserts the source as a new paragraph. It breaks the current run (or whatever) at the caret, inserts the source, and ends the paragraph. That's appropriate if the source actually is a paragraph (or more than one paragraph), but not if it's just (say) a line of text. I think it's likely that the answer to this is going to end up being checking the target to see if it consists entirely of a single block, and if it does, setting the `TextRange` to point at the beginning and end of the block's content before saving it to the stream. The entire world of the `FlowDocument` is a roiling sea of dark mysteries to me. I can become an expert at it if I have to (per Dostoevsky: "Man is the animal who can get used to anything."), but if someone has already figured this out and can tell me how to do this it would make my life far easier.

Original source