This "How To" shows several concepts around grouping and wrapping text.

It also shows just how flexible the TextBlock now is, since the introduction of Runs. Each Run can have different styles, cursors, even events like MouseLeftButtonDown

For this scenario, a WPF Forum poster wanted to know how to wrap blocks of text into one group, to make a single paragraph. This project shows two methods in four examples, the WrapPanel and TextBlock Runs. Both are useful concepts to understand, as they each have their uses.

 

 

 


1) Firstly an example of the WrapPanel, which is a great container, a very useful control:

<WrapPanel Margin="10">
    <TextBlock Margin="0,0,4,0" Text="This is an example"/>
    <TextBlock Margin="0,0,4,0" Text="with textboxes that line up horizontally"/>
    <TextBlock Margin="0,0,4,0" Text="then wrap by TEXTBLOCK to a new"/>
    <TextBlock Margin="0,0,4,0" Text="line when there is no more space"/>
    <TextBlock Margin="0,0,4,0" Text="pull the window out to"/>
    <TextBlock Margin="0,0,4,0" Text="see it wrap and change"/>
</WrapPanel>

This will produce a bunch of textblocks that do not themselves wrap, and when there is no space, the TextBlock as a whole will jump to the next line.
 
2) Next here is an example of using the TextBlock.Inlines collection, by manually assigning Runs in XAML:


<TextBlock Margin="10" TextWrapping="Wrap"><Run Text="This is an example"/><Run Text=" of a group of"/><Run Text=" manually coded runs"/><Run Text=" which are parts of a textblock"/><Run Text=" and this is probably closer to what you want"/></TextBlock>


This does just what we want, joining the blocks together and breaking on words, not per block.

3) Example three shows how to build the Runs programatically and add them to an existing TextBlock:

var texts = new List<string> { "This is an example of building a TextBlock", " programmatically from a collection", " of strings into one flexible wrapping TextBlock"};
  
foreach (var text in texts)
    tb1.Inlines.Add(new Run { Text = text });


4)
Finally here is an example of some of the flexibility of this technique, by making two very different Runs which act independantly within a single TextBlock which I create and add to the Visual Tree.

var cm1 = new ContextMenu();
cm1.Items.Add(new MenuItem { Header = "Copy" });
 
var cm2 = new ContextMenu();
cm2.Items.Add(new MenuItem { Header = "Do something else" });
 
 
var run1 = new Run
{
    Text = "Hello",
    Background = Brushes.Yellow,
    Foreground = Brushes.Blue,
    Cursor = Cursors.Hand,
    ContextMenu = cm1,
    FlowDirection = System.Windows.FlowDirection.RightToLeft,
    FontFamily = new FontFamily("Courier"),
    FontSize = 20.0D,
    FontWeight = FontWeights.Bold,
    TextDecorations = TextDecorations.Strikethrough,
    ToolTip = "Run 1",
};
run1.MouseLeftButtonDown += new MouseButtonEventHandler(run1_MouseLeftButtonDown);
 
var run2 = new Run
{
    Text = " World",
    Background = Brushes.LightGreen,
    Foreground = Brushes.Red,
    Cursor = Cursors.Cross,
    ContextMenu = cm2,
    FlowDirection = System.Windows.FlowDirection.LeftToRight,
    FontFamily = new FontFamily("Times New Roman"),
    FontSize = 30.0D,
    FontWeight = FontWeights.ExtraLight,
    TextDecorations = TextDecorations.Underline,
    ToolTip = "Run 2",
};
run2.MouseLeftButtonDown += new MouseButtonEventHandler(run2_MouseLeftButtonDown);
 
var tb2 = new TextBlock { Margin = new Thickness(10) };
tb2.Inlines.Add(run1);
tb2.Inlines.Add(run2);
 
sp1.Children.Add(tb2);


In this example, as well as the visual differences, you can hover over EACH TEXT PART and see a different Cursor & Tooltip and even click each block to trigger separate events!


This available in a demo project here.


This small article is part of a series of WPF "How To" articles, in response to real user questions on the MSDN WPF Forum.