Separation of business logic and interface is very important for maintenance and migration. An attribute is assigned a value on the interface, and changes must be detected in the background.
Problem:
Enter the unit price and quantity amount of an item. The total price must be calculated automatically, that is, totalprice = price * amount, for example:
Common Implementation Method
Textbox. in the textchanged () event, you can detect a change in the value and assign it to another textbox. if there are too many textbox values, adding such an event to each textbox will increase the workload.
The following describes another method:
Databindings can be used with inotifypropertychanged to easily achieve this requirement.
Step 1.Write a class policypropertychanged to inherit inotifypropertychanged to implement onpropertychanged. When adding a propertychanged event on the interface, refresh it.
Public class policypropertychanged: inotifypropertychanged {public event implements propertychanged; Public bool suppresspolicypropertychanged {Get; set;} protected virtual void onpropertychanged (string propertyname) {If (this. propertychanged! = NULL &&! Suppressnotifypropertychanged) {This. propertychanged (this, new propertychangedeventargs (propertyname) ;}} protected virtual void onpropertychanged <t> (expression <func <t> expr) {This. onpropertychanged (utils. getmembername (expr);} // <summary> // if no other business logic exists, if you are familiar with Lambda expressions, you can use the following methods to pass the attribute name: // </Summary> /// <typeparam name = "T"> </typeparam> // /<Param name = "propfield"> </param> // <Param Name = "value"> </param> // <Param name = "expr"> </param> protected void setproperty <t> (ref t propfield, T value, expression <func <t> expr) {var bodyexpr = expr. body as system. LINQ. expressions. memberexpression; If (bodyexpr = NULL) {Throw new argumentexception ("expression must be a memberexpression! "," Expr ");} var propinfo = bodyexpr. member as propertyinfo; If (propinfo = NULL) {Throw new argumentexception (" expression must be a propertyexpression! "," Expr ");} var propname = propinfo. name; propfield = value; this. onpropertychanged (propname) ;}} public class utils {public static string getmembername <t> (expression <func <t> expr) {var bodyexpr = expr. body as system. LINQ. expressions. memberexpression; If (bodyexpr = NULL) return string. empty; return bodyexpr. member. name ;}}Step 2. Write a class to implement the logic of price, amount, and totalprice. Of course, this class inherits policypropertychanged
public class TestBindingClass : NotifyPropertyChanged { #region Properties private decimal _price; public decimal Price { get { return _price; } set { _price = value; _totalPrice = Amount * Price; OnPropertyChanged(() => this.Price); OnPropertyChanged(() => this.TotalPrice); } } private decimal _amount; public decimal Amount { get { return _amount; } set { _amount = value; _totalPrice = Amount * Price; OnPropertyChanged(() => this.Amount); OnPropertyChanged(() => this.TotalPrice); } } private decimal _totalPrice; public decimal TotalPrice { get { return _totalPrice; } set { _totalPrice = value; if (Amount != 0) _price = TotalPrice / Amount; // Note:don‘t call method Price_Set, or it goes into an infinite loop OnPropertyChanged(() => this.TotalPrice); OnPropertyChanged(() => this.Price); } } #endregion }Step 3. Bind relevant properties to the interface, and add the propertychanged event to the relevant properties.
public partial class Form1 : Form { public Form1() { InitializeComponent(); } protected override void OnLoad(EventArgs e) { base.OnLoad(e); textBox1.DataBindings.Add(ControlBindingProperty.Text, myClass, () => myClass.Price); textBox2.DataBindings.Add(ControlBindingProperty.Text, myClass, () => myClass.Amount); textBox3.DataBindings.Add(ControlBindingProperty.Text, myClass, () => myClass.TotalPrice); } TestBindingClass myClass = new TestBindingClass(); } public static class GUIUtils { public static void Add<T>(this ControlBindingsCollection bindings, string propertyName, object dataSource, Expression<Func<T>> expr) { string dataMember = Utils.GetMemberName(expr); bindings.Add(propertyName, dataSource, dataMember); } } public static class ControlBindingProperty { public const string Text = "Text"; }Summary:
(1). When assigning values to other attributes in the Set Method of other attributes, it is best to use a lower-case attribute name instead of calling the otherproperty_set method directly. Otherwise, it is easy to enter an endless loop.
(2 ). you can see that the extension method of controlbindingscollection is added in step 3, so you don't have to worry about the ugly way of writing such as "propertyname, in fact, the prompt function of IDE makes it very convenient to write lambda expressions, and you can check whether the attribute name corresponds during build, which improves code writing efficiency and reduces the chance of errors.