BindableRun
A useful feature that was left out of the first version of WPF was the ability to databind the value of Run.Text. I was still around when this feature was, unfortunately, cut. Fortunately it isn’t that hard to implement yourself.
We’ll do this by creating a subclass of Run, creatively named BindableRun.
using System;
using System.Windows;
using System.Windows.Documents;
namespace BindableText
{
/// <summary>
/// A subclass of the Run element that exposes a DependencyProperty
/// to allow data binding.
/// </summary>
public class BindableRun : Run
{
public static readonly DependencyProperty BoundTextProperty =
DependencyProperty.Register(
"BoundText",
typeof(string),
typeof(BindableRun),
new PropertyMetadata(
new PropertyChangedCallback(BindableRun.onBoundTextChanged)));
private static void onBoundTextChanged(
DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
((Run)d).Text = (string)e.NewValue;
}
public string BoundText
{
get { return (string)GetValue(BoundTextProperty); }
set { SetValue(BoundTextProperty, value); }
}
}
}
The code is pretty straightforward: create a new DependencyProperty in the usual manner, then add a PropertyChangedCallback that manually sets the Text property. That’s it. The base class takes care of everything else.
You can use this in XAML the same way you would any other classes in a DLL, by declaring an XML namespace and linking it to a CLR namespace, as in the example below:
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:bt="clr-namespace:BindableText;assembly=BindableText">
<FlowDocumentScrollViewer>
<FlowDocument>
<Paragraph>
You can control the value of this text through the TextBox below:
<bt:BindableRun
FontWeight="Bold"
BoundText="{Binding ElementName=tb, Path=Text}" />
</Paragraph>
<BlockUIContainer>
<TextBox
Name="tb"
Text="This is text with spaces that wraps across a line ... like this!" />
</BlockUIContainer>
</FlowDocument>
</FlowDocumentScrollViewer>
</Page>
If you’re running this in XamlPad, make sure BindableRun.dll is in the same directory as XamlPad itself. Either create a new copy of XamlPad.exe or copy BindableRun.dll into the directory where you keep XamlPad.
Update 3/21: Paul Stovell also described an alternate attached-property technique to achieve the same effect.