views:

228

answers:

4

I have a situation where I have a couple of variables who's values depend on each other like this:

A is a function of B and C B is a function of A and C C is a function of A and B

Either value can change on the UI. I'm doing the calculation and change notification like this:

private string _valA;
private string _valB;
private string _valC;

public string ValA
{
    get { return _valA; }
    set
    {
        if (_valA != value)
        {
            _valA = value;
            RecalculateValues("ValA");       //Here ValB and ValC are calculated
            OnPropertyChanged("ValA");
        }
    }
}

public string ValB
{
    get { return _valB; }
    set
    {
        if (_valB != value)
        {
            _valB = value;
            RecalculateValues("ValB");       //Here ValA and ValC are calculated
            OnPropertyChanged("ValB");
        }
    }
}

(...)

private void RecalculateValues(string PropName)
{
    if (PropName == "ValA")
    {
       _valB = TotalValue * _valA;
       OnPropertyChanged("ValB");

       _valC = something * _valA
       OnPropertyChanged("ValC");
    }
    else
    (...)

}

I'm calling the calculation method on the setter of the changed variable, calculating the values for _valB, _valC (for example) and then calling PropertyChanged for these ones. I do it like this because of the dependencies between the variables, like this i can control which variable gets calculated with the correct values. I also thought about triggering the PropertyChanged for the other variables and perform the calculation on the getter of the variables but i would have to know which property changed before and use that value...not sure if it's the best/simplest solution.

Is this a good way to do this? I don't like the idea of performing this on the setter block, but at the time I can't see any better way to do it.do you see any other (better or cleaner solution)?

Another issue I have is using IdataErrorInfo for validation/presenting error info to the UI.The thing is the this[columnName] indexer gets called at the end of the setter and I needed to validate the values before the calculation, so that if the value inputted is not valid the calculation would not happen.I'm seriously considering abandoning IDataErrorInfo and simply calling my validation method before the calculation(s) occurs. Is there any way to explicitly calling it or right after the value attribution?

NOTE: ValidationRules are not an option because I need to call validation logic on another object from my ViewModel.

+2  A: 

Its alright to call your Validation and Calculation in Setter, as long as it does not block the thread and goes into heavy cpu intensive calculations. If you have simple 5 to 10 math based statements without involving complex loops, its alright.

For databinding and WPF, this is the only way, However there is one more implementation called IEditableObject which has BeginEdit, CancelEdit and EndEdit, where you can do your calculation and validation on "EndEdit" function.

In BeginEdit, you can save your all values in temp storage, in CancelEdit you can bring back all values from temp and fire PropertyChaged event for all values those were modified and in EndEdit, you can finally update all variables and fire PropertyChanged for all those are updated.

IEditableObject will be best for performance wise, but it may not display new values until it was cancelled or ended, however to display instantly the only way to do is the way you are doing it.

In case of heavy cpu usage, you can invoke another thread to do calculation and set variables and at the end you can fire PropertyChanged, but yes technicaly you are just doing same thing while setting value in setter but asynchronously.

Akash Kava
A: 

@Jason, Sorry, you're right.I introduced this code snippet but removed the validation (and conversion to number) from it.My goal was simply to show what I was doing for the sake of it, in terms of procedure.

Actually, I validate and calculate the new variable values, write them to the business model and update the view model.

@Akash, yes It's simple calculations which won't block the thread, for sure.I just feel akward doing it this way, but, for me, it was the first thing that came into my mind.

Regarding IDataErrorInfo...any sugestion/help? Is there a way to control when the this indexer is called?

Thanks for your answers

Jay
A: 

Since you can evoke NotifyPropertyChanged from anywhere in the class, one way to simplify this is to have your Calculate method fire the NotifyPropertyChanged for all three variables. I use this trick when I have complex calculations with multi-dependencies. Sometimes you'll still want to fire the event in the Setter: in that case I use a flag field to disable the event when I am updating the property from within the Calculations so that the event is not fired twice.

Joel Cochran
A: 

Thanks for your answers guys. Good new year's eve to everybody.

Jay