WPF Data Binding and IValueConverter

c#, converters, data-binding, wpf

Solution

(see edits below; latest: #2)

It isn't updating because your `Person` object is not capable of notifying anything that the value of `FirstName` or `LastName` has changed. See this Question.

And here's how you implement `INotifyPropertyChanged`. (Updated, see Edit 2)

using System.ComponentModel;

class Person : INotifyPropertyChanged {
    public event PropertyChangedEventHandler PropertyChanged;

    string _firstname;
    public string FirstName {
        get {
            return _firstname;
        }
        set {
            _firstname = value;
            onPropertyChanged( "FirstName", "FullName" );
        }
    }

    string _lastname;
    public string LastName {
        get {
            return _lastname;
        }
        set {
            _lastname = value;
            onPropertyChanged( "LastName", "FullName" );
        }
    }

    public string FullName {
        get {
            return _firstname + " " + _lastname;
        }
    }

    void onPropertyChanged( params string[] propertyNames ) {
        PropertyChangedEventHandler handler = PropertyChanged;

        if ( handler != null ) {
            foreach ( var pn in propertyNames ) {
                handler( this, new PropertyChangedEventArgs( pn ) );
            }
        }
    }
}

Edit 1

Actually, since you're after the first name and last name updating, and `Path=FirstName` and such works just fine, I don't think you'll need the converter at all. Multiple `TextBlocks` are just as valid, and can actually work better when you're localizing to a right-to-left language.

Edit 2

I've figured it out. It's not being notified that the properties have updated because it is binding to the object itself, not one of those properties. Even when I made `Person` a `DependencyObject` and made `FirstName` and `LastName` `DependencyProperties`, it wouldn't update.

You will have to use a `FullName` property, and I've update the code of the `Person` class above to reflect that. Then you can bind the `Title`. (Note: I've set the `Person` object as the `Window`'s `DataContext`.)

Title="{Binding Path=FullName, Mode=OneWay}"

If you're editing the names in a `TextBox` and want the name changed reflected immediately instead of when the `TextBox` loses focus, you can do this:

<TextBox Name="FirstNameEdit"
    Text="{Binding Path=FirstName, UpdateSourceTrigger=PropertyChanged}" />

I know you didn't want to use a `FullName` property, but anything that would accomplish what you want would probably be a bit of a Rube Goldberg device. Such as implementing `INotifyPropertyChanged` and a `Person` property on the `Window` class itself, having the `Window` listen on the `PropertyChanged` event in order to fire the `Window`'s `PropertyChanged` event, and using a relative binding like the following. You'd also have set the `Person` property before `InitializeComponent()` or fire `PropertyChanged` after setting the `Person` property so that it shows up, of course. (Otherwise it will be `null` during `InitializeComponent()` and needs to know when it's a `Person`.)

<Window.Resources>
    <loc:PersonNameConverter
        x:Key="conv" />
</Window.Resources>
<Window.Title>
    <Binding
        RelativeSource="{RelativeSource Self}"
        Converter="{StaticResource conv}"
        Path="Person"
        Mode="OneWay" />
</Window.Title>

Problem

Why is it that when I use a converter in my binding expression in WPF, the value is not updated when the data is updated. I have a simple Person data model: ``` class Person : INotifyPropertyChanged { public string FirstName { get; set; } public string LastName { get; set; } } ``` My binding expression looks like this: ``` <TextBlock Text="{Binding Converter={StaticResource personNameConverter}" /> ``` My converter looks like this: ``` class PersonNameConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { Person p = value as Person; return p.FirstName + " " + p.LastName; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } } ``` If I bind the data without a converter it works great: ``` <TextBlock Text="{Binding Path=FirstName}" /> <TextBlock Text="{Binding Path=LastName}" /> ``` What am I missing? EDIT: Just to clarify a few things, both Joel and Alan are correct regarding the INotifyPropertyChanged interface that needs to be implemented. In reality I do actually implement it but it still doesn't work. I can't use multiple TextBlock elements because I'm trying to bind the Window Title to the full name, and the Window Title does not take a template. Finally, it is an option to add a compound property "FullName" and bind to it, but I'm still wondering why updating does not happen when the binding uses a converter. Even when I put a break point in the converter code, the debugger just doesn't get there when an update is done to the underlying data :-( Thanks, Uri

Original source