views:

160

answers:

2

How can I prefix bound values in TextBlock controls in a StackPanel without using separate controls for the prefixes?

E.g., let's say I have a dialog that uses a TreeView to display a list of books, with the top nodes being the title, and a set of subordinate nodes for the other book attributes (ISBN, Author, etc.).

I have the binding working correctly, but my user wants the book attributes list to stack vertically, and, obviously, he wants each attribute node to have a descriptive prefix before the value (e.g., "Author: Erich Gamma" instead of just "Erich Gamma"). Inside my HDT and DT elements, I am using a StackPanel and TextBlock controls to display the values.

Must I use a separate TextBlock control for the prefix of each attribute

<!-- Works, but requires 2 controls to display the book author and the prefix stacks above the author -->
<TextBlock Text="Author: "/><TextBlock Text="{Binding Path=Author}" />  

or is there a way to do this with a single TextBlock control for each node?

<!-- only one control, but doesn't work -->
<TextBlock Text="Author:  {Binding Path=Author}" />  

I know this must be a common issue, and I Googled for it and searched in the three WPF books I have, but I guess I don't know the right way to search for what I'm trying to say.

Thanks!

+1  A: 

Quick dirty and simple method: use a converter, and pass the prefix text in as the converter parameter. Then in the converter all you do is prepend the converter parameter text to the binding text.

<TextBlock Text="{Binding Path=Title, Converter={StaticResource MyTextConverter}, ConverterParameter=Title}" />
<TextBlock Text="{Binding Path=ISBNNumber, Converter={StaticResource MyTextConverter}, ConverterParameter=ISBN}" />
<TextBlock Text="{Binding Path=AuthorName, Converter={StaticResource MyTextConverter}, ConverterParameter=Author}" />

public class MyTextConverter : IValueConverter 
{

    #region IValueConverter Members

    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if (value is string)
        {
            return string.Format("{0}{1}{2}", parameter ?? "", !string.IsNullOrEmpty(parameter) ? " : " : "", value);
        }
        return value;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }

    #endregion
}

That was straight of the top of my head, excuse any small errors in it. This gets it done using just the one textblock. All you have to do is include the converter in the static resources of the xaml file.

slugster
Great suggestion. I suspect I will need to do that for anything more sophisticated that what I'm currently doing.Thanks!
Minnow Noir
+3  A: 

If you have .Net 3.5 SP1, you can easily achive this via StringFormat

<TextBlock Text="{Binding Path=Title, StringFormat= Title: {0}}" />

You can also do this

<TextBlock>
  <TextBlock.Text>
    <MultiBinding StringFormat="Author: {0}, Title: {1}">
      <Binding Path="Author"/>
      <Binding Path="Title"/>
    </MultiBinding>
  </TextBlock.Text>
</TextBlock>

If you are not using SP1, Then you can use a ValueConverter

SysAdmin
Your method is cleaner than mine :)
slugster
yes..but, this feature is only present from 3.5SP1
SysAdmin
+1, but you might want to provide a fallback value for when the binding fails or reads null
Rob Fonseca-Ensor
for that you can provide {Binding FallbackValue=Something}. This will work if the binding fails. Or you can use priority binding
SysAdmin
That was what I was looking for. Thanks!Note to self: The curlies for the parameters for StringFormat must be escaped.
Minnow Noir