Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
208 views
in Technique[技术] by (71.8m points)

c# - MVVM update of calculated properties

I'm just learning MVVM, and I'm trying to work how to display changes to a calculated property as a result of changes to the values from which it's calculated. All of the solutions I've seen so far seriously violate encapsulation, and I'm wondering whether there's anything better.

Suppose one of the things I need to display is the results of a complex tax calculation. The calculation (and possibly its dependencies) will change from time to time, so I would like to keep it strictly encapsulated.

The solution most often offered here seems to be to get all of the properties on which the tax value depends to invoke PropertyChanged in the ModelView for the property itself and for every property that depends on it. That means that every property needs to know everything that uses or might use it. When my tax rules change in a way that makes the calculation dependent on things it was not previously dependent on, I will need to touch all those new properties that go into my calculation (possibly in other classes, possibly not under my control), to get them to invoke PropertyChanged for the tax value. That completely trashes any hope of encapsulation.

The best solution I can think of is to make the class that does the calculation receive PropertyChanged events, and raise a new PropertyChanged event for the tax value when anything changes that goes into the calculation. That at least preserves encapsulation at class level, but it still breaches method encapsulation: the class shouldn't have to know about how a method does its work.

So, my question is, is there a better way (and if so, what is it)? Or does encapsulation of presentation (MVVM) prevent encapsulation of the business logic? Am I faced with an either/or choice?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The solution most often offered here seems to be to get all of the properties on which the tax value depends to invoke PropertyChanged in the ModelView for the property itself and for every property that depends on it.

No the supporting properties do not need their own change notification unless they are being displayed. But each property will need to call the tax value's OnPropertyChanged("TaxValue") in their setter(s) either directly; or indirectly as per the example below. That way the UI gets updated because a supporting property has changed.

With that said, let us consider an example. One way is to create a method which will do the value calculation. When the ultimate value is set (TaxValue below) it will call OnNotifyPropertyChange. That operation will inform the user of the TaxValue change to the whole world; regardless of what value triggers it (Deduction|Rate|Income):

public class MainpageVM : INotifyPropertyChanged 
{
       public decimal TaxValue 
        {
           get { return _taxValue; }
           set { _taxValue = value; OnPropertyChanged(); }  // Using .Net 4.5 caller member method.
        }

        public decimal Deduction
        {
           get { return _deduction; }
           set { _deduction = value; FigureTax(); }
        }

        public decimal Rate
        {
           get { return _rate; }
           set { _rate = value; FigureTax(); }
        }

        public decimal Income
        {
           get { return _income; }
           set { _income = value; FigureTax(); }
        }

        // Something has changed figure the tax and update the user.
        private void FigureTax()
        {
           TaxValue = (Income - Deduction) * Rate;
        }


    #region INotifyPropertyChanged
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>Raises the PropertyChanged event.</summary>
        /// <param name="propertyName">The name of the property that has changed, only needed
        /// if called from a different source.</param>
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }

    #endif
    }

Edit

To use CallerMemberName (and other items) in .Net 4 install the Nuget package:

Microsoft.BCL.

or if not use the standard OnPropetyChanged("TaxValue") instead.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...