masoumeh karvar masoumeh karvar - 3 months ago 24
C# Question

source updating in wpf binding

I have a WPF window and give it a DataContext, when targets change it work well (by using

UpdateSourceTrigger
), but when one of DataContext properties (source) change, the target doesn't update. Is there any way to force target binding update when source updates? Is there something like
ObservableCollection
for one item ?

This is my class:

public class PaymentDetailInfo : INotifyPropertyChanged
{
public Payment Payment
{
get;
set;
}

public int SumOfValidNormalOverTimePrice
{
get
{
return 100 * Payment.ConsideredValidNormalOverTime / 60;
}
}
public int SumOfInvalidNormalOverTimePrice
{
get
{
return 100 * Payment.ConsideredInvalidNormalOverTime / 60;
}
}
public int SumOfOverPriceConst
{
get
{
int _sumOfOverPriceConst = 0;

if (!Payment.ValidNormalOverTimePriceIsPercent)
_sumOfOverPriceConst += SumOfValidNormalOverTimePrice;

if (!Payment.InvalidNormalOverTimePriceIsPercent)
_sumOfOverPriceConst += SumOfInvalidNormalOverTimePrice;

_sumOfOverPriceConst += Payment.RewardPrice;

return _sumOfOverPriceConst;
}
}
public event PropertyChangedEventHandler PropertyChanged;

public void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (PropertyChanged != null)
PropertyChanged(this, e);
}

}


Some elements in my Window are bound to
Payment
of
PaymentDetailInfo
(payment is a database table), so as an example user can change
Payment.ConsideredValidNormalOverTime
then
PaymentDetailInfo.SumOfValidNormalOverTimePrice
will change and then
PaymentDetailInfo.SumOfOverPriceConst
will change. But target that is bound to
PaymentDetailInfo.SumOfOverPriceConst
does not update even though the datacontext of my form is updated.

Answer

Update 2:*

Now Payment is passed to PaymentDetailInfo at construction. PaymentDetailInfo wraps the properties of Payment so you can create your binding on PaymentDetailInfo alone.

I've made the setter of SumOfValidNormalOverTimePrice private. I think this should work but I haven't tested it.

public class PaymentDetailInfo : INotifyPropertyChanged
{

/// <summary> The payment model.
/// </summary>
private Payment _model = null;

/// <summary> Constructor.
/// </summary>
public PaymentDetailInfo(Payment payment)
{
    _model = payment;
}

/// <summary> Wrapper around Payment.ConsideredValidNormalOverTime.
/// </summary>
public int ConsideredValidNormalOverTime
{
    get { return _model.ConsideredValidNormalOverTime; }
    set
    {
        _model.ConsideredValidNormalOverTime = value;

        // make sure to set the property and not the backing field, otherwise OnPropertyChanged won't be 
        // called and the value of _sumOfValidNormalOverTimePrice will be incorrect
        SumOfValidNormalOverTimePrice = value;

        OnPropertyChanged(new PropertyChangedEventArgs("ConsideredValidNormalOverTime"));
    }
}

private int _sumOfValidNormalOverTimePrice = 0;
public int SumOfValidNormalOverTimePrice
{
    get { return _sumOfValidNormalOverTimePrice; }
    private set
    {
        _sumOfValidNormalOverTimePrice = 100 * value / 60;
        OnPropertyChanged(new PropertyChangedEventArgs("SumOfValidNormalOverTimePrice"));
    }
}

public event PropertyChangedEventHandler PropertyChanged;

public void OnPropertyChanged(PropertyChangedEventArgs e)
{
    if (PropertyChanged != null)
        PropertyChanged(this, e);
}
}

Update:

Since a change in Payment should trigger the update I would reverse the association between Payment and PaymentDetailInfo. Then make the binding on payment.DetailInfo.

public class Payment
{
    public PaymentDetailInfo DetailInfo = new PaymentDetailInfo();

    private int _consideredValidNormalOverTime = 0;
    public int ConsideredValidNormalOverTime
    {
        get
        {
            _return _consideredValidNormalOverTime;
        }
        set
        {
            _consideredValidNormalOverTime = value;
            DetailInfo.SumOfValidNormalOverTimePrice = _consideredValidNormalOverTime;
        }
    }
}

public class PaymentDetailInfo : INotifyPropertyChanged
{    
    private int _sumOfValidNormalOverTimePrice = 0;
    public int SumOfValidNormalOverTimePrice
    {
        get
        {
            return __sumOfValidNormalOverTimePrice;            
        }
        set
        {
            __sumOfValidNormalOverTimePrice = 100 * value / 60;
            OnPropertyChanged(new PropertyChangedEventArgs("SumOfValidNormalOverTimePrice"));
        }
    }    

    public event PropertyChangedEventHandler PropertyChanged;

    public void OnPropertyChanged(PropertyChangedEventArgs e)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, e);
    }
}

Now when ConsideredValidNormalOverTime is updated SumOfValidNormalOverTimePrice is too and automatically updates the source. Implement this for all remaining properties and it should work. Just think about where to put the logic for SumOfOverPriceConst. Since it relies heavily on Payment maybe you should put it there.

Original:

Here's a sample of implementing INotifyPropertyChanged. Whenever Name changes the target is updated.

public class Foo: INotifyPropertyChanged
{
    #region field and properties

    private string _name = String.Empty;

    public string Name
    {
        get { return _name; }
        set
        {
            _name = value;
            OnPropertyChanged(new PropertyChangedEventArgs("Name"));
        }
    }


    #endregion

    #region INotifyPropertyChanged implementation

    public event PropertyChangedEventHandler PropertyChanged;

    public void OnPropertyChanged(PropertyChangedEventArgs e)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, e);
    }

    #endregion
Comments