Search code examples
c#wpf.net-6.0inotifypropertychanged

C# WPF dependent value is not updating


I'm working on a C# .NET 6.0 project with WPF, and I have the following scenario:

I have two textboxes in my WPF application:

<StackPanel>
    <Label>Fee Percentage:</Label>
    <TextBox Text="{Binding ContractExtension.FeesPercentage, UpdateSourceTrigger=PropertyChanged}"></TextBox>
</StackPanel>
<StackPanel>
    <Label>Amount in $</Label>
    <TextBox Text="{Binding FeesAmount, Mode=OneWay}" IsReadOnly="True"></TextBox>
</StackPanel>

The value of the second text box depends on the value of the first one:

public class MainViewModel : BaseViewModel
{
    private ContractExtension contractExtension;

    public ContractExtension ContractExtension
    {
        get { return contractExtension; }
        set
        {
            contractExtension = value;
            OnPropertyChanged(nameof(ContractExtension));
            OnPropertyChanged(nameof(FeesAmount));
        }
    }

    public decimal FeesAmount
    {
        get
        {
            decimal feesAmount = Toolbox.GetContractGivenValue(contract, false) * ContractExtension.FeesPercentage / 100;
            return feesAmount;
        }
    }
}
public abstract class BaseViewModel : INotifyPropertyChanged
{
    #region Attributes
    private Window parentWindow;
    #endregion Attributes

    #region Properties
    public Window ParentWindow
    {
        get
        {
            return parentWindow;
        }
        set
        {
            parentWindow = value;
            SetParentWindow(value);
        }
    }

    public abstract void SetParentWindow(Window parentWindow);
    public SearchBoxViewModel SearchBoxViewModel { get; }
    #endregion Properties

    public event PropertyChangedEventHandler? PropertyChanged;

    public abstract void ResetData();

    protected static ExceptionManager exceptionManager;

    public BaseViewModel()
    {
        exceptionManager = new ExceptionManager();
        SearchBoxViewModel = new SearchBoxViewModel();
    }

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

    public void RefreshProperty(string propertyName)
    {
        OnPropertyChanged(propertyName);
    }
}

However, when I modify the value of FeesPercentage, FeesAmount doesn't update in the interface. ContractExtension is an entity model, and Toolbox.GetContractGivenValue is a utility method used to calculate FeesAmount.

Things I've checked:

  • Ensuring that ContractExtension implements INotifyPropertyChanged
  • Verifying that the DataContext is correctly set
  • Checking that the setter of ContractExtension is called when FeesPercentage changes

Is there anything else I should check or modify in my code to make FeesAmount update properly in the interface when FeesPercentage changes?

What I don't understand is that everywhere else in my application this is the way I did it and it worked, but it doesn't work here... I debugged the ContractExtension get,set it enters the set on the initialisation, but whenever I edit the value of FeesPercentage in the textbox, it doesn't trigger the set of ContractExtension and therefore doesn't do OnPropertyChanged(FeesAmount).


Solution

  • It seems you would need to do this:

    public ContractExtension ContractExtension
    {
        get { return contractExtension; }
        set
        {
            //remove event listener from previous instance
            if (contractExtension is not null)
                contractExtension.PropertyChanged -= ContractExtension_OnPropertyChanged;
    
            contractExtension = value;
            OnPropertyChanged(nameof(ContractExtension));
            OnPropertyChanged(nameof(FeesAmount));
    
            //listen to the propertychanged event of your contractExtension
            contractExtension.PropertyChanged += ContractExtension_OnPropertyChanged;
        }
    }
    
    private void ContractExtension_OnPropertyChanged(object sender, PropertyChangedEventArgs args)
    {
         //trigger FeesAmount if FeesPercentage changes
         if (args.PropertyName == nameof(ContractExtension.FeesPercentage))
            OnPropertyChanged(nameof(FeesAmount));
    }