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

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

问题:

输入某物品 单价 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. ASP.net 前台页面通过ID获取控件

    asp.net的服务器控件的ID通常只能在服务器端很好的识别,客户端需要通过ClientID获得控件 1.通过js获得   var controlID = "<%=controlID. ...

  2. 《大话移动 APP 测试》

    <大话移动 APP 测试> wiki: Software testing 第5章 常用工具介绍和实践 Android.iOS Monkey Android SDK 提供的一个工具:发送伪随 ...

  3. eclipse使用和优化配置

    一.简介 eclipse 可谓是Java开发界的神器,基本占据了大部分的Java开发市场,而且其官方还对其他语言提供支持,如C++,Ruby,JavaScript等等.为 什么使用它?我想离不开下面的 ...

  4. 恢复root用户目录,及~目录

    普通帐号登su;mkdir /root;chown root:root /root cp -R /etc/skel/.[!.]* ./

  5. Ubuntu pip 安装网络爬虫框架 scrapy 出现的错误

    1.问题     File "/usr/bin/pip", line 9, in <module> load_entry_point('pip==1.5.4', 'co ...

  6. js 当前系统时间

    <script language=Javascript> function time(){ //获得显示时间的div t_div = document.getElementById('sh ...

  7. jquery正则表达式显示文本框输入范围 只能输入数字、小数、汉字、英文字母的方法

    正则表达式限制文本框只能输入数字 许多时候我们在制作表单时需要限制文本框输入内容的类型,下面我们用正则表达式限制文本框只能输入数字.小数点.英文字母.汉字等各类代码.1.文本框只能输入数字代码(小数点 ...

  8. ios开发之常用宏的定义

    有些时候,我们需要将代码简洁化,这样便于读代码.我们可以将一些不变的东东抽取出来,将变化的东西作为参数.定义为宏,这样在写的时候就简单多了. 下面例举了一些常用的宏定义和大家分享: 1. 判断设备的操 ...

  9. Regular expression cheat sheet

    \s white-space characters \S Non-white-space characters \d digital numbers \D non-digital numbers \w ...

  10. cf B. Valera and Contest

    http://codeforces.com/contest/369/problem/B 先对k个处理,先处理sk%k个为sk/k+1,如果sk/k==0,k个数都为sk/k:对与剩下的数也按照同样的方 ...