Silverlight:双向绑定综合应用-自动更新集合汇总字段
场景:有一家公司(类名:Company),它有N多员工(类名:Employee)。要在界面上用网格显示所有员工的姓名、工资,并且当操作用户在网格里对员工进行增减或修改其工资时,能自动汇总出员工工资的总和并显示出来。
员工类 Employee代码如下:
/// <summary>
/// 员工类
/// </summary>
public class Employee:INotifyPropertyChanged
{
private string _name = "";
public string Name { set { _name = value; OnPropertyChanged("Name"); } get { return _name; } }
private int _salary = 0;
public int Salary
{
get { return _salary; }
set { _salary = value; OnPropertyChanged("Salary"); }
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null) {
PropertyChanged(this,new PropertyChangedEventArgs(propertyName));
}
}
}
公司类 Company代码原型如下:
public class Company {
private ObservableCollection<Employee> _employeeCollection = new ObservableCollection<Employee>();
/// <summary>
/// 公司的"员工集合"
/// </summary>
public ObservableCollection<Employee> EmployeeCollection
{
get { return _employeeCollection; }
}
private void computeSalaryTotal()
{
_salaryTotal = _employeeCollection.Sum(c => c.Salary);
}
private int _salaryTotal = 0;
/// <summary>
/// 工资汇总
/// </summary>
public int SalaryTotal
{
get
{
computeSalaryTotal();
return _salaryTotal;
}
}
}
常规解决办法:
可以在Grid每行“工资”字段对应的TextBox上,注册TextChanged或LostFocus事件,在输入值变化或失去焦点时,去更新总和。
这是很容易想到的办法,但是并不优雅,原因:
1、每行的TextBox上都要去绑定事件,并在xaml.cs上写代码处理类似 TextBoxTotal.text = company.SalaryTotal 的逻辑。这样界面逻辑代码与UI绑得太紧,应对变化的能力有限。比如以后将TextBox换成其它形式的控件,一旦并不支持TextChanged事件,原来的代码就得修改。
2、代码重用率低,如果其它界面上也需要类似的需求,只能把本页面Xaml、Xaml.cs的代码复制一遍,如果以后需求有变化,更增加了维护成本。
所以,理想的解决方法,应该是Company类自身能“智能感知”员工的变化,并自动更新工资汇总字段。(即:员工Employee的工资有变化时,应该主动通知Company类。这跟实际公司的运营管理也比较接近,人事给员工调整了工资,肯定会主动通知财务,所以财务肯定也就知道了最新的工资汇总数据。)
这时,双向绑定就再一次体现了这种威力,我们把Company类改造一下:
public class Company:INotifyPropertyChanged {
private ObservableCollection<Employee> _employeeCollection = new ObservableCollection<Employee>();
/// <summary>
/// 公司的"员工集合"
/// </summary>
public ObservableCollection<Employee> EmployeeCollection
{
get { return _employeeCollection; }
}
/// <summary>
/// 构造函数
/// </summary>
public Company()
{
_employeeCollection.CollectionChanged += new NotifyCollectionChangedEventHandler(_employeeCollection_CollectionChanged);
}
/// <summary>
/// 员工有“增减”时自动触发
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void _employeeCollection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
//重新计算工资总和
computeSalaryTotal();
//每个员工的“工资”属性变化时,自动触发指定事件
foreach (var item in _employeeCollection)
{
item.PropertyChanged -= new PropertyChangedEventHandler(item_PropertyChanged);
item.PropertyChanged += new PropertyChangedEventHandler(item_PropertyChanged);
}
}
/// <summary>
/// 员工属性变化时自动调用本方法
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void item_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
//如果是"工资"属性变化,则自动重新计算工资汇总
if (e.PropertyName == "Salary")
{
computeSalaryTotal();
}
}
private void computeSalaryTotal()
{
_salaryTotal = _employeeCollection.Sum(c => c.Salary);
OnPropertyChanged("SalaryTotal");//工资总合重新计算后,向外广播事件,以便UI能自动更新
}
private int _salaryTotal = 0;
/// <summary>
/// 工资汇总
/// </summary>
public int SalaryTotal
{
get
{
return _salaryTotal;
}
}
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
这里,我们充分利用了INotifyPropertyChanged接口的PropertyChanged事件,以及INotifyCollectionChanged接口的CollectionChanged事件,实现了自动通知。
这样一来,界面UI部分就轻松多了,只需要简单的绑定即可。 Xaml部分:
<UserControl x:Class="XmlClassSerelizer.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400" xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk">
<StackPanel x:Name="LayoutRoot" Background="White">
<sdk:DataGrid AutoGenerateColumns="False" HorizontalAlignment="Center" Margin="0,10,0,0" Name="dataGrid1" VerticalAlignment="Center"
ItemsSource="{Binding EmployeeCollection}">
<sdk:DataGrid.Columns>
<sdk:DataGridTemplateColumn Header="姓名">
<sdk:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding Name,Mode=TwoWay}" VerticalAlignment="Center" Margin="1"></TextBox>
</DataTemplate>
</sdk:DataGridTemplateColumn.CellTemplate>
</sdk:DataGridTemplateColumn>
<sdk:DataGridTemplateColumn Header="工资">
<sdk:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding Salary,Mode=TwoWay}" VerticalAlignment="Center" Margin="1"></TextBox>
</DataTemplate>
</sdk:DataGridTemplateColumn.CellTemplate>
</sdk:DataGridTemplateColumn>
<sdk:DataGridTemplateColumn Header="操作">
<sdk:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Click="RemoveEmployee" HorizontalAlignment="Center" VerticalAlignment="Center" Padding="10,1">-</Button>
</DataTemplate>
</sdk:DataGridTemplateColumn.CellTemplate>
</sdk:DataGridTemplateColumn>
</sdk:DataGrid.Columns>
</sdk:DataGrid>
<StackPanel HorizontalAlignment="Center" Orientation="Horizontal" Margin="5">
<TextBlock VerticalAlignment="Center">工资总合:</TextBlock>
<TextBlock Text="{Binding SalaryTotal, Mode=TwoWay}" Margin="5,0,5,0" Width="60"></TextBlock>
</StackPanel>
<StackPanel HorizontalAlignment="Center" Orientation="Horizontal" Margin="5">
<Button Click="AddEmployee" Padding="10,1">+</Button>
</StackPanel>
</StackPanel>
</UserControl>
Xaml.cs部分:
using System.Windows;
using System.Windows.Controls;
namespace XmlClassSerelizer
{
public partial class MainPage : UserControl
{
Company c = new Company();
public MainPage()
{
InitializeComponent();
this.Loaded += new RoutedEventHandler(MainPage_Loaded);
}
void MainPage_Loaded(object sender, RoutedEventArgs e)
{
Employee e1 = new Employee(){ Name="张三",Salary=3000};
Employee e2 = new Employee(){ Name="李四",Salary=4000};
c.EmployeeCollection.Add(e1);
c.EmployeeCollection.Add(e2);
this.DataContext = c;
}
/// <summary>
/// 删除员工
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void RemoveEmployee(object sender, RoutedEventArgs e)
{
var emp = (sender as Button).DataContext as Employee;
if (emp != null) { c.EmployeeCollection.Remove(emp); }
}
/// <summary>
/// 添加员工
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void AddEmployee(object sender, RoutedEventArgs e)
{
c.EmployeeCollection.Add(new Employee() { Name = "新人", Salary = 1000 });
}
}
}
运行效果:
- Java面向对象抽象类实例练习
- 经典JS闭包面试题
- Java面向对象接口的应用实例练习
- 【机器学习笔记之八】使用朴素贝叶斯进行文本的分类
- 【Java学习笔记之二十二】解析接口在Java继承中的用法及实例分析
- 扫一扫二维码就能打开网站,就能添加联系人,就能链接wifi(续)
- Java面向对象抽象类案例分析
- 【Java学习笔记之二十三】instanceof运算符的用法小结
- 基础才是重中之重~多线程的代价~我的内存都被吃了!
- 【Java学习笔记之二十八】深入了解Java8新特性
- 【Java学习笔记之二十四】对Java多态性的一点理解
- 【Java学习笔记之二十六】深入理解Java匿名内部类
- 【Java学习笔记之二十五】初步认知Java内部类
- AIM Tech Round 4 (Div. 2)(A,暴力,B,组合数,C,STL+排序)
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- (译)kubectl 的奇技淫巧
- Python实现主播人气排行榜,带你发现人气王
- (译)Kubernetes:移除 CPU 限制,服务运行更快
- python_不误正业之贪吃蛇
- 如何让你在众多二手车中挑中满意的?python帮你实现(附源码)
- python苦短_SVM测试
- 招聘信息太多,哪家职位才是适合你的?Python采集招聘信息
- 福利来啦,送给大家一个小游戏的源码,不要错过哟(复制粘贴运行即可玩)
- 今天抠图,Python实现一键换底片!想换什么换什么(附源码)
- 你想不到的沙雕,8行代码Python实现GIF图倒放,每天的快乐源泉
- python爬虫-首医
- 2万8千张图片如何用python组成一张(简洁明了附源码)
- 10 种常用 Matplotlib 图的 Python 代码
- Python爬取某宝商品数据案例:100页的价格、购买人数等数据
- 如何让你的图片加上一层天气的特效?Python帮你解决(附源码)