Bindable Run
Since the initial release of WPF, Run.Text has been a normal CLR property. This has meant that Run.Text lacks all the benefits of the WPF dependency property system, most notably the ability to be bound. In some cases, one could substitute Runs for TextBlocks, which can be bound to; however this can quickly create text flow problems (see example below). The omission of a bindable Run has given birth to many homebrew solutions (eg. here and here). Feeble workarounds and custom solutions are no longer necessary. In WPF 4.0 we have done the work to back Run.Text with a dependency property.
Bindable Run API
The property Run.Text has been converted to a dependency property . Now Run.Text enjoys all of the benefits of full dependency properties (binding, styling, templating, etc…).
<Window x:Class="TextBlogDWriteDemo.Window1"
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1">
<Window.Resources>
<TextBox x:Key="DataStore1" Text="how text does not
flow correctly"/>
<TextBox x:Key="DataStore2" Text="This is an example
of how text flows correctly when only
using bound Runs"/>
</Window.Resources>
<FlowDocument>
<Paragraph>
<Run Text="This is an example of"/>
<TextBlock Text="{Binding Source=
{StaticResource DataStore1},
Path=Text}"/>
<Run Text="when using a combination of Runs
and TextBlock"/>
</Paragraph>
<Paragraph>
<Run Text="{Binding Source={StaticResource
DataStore2}, Path=Text}"/>
</Paragraph>
</FlowDocument>
</Window>
This is a screenshot of the text produced by the above XAML. Notice the spacing issues which are fixed by binding to a Run as opposed to a TextBlock.
Bindable Run Usage
One way data binding is fully supported. A Run can be bound to a data source and the content of the Run will reflect the value of what it is bound to. The bound Run will receive and display any changes that occur in the data source.
Two way data binding is partially supported. If a bound Run is updated via calls to the WPF property system, the data source which the Run is bound to will reflect the changes to the Run. On the other hand, if a bound Run is updated via a RichTextBox or the text object model, the Run will lose its binding.
- Chipalo
Comments
Anonymous
October 27, 2009
Sweet! I never liked having to dig out the homebrew code whenever I start playing around with a new project.Anonymous
October 27, 2009
This is great news. I ended up resorting to 'binding' with xslt for FlowDocument stuff previously so this is a huge improvement. Thanks for listening to your customers on this one!Anonymous
April 12, 2010
The comment has been removedAnonymous
April 13, 2010
Po- Short answer… This example doesn’t really show justification. That is a byproduct of how the Run is doing linebreaking. Long answer… When reexamining the code snippet, I realize that I could have more clearly conveyed the advantages of a BindableRun. The purpose of this example was to show a scenario when one would like data bound content in the midst of static content. Imagine a report where each page has a footer which reads, “this is the ___ page of this report.” In previous versions of the framework, the only way to do this would be to use a databound TextBlock. If the stars are aligned and the space provided for your footer is perfect, the text spacing may come out correct. In the majority of cases, due to the behavior of TextBlock and Run, the output will look very bad. The correct way to accomplish this would to be use three runs where one is databound. This is now possible in WPF4. I have included updated sample code so that you can play with it and see some of the spacing issues I briefly mentioned. <Window x:Class="TextBlogDWriteDemo.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1"> <Window.Resources> <TextBox x:Key="DataStore1" Text="how text does not flow correctly"/> <TextBox x:Key="DataStore2" Text="This is an example of how text flows correctly when only using bound Runs"/> <TextBox x:Key="DataStore3" Text="how text flows correctly"/> </Window.Resources> <FlowDocument> <Paragraph> <Run Text="This is an example of"/> <TextBlock Text="{Binding Source= {StaticResource DataStore1}, Path=Text}"/> <Run Text="when using a combination of Runs and TextBlock"/> </Paragraph> <Paragraph> <Run Text="This is an example of"/> <Run Text="{Binding Source={StaticResource DataStore3}, Path=Text}"/> <Run Text="when using a combination of Runs and TextBlock"/> </Paragraph> </FlowDocument> </Window>