views:

8247

answers:

5

Or, to be more clear, how can I format a block of text (in my case, to be included within a tooltip) such that some portions of the text come from bound values.

In plain C# I would use:

_toolTip.Text = string.Format("{1:#0}% up, {2:#0}% down",
    Environment.NewLine, percentageOne, percentage2);

However the WPF XAML markup for a Text property seems able to contain only a single binding. The curly braces gave me high hopes, but this isn't possible:

<Element>
  <Element.Tooltip>
    <!-- This won't compile -->
    <TextBlock Text="{Binding Path=PercentageOne}% up, {Binding Path=PercentageTwo}% down"/>
  </Element.Tooltip>
</Element>

I read that the Run.Text property is not a dependency property and can therefore not be bound.

Is there a way I can perform this formatting in XAML?

+2  A: 

As far as I know, WPF doesn't do what you want. You do have a much more powerful (albeit more involved) solution.

Take a look at the IValueConverter interface.

MSDN HowTo link here

EDIT

Based on aku's answer, and your assertion that you can't use 3.5 SP1, here's an alternative.

Take a look at Phil Haack's recent series of posts on string formatting:

Create a ValueConverter as that takes the format as a property. You should then be able to bind your data object and have it format based on your defined format (using property name instead of position).

Michael Meadows
Hi Michael. Thanks, but I was hoping to do something more declarative in the XAML rather than having to write a .NET type specifically catering to my requirement. It wouldn't be reusable outside of this context in the way that String.Format is reusable.
Drew Noakes
+19  A: 

You can use MultiBinding + StringFormat (requires WPF 3.5 SP1):

<TextBox.Text>
    <MultiBinding StringFormat="{}{1:#0}% up, {2:#0}% down">
      <Binding Path="PercentageOne" />
      <Binding Path="PercentageTwo"/>
    </MultiBinding>
</TextBox.Text>

Regarding Run.Text - you can't bind to it but there are some workarounds:

aku
awesome, didn't know about that new feature. Do you know what the empty squiglies at the beginning of the string represents?
Michael Meadows
{} is necessary to tell WPF to not treat {something} as a markup extension.
aku
Cheers aku. Unfortunately I'm not targeting SP1 so will have to kludge this one. Still, I think this is the most elegant answer so I've given it to you.
Drew Noakes
Thanks for including the link to BindableRun!
John Fisher
+1  A: 

The way I've solved this in the past is actually to break the TextBlock you have in your listing up into several TextBlocks. Try something like this:

<Element>
  <Element.Tooltip>
    <StackPanel Orientation="Horizontal">
      <TextBlock Text="{Binding Path=PercentageOne}"/>
      <TextBlock Text="% up, "/>
      <TextBlock Text="{Binding Path=PercentageTwo}"/>
      <TextBlock Text="% down"/>
    </StackPanel>
  </Element.Tooltip>
</Element>

Alternately you can create something like a StringFormatConverter, which could take the format string as a parameter, and use a MultiBinding to pass it the parameters. See this link for MultiBindings:

MultiBinding Info

And this one for info on converters:

Converters Info

You can pretty easily imagine a converter that takes "object[] values" instead of "object value" as it's first parameter, and passes those on to the Format function.

Gabe
increasing number of visuals can harm performance, also this markup can work different when text overflow occurs
aku
Thanks for the answer. I'd considered the StackPanel approach but it seems a little clunky and I was hoping for something better. Also, as aku says, it wouldn't wrap neatly. Unfortunately I'm not using SP1 so can't using MultiBinding.
Drew Noakes
A WrapPanel would solve the wrapping issue in lieu of a StackPanel
jomtois
+3  A: 

If you're using 3.5 SP1, Aku's answer is the way to go. If you're not, you can use the FormatConverter from my WPF Converters library.

HTH, Kent

Kent Boogaart
Thanks Kent, these look useful and I'll check them out today. BTW the formatting on the codeplex page you've listed doesn't look quite right towards the end of code samples (in Chrome on WinXP at least).
Drew Noakes
+2  A: 

I would split into multiple textblocks, binding each one with the StringFormat={0:P} in the binding as such:

<TextBox Text="{Binding Something, StringFormat=\{0:P\}}" />

See this post for examples:Lester's WPF Blog on StringFormat

Checkout VS2010 - The binding from properties includes formatting in the options.

Dave