Search code examples
c#xamluwpbindinginotifypropertychanged

uwp binding to child property doesn't update when parent becomes null


I have this nested binding to an object ItemDescription and by default the MyEvidence property is null so it shows up blank but as soon as I set its value to an EvidenceDTO object the itemDescription value shows up as expected.

Now when I again set MyEvidence property to null, I expect the ItemDescription text to be blank again, but that doesn't happen, apparently it doesn't know that parent object is now changed/nulled so child object shouldn't be showing up anymore. I have INotifyPropertyChanged setup on ViewModel class, hence on MyEvidence property as well as ItemDescription property in EvidenceDTO with a helper class known as Observable

Xaml Binding

<TextBlock Text="{x:Bind ViewModel.MyEvidence.ItemDescription, Mode=OneWay}" />

Initializing ViewModel in page backend like this

public MainViewModel ViewModel { get; } = new MainViewModel();

My viewmodel code

public class MainViewModel : Observable
{
    private EvidenceDTO _myEvidence;
    public EvidenceDTO MyEvidence { get => _myEvidence; set => Set(ref _myEvidence, value); }
}

EvidenceDTO

public class EvidenceDTO : Observable
{
    private string _itemDescription;
    public string ItemDescription { get => _itemDescription; set => Set(ref _itemDescription, value); }
}

Observable Class

public class Observable : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected void Set<T>(ref T storage, T value, [CallerMemberName]string propertyName = null)
    {
        if (Equals(storage, value))
        {
            return;
        }

        storage = value;
        OnPropertyChanged(propertyName);
    }

    protected void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

I am showing ItemDescription as an example, there are many more properties of EvidenceDTO which are showing up on UI in a similar way, so I need some universal solution where the property change notification is raised on all levels of a binding path.


Solution

  • It seems that when you set MyEvidence to null, it will still display the previously cached value, and when re-initializing the MyEvidence, it will redisplay according to the attribute value. So if you want to show the ItemDescription text to be blank again, it's better to re-initializing the MyEvidence, like:

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        ViewModel.MyEvidence = new EvidenceDTO();
    }
    

    If you have more than one layer of nested model, you can set model like below in each layer of the nested model. When you set the MyEvidence to null, it will trigger the get method of MyEvidence, then re-initializing it in get method.

    public EvidenceDTO MyEvidence
    {
        get
        {
            if (_myEvidence == null)
            {
                _myEvidence = new EvidenceDTO();
            }
            return _myEvidence;
        }
        set => Set(ref _myEvidence, value);
    }
    
    private void Button_Click(object sender, RoutedEventArgs e)
    {
        ViewModel.MyEvidence = null;
    }