Willem van Rumpt
Willem van Rumpt

Reputation: 6570

Data binding the Text property of a Run

I'm trying to databind a Run of a TextBlock in code at runtime, and I can't for the life of me figure out how.

Several sources on the internet suggest this isn't possible without some (not too pretty) additional workarounds, and, more importantly, it should completely fail when you try to do it in XAML.

Yet, in my application I have the following, which works beautifully:

<DataTemplate x:Key="PitchTemplate">
    <Grid Width="120" Height="120" HorizontalAlignment="Center" VerticalAlignment="Center">
        <TextBlock HorizontalAlignment="Center"
                   VerticalAlignment="Center"
                   TextAlignment="Center">
            <Run Text="{Binding}" FontFamily="{StaticResource PhoneFontFamilySemiBold}" FontSize="36"/>
            <LineBreak/>
            <Run Text="{Binding Frequency, StringFormat=\{0:n2\}Hz}" FontFamily="{StaticResource PhoneFontFamilyNormal}" Foreground="{StaticResource PhoneSubtleBrush}"/>
        </TextBlock>
    </Grid>
</DataTemplate>

So I figured: If it can be done in XAML, it should be possible to do it in code.

Sofar, to no avail. Using the "regular" way of binding in code won't work; the Run class doesn't inherit from FrameworkElement, so doesn't have a SetBinding method, and it's Text property is not a DependencyProperty.

Using BindingOperations.SetBinding doesn't work because the Text property is not a DependencyProperty.

I'm up to the point that I'm willing to accept that it can't be done at runtime (although not without a last attempt at StackOverflow), but I'm still curious if

And if not:

EDIT:

The example shown is just there to show that it can be done in XAML. The reason I need to create the bindings in code, is that I have a control that dynamically creates other elements, which need to be data bound.

UPDATE:

As Pete and I both found out, there is a dependency property for Text, but it's private. I assume that's why it does work through XAML (the xaml parser probably has more rights when it comes to reflection, and more knowledge in general about classes).

The upside is, that this means (tried & tested) it also works through XamlReader.Load(), which is (sofar) the cleanest solution I've come up with.

But if anyone has anything better, I'd be glad to hear about it.

Upvotes: 3

Views: 2572

Answers (3)

Willem van Rumpt
Willem van Rumpt

Reputation: 6570

Thanks both to Pete & Panagiotis for their efforts and suggestions (both 1 up).

In the end, I decided to go with my own solution (found in the "Update" section of the question): Create dedicated XAML strings containing the Run including the binding, and use XamlReader.Load() to parse it, and return a Run object.

The situation I'm working on is quite specific, so a local solution to the problem is good enough (for now). Reflection, as suggested by Panagiotis, won't work due to restrictions imposed by Silverlight. Lastly, the BindableRuns solutions would need either extensive work to deal with nested properties, or I would have to "uglify" my view model, so I discarded it (also for now).

Thanks all for your input.

Upvotes: 0

Panagiotis Kanavos
Panagiotis Kanavos

Reputation: 131374

Run.Text is backed by the private TextProperty which means you can't directly set its value without some reflection gimmicks, something like this:

        Run r=new Run();
        r.Text = "Moo";
        var field=r.GetType().GetField("TextProperty", BindingFlags.Static | BindingFlags.NonPublic);
        var dp=field.GetValue(null) as DependencyProperty;
        BindingOperations.SetBinding(r, dp, new Binding {...});

This is rather ugly, but perhaps it can be useful.

You can find various workarounds for this. This SO post uses a custom attached property to configure binding. The attached property is used because Run is a sealed class in Silverlight so you can't create you own Run that supports binding.

Upvotes: 2

pete the pagan-gerbil
pete the pagan-gerbil

Reputation: 3166

The reason that it works in XAML but not in code-behind might be that there is a dependency property for Text, but it is private. It's a little bit 'black magic' to me though, so that's just a guess! This is a strange one, as in WPF the Run does inherit from FrameworkContentElement and has a SetBinding method...

Could you create a subclass of Run that contains a public DependencyProperty for text? I'm afraid I don't have the Silverlight dev tools to hand to try it out at the moment, but I'll try and take a look later.

Upvotes: 1

Related Questions