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

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

问题:

输入某物品 单价 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. CentOS7 安装chrome浏览器

    本篇文章主要记录如何在CentOS7.0上安装chrome. 1.配置yum下载源: 在目录 /etc/yum.repos.d/ 下新建文件 google-chrome.repo, 并且在该文件中添加 ...

  2. Python顺序与range和random

    range([start,] stop[, step]) start是开始,stop是停下,step是步长. >>> range(10) range(0, 10) >>& ...

  3. chmod

    0表示没有权限,1表示可执行权限,2表示可写权限,4表示可读权限数字与字符对应关系如下:r=4,w=2,x=1若要rwx属性则4+2+1=7若要rw-属性则4+2=6:若要r-x属性则4+1=5命令: ...

  4. 配置Samba服务

    1. samba服务用在什么地方?samba服务用于把Linux服务器上的文件或者打印接共享给windows或者Linux.2. 在samba服务的配置文件中,[global]配置部分的securit ...

  5. linux局域网不能相互访问

    1.关闭防火墙 /etc/init.d/iptable stop   2.关闭selinux   1.临时禁用SELinux: root@server# setenforce 0  这样重启服务器之后 ...

  6. itext poi 学习之旅 (3)读取数据库信息并由excel展现出来

    DBConnection.java 连接数据库操作 package com.zkbj.poi; import java.sql.Connection; import java.sql.DriverMa ...

  7. Request 分别获取具有相同 name 属性表单元素值

    html 中是允许多个具有相同name属性的元素的,例如 <div> <input name="txtName" id="txtFirstName&qu ...

  8. IIS下 Yii Url重写

    下载URL重写组件 http://www.microsoft.com/zh-cn/download/details.aspx?id=7435 导入官方提供的.htaccess文件 Options +F ...

  9. GCD 单例

    + (ThemeManager *)sharedInstance { static   dispatch_once_t   once; //只执行一次 static   ThemeManager   ...

  10. Android Studio builde.gradle 配置说明

    //声明是Android应用程序 apply plugin: 'com.android.application' android { //编译SDK版本 compileSdkVersion 21 bu ...