Bob Sammers
Bob Sammers

Reputation: 3290

A pattern for change notification on nested properties

I want to bind to a property of a property of my ViewModel. The nested object is not known at compile time so I cannot just set the binding path to one of its properties directly. Instead, I bind to the object as a whole and a value converter seeks out the property (or properties) of interest using reflection.

The binding and value converter work fine initially, but the value converter is not called if the values of the nested properties are altered (it is if the reference to this object is changed in the VM). Both VM and nested class implement INotifyPropertyChanged.

Here is a simplified example in which if the value of Name is initially set to "Bond" the secret is revealed, but never is if the combo box value is changed to "Bond" while the program is running:

XAML:

<Window.Resources>
    <local:ViewModel x:Key="vm" />
    <local:NameToVisConverter x:Key="NameToVis" />
</Window.Resources>

<StackPanel DataContext="{StaticResource vm}">
    <ComboBox SelectedValue="{Binding MyPoco.Name}">
        <sys:String>Smith</sys:String>
        <sys:String>Jones</sys:String>
        <sys:String>Bond</sys:String>
    </ComboBox>

    <Label Visibility="{Binding MyPoco, Converter={StaticResource NameToVis}}">
        Secret!
    </Label>
</StackPanel>

ViewModel & nested class:

public class ViewModel : BindableBase
{
    public Poco MyPoco
    {
        get { return _MyPoco; }
        set { SetProperty(ref _MyPoco, value); }
    }
    private Poco _MyPoco = new Poco();
}

public class Poco : BindableBase
{
    public String Name
    {
        get { return _Name; }
        set { SetProperty(ref _Name, value); }
    }
    private String _Name;

}

Note: BindableBase and its SetProperty() are provided by Prism and implement INotifyPropertyChanged.

Value conversion methodfrom NameToVisConverter:

public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
    return (value as Poco).Name == "Bond"
        ? Visibility.Visible
        : Visibility.Collapsed;
}

I naively expected that the binding would be alerted if a property of an INotifyPropertyChanged class is changed if the binding is on the object itself rather than on one of the object's properties. Obviously it isn't... is there a good pattern (or, hopefully, a simple workaround) for dealing with this situation?

Upvotes: 0

Views: 859

Answers (1)

If I were you, I'd just have the parent VM raise PropertyChanged("MyPoco") when any of MyPoco's properties changes (or should be presumed to have changed, or whatever the situation is). This will cause the binding to update the target, and your converter's Convert will be called.

Alternatives would involve C# code you'd have to write and debug and maintain, and this is a perfectly normal way to use INotifyPropertyChanged in cases where the XAML binds to a property of a property value object that's immutable, or doesn't implement INotifyPropertyChanged. For example, I've bound StringProperty.Length a few times, or NullableProperty.HasValue.

Upvotes: 1

Related Questions