Embedding Controls in a RichTextBox Document
January 18, 2006
New York City
In the world of WPF text elements, a TextBlock is basically a collection of Inline objects, which include things like Run, Span, Italic, and Bold.
The TextFlow and FlowDocument elements are basically collections of Block objects, which include Section, List, Table, and Paragraph. (Of course, Paragraph is a collection of Inline objects.)
Among the descendents of Block is BlockUIContainer and among the descendents of Inline is InlineUIContainer. These two classes are very similar: They both define a Child element of type UIElement. In theory, these two classes allow you to insert any WPF element into text, either inline or as its own paragraph.
The WPF version of RichTextBox is basically a FlowDocument editor, which means that (in theory, anyway) you can insert any WPF element into a RichTextBox document. Not only things like Image and Polyline but also anything that derives from Control, and any class that you define based on UIElement or (more likely) FrameworkElement or Control. The user can interact with these elements, and when you eventually get the FlowDocument object out of the RichTextBox, it will include an XAML representation of these embedded elements.
Alas, I couldn't get it to work. I could see the embedded elements in the RichTextBox but they wouldn't interact with me. It looked as if the elements were disabled, but explicitly enabling them didn't seem to help.
Fortunately, I knew that at least one programmer got this working at one time, because I saw some XAML and a screen shot on Wired Prairie. Thank you, Aaron, for helping me to get this to work. The secret is that the FlowDocument must be explicitly enabled. Here's some XAML I got working under the December CTP. (Although the tags aren't required, the first Button gets wrapped in an InlineUIContainer and the second in a BlockUIContainer.)
-
<StackPanel xmlns="http://schemas.microsoft.com/winfx/avalon/2005">
<RichTextBox>
<FlowDocument IsEnabled="true">
<Paragraph>
This is some text before the inline button.
<Button Margin="10,0,10,0">
Hello, Embedded Inline Button
</Button>
And this is some text after the inline button.
</Paragraph>
<Paragraph TextAlignment="Center">
<Button>
Hello, Embedded Block Button
</Button>
</Paragraph>
<Paragraph TextAlignment="Right">
And this is some text after the block button
</Paragraph>
</FlowDocument>
</RichTextBox>
</StackPanel>
A FlowDocument begins life with its IsEnabled flag set to true. However, when you pass a FlowDocument object to the RichTextBox constructor, or when you assign the Document property of a RichTextBox to a FlowDocument object, or when you let RichTextBox create its own FlowDocument object, the control apparently disables the FlowDocument. I don't know why.