--首发于博客园, 转载请保留此链接  博客原文地址

业务逻辑与界面的分离对于维护与迁移是非常重要的,在界面上给某属性赋值,后台要检测到其已经发生变化

问题:

输入某物品 单价 Price, 数量Amount, 要求自动计算总价,即: TotalPrice = Price * Amount, 如下图:

普通的实现方式

TextBox.TextChanged() 事件中可以检测到值发生改变,并且可以给其他的 TextBox.Text 赋值,但是如果 TextBox 太多,给每个 TextBox 加这样的一个事件工作量会比较大。

下面介绍另外一种方法:

使用 DataBindings 与 INotifyPropertyChanged 配合可以轻松实现这个需求。

Step 1. 先写一个类 NotifyPropertyChanged 继承 INotifyPropertyChanged 实现 OnPropertyChanged , 当界面添加 PropertyChanged 的事件时可以实现刷新

    public class NotifyPropertyChanged : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public bool SuppressNotifyPropertyChanged { 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>
/// 如果没有其他的业务逻辑,对 lambda 表达式比较熟悉的同学可以考虑用以下方法实现属性名称传递
/// </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. 写一个类 实现 Price, Amount, TotalPrice 的逻辑, 当然这个类继承 NotifyPropertyChanged

    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. 界面绑定相关属性,这样就给相关的属性加了 PropertyChanged 事件

    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";
}

总结: 

(1). 在其他属性的 set 方法里给其他属性赋值时最好用小写的属性名,而不要直接调用 OtherProperty_set 方法, 否则容易进入死循环

(2). 可以看到step 3 里,添加了 ControlBindingsCollection 的扩展方法,这样就不用担心"PropertyName" 之类容易出错的看起来很恶心的写法,事实上 IDE 的提示功能使 lambda 表达式写起来非常方便,并且在 Build 的时候就可以查出属性名是否对应,提高写代码的效率,减少出错的机会

DataBindings 与 INotifyPropertyChanged 实现自动刷新 WinForm 界面的更多相关文章

  1. 使用线程操作刷新Winform界面

    主窗体中添加代码 public FrmMain() { InitializeComponent(); System.Threading.Thread thread = new System.Threa ...

  2. sublime3安装liveload,实现前端自动F5刷新html界面

    这两天倒腾编辑器,atom实在太大了,还是sublime好用 以前一直用sublime2, 然后更新到sublime3, 然后把一些必要的插件安装了一下:liveload(自动刷新): package ...

  3. C# Winform频繁刷新导致界面闪烁解决方法

    C#Winform频繁刷新导致界面闪烁解决方法 一.通过对窗体和控件使用双缓冲来减少图形闪烁(当绘制图片时出现闪烁时,使用双缓冲) 对于大多数应用程序,.NET Framework 提供的默认双缓冲将 ...

  4. winform频繁刷新导致界面闪烁解决方法

    转自龙心文 原文 winform频繁刷新导致界面闪烁解决方法 一.通过对窗体和控件使用双缓冲来减少图形闪烁(当绘制图片时出现闪烁时,使用双缓冲) 对于大多数应用程序,.NET Framework 提供 ...

  5. Web界面和Winform界面生成,代码生成工具

    在上面一篇随笔<代码生成工具之界面快速生成>介绍了代码生成工具Database2Sharp的界面生成操作,其中介绍了Web界面(包括列表界面.内容显示.内容编辑界面的生成,另外还介绍了Wi ...

  6. NanUI for Winform发布,让Winform界面设计拥有无限可能

    如今,尽管WPF.UWP大行其道,大有把Winform打残干废的趋势.但是还是有那么一波顽固不化的老家伙们固守着Winform,其中就包括我. 好吧,既然都说Winform做得软件不如WPF界面美观效 ...

  7. 告诉你吧,一套皮肤在winform与wpf开发模式下实现的界面效果同样精彩,winform界面和wpf界面。

    一.同一资源: 二.先上软件界面: (1)wpf界面: 在wpf中实现这样类似web风格的软件界面就不用我多说了,在wpf实现这样的风格是很简单的,完全像网页设计一样的. (2)winform界面 在 ...

  8. 循序渐进开发WinForm项目(4)--Winform界面模块的集成使用

    随笔背景:在很多时候,很多入门不久的朋友都会问我:我是从其他语言转到C#开发的,有没有一些基础性的资料给我们学习学习呢,你的框架感觉一下太大了,希望有个循序渐进的教程或者视频来学习就好了. 其实也许我 ...

  9. 自动刷新ALV

    转自http://blog.sina.com.cn/s/blog_701594f40100l8ms.html ABAP:利用SAP定时器自动刷新ALV 曾于无意之中发现,SAP系统中有个名为CL_GU ...

随机推荐

  1. Hibernate项目里配置环境时,jar包配置不当会对测试结果产生影响。

    问题情况: 如下图所示,该图中,显示了一堆错误的jar包.这是导致了Junit4测试通过了,数据库却没反应的原因. 原因: 之所以出现这种情况,eclipse其实已经告诉了我们原因.如下图所示,这些j ...

  2. javascript获取标签样式(获取背景为例)

    function getStyle(el){ if(window.getComputedStyle){ return window.getComputedStyle(el,null); } retur ...

  3. NHibernate——基本映射(5)

    一.映射定义概括 1.1 映射定义(Mapping declaration) 对象和关系数据库之间的映射是用一个XML文档(XML document)来定义的.这个映射文档被设计为易读的,并且可以手工 ...

  4. (转)JSON对象长度和遍历方法

    最近在修改一个HTML页面的JS的时候遍历JSON对象,却怎么也调试不通过.怪这个HTML网页不知道用了什么方法禁止了js错误提示,刚开始的时候不知道有这个问题,用chrome的开发人员工具都没发现错 ...

  5. Polygon对象

    Polylgon对象是由一个或多个Ring对象的有序集合,它可以是由单个Ring 对象构成,也可以使用多个Ring组成.Polygon通常用来代表有面积的多边形矢量对象,如行政区,建筑物等.

  6. StringEscapeUtils的使用

    使用commons-lang.jar import org.apache.commons.lang.StringEscapeUtils; public class T { public static ...

  7. java下tcp的socket连接案例

    package cn.stat.p4.ipdemo; import java.io.BufferedReader; import java.io.IOException; import java.io ...

  8. 【转】char *str 和 char str[]的区别

    char str[] = "abcd";定义了一个局部字符数组,返回它的地址肯定是一个已经释放了的空间的地址. 此函数返回的是内部一个局部字符数组str的地址,且函数调用完毕后 此 ...

  9. boost

    参考博客 http://www.cnblogs.com/lidabo/p/3805487.html http://www.cppblog.com/Robertxiao/archive/2013/01/ ...

  10. 搭建hadoop2.6.0集群环境

    一.规划 (一)硬件资源 10.171.29.191 master 10.171.94.155  slave1 10.251.0.197 slave3 (二)基本资料 用户:  jediael 目录: ...