3

I have a class in which the name of the object can be null.

public class Thing
{
    /// <summary>
    /// The identifier of the thing
    /// </summary>
    /// <remarks>
    /// This will never be null.
    /// </remarks>
    public string Identifier { get; set; }
    /// <summary>
    /// The name of the thing
    /// </summary>
    /// <remarks>
    /// This MAY be null. When it isn't, it is more descriptive than Identifier.
    /// </remarks>
    public string Name { get; set; }
}

In a Silverlight ListBox, I use a DataTemplate where I have the name bound to a TextBlock:

<DataTemplate x:Key="ThingTemplate">
    <Grid>
        <TextBlock TextWrapping="Wrap" Text="{Binding Name}" />
    </Grid>
</DataTemplate>

However, this obviously doesn't look very good if the Name is null. Ideally, I would want to use something equivalent to

string textBlockContent = thing.Name != null ? thing.Name : thing.Identifier;

but I can't change my model object. Is there any good way to do this?

I thought about using a Converter, but it seems to me I'd have to bind the converter to the object itself, and not the Name property. This would be fine, but how would I then rebind when either Name or Identifier changes? IValueConverter doesn't appear to have any way to force a reconvert if I would manually listen on my object's INotifyPropertyChanged event.

Any ideas on the best way to do this?

H.B.
  • 166,899
  • 29
  • 327
  • 400
Bryan
  • 1,613
  • 16
  • 18
  • possible duplicate of [Equiv. to Coalesce() in XAML Binding?](http://stackoverflow.com/questions/6947728/equiv-to-coalesce-in-xaml-binding) – Justin Niessner Sep 12 '11 at 19:05
  • Be specific. Is it silverlight, or WPF? – Nawaz Sep 12 '11 at 19:07
  • You can't do this without changing code, as far as I know. If you found a way using strictly XAML, it would most likely be considered a hack. – qJake Sep 12 '11 at 19:07
  • @Nawaz: Silverlight. I added 'Silverlight' to the title to make it more explicit. – Bryan Sep 12 '11 at 19:21
  • Can you create a wrapper object for your model? That way it could listen for property changes for both `Name` and `Identifier` and output the correct property in either case. – Gabe Sep 12 '11 at 19:35
  • @Gabe: I've created wrappers for other parts of my application, and that may be the way to go. My ViewModel has ObservableCollection, and I may have to change that to ObservableCollection. It's a bit more of a pain, because then when I get the data, I have to wrap it all, but it's viable. If MultiBinding were built in, that would be the way I would go, but since it isn't, wrapping might be my best option. – Bryan Sep 12 '11 at 19:48
  • @Bryan: In Silverlight 5 you will be able to create a binding extension like MultiBinding. – Gabe Sep 12 '11 at 20:14

4 Answers4

3

You can change the Binding, so that it binds directly to your Thing instance:

<DataTemplate x:Key="ThingTemplate">
    <Grid>
        <TextBlock TextWrapping="Wrap" Text="{Binding .,Converter={StaticResource ConvertMyThingy}" />
    </Grid>
</DataTemplate>

Then use a converter that returns either Instance or Name from the passed Thing instance

public class ConvertMyThingyConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var thing= value as Thing;
        if(thing == null)
           return String.Empty;
        return thing.Name ?? thing.Identifier;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}
thumbmunkeys
  • 20,606
  • 8
  • 62
  • 110
  • This is a good, simple way to handle this, though it's not reusable to other types, as it requires a custom converter per type (or interface, at least). – Reed Copsey Sep 12 '11 at 19:36
  • @xor: In my model. I have two TextBoxes elsewhere in my application bound to Identifier and Name. When one of these changes, the value in the listbox needs to change. – Bryan Sep 12 '11 at 19:45
2

In WPF, you can do this easily by implementing an IMultiValueConverter. Unfortunately, Silverlight doesn't support this directly, though there are workarounds written for Silverlight.

Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373
0

The Converter is not bind to Name or Object, it binds to Text property binding. So, it will be invoked every time you assign a value to Text. Up to you after inside a Converter to choice what Text property will look like.

One of possible solutions is to make NameAndIdentifier string special property which is assignable from Converter based on Model's Name and Identifier properties.

Tigran
  • 61,654
  • 8
  • 86
  • 123
0

You need to create a custom display property in your ViewModel/Presenter (or whatever you want to call it). Basically, the code you posted above must be modified, and this added to it:

public readonly string DisplayName
{
    get
    {
        return Name ?? Identifier;
    }
}

Any other way, as far as I know, is going to be a hack.

qJake
  • 16,821
  • 17
  • 83
  • 135
  • You should use the `??` operator: `return Name ?? Identifier;`. This is the best solution in general, though it's moot since the OP has said that he can't change the model. – dlev Sep 12 '11 at 19:13
  • 1
    It's worth mentioning if someone else sees this question that *can* edit their model. And good point about the `??` operator, I always forget about it. ;) – qJake Sep 12 '11 at 19:14