专栏首页菩提树下的杨过Silverlight:双向绑定综合应用-自动更新集合汇总字段

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

       
    }
}

运行效果:

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • silverlight + wcf(json格式) + sqlserver存储过程分页

    silverlight并没有提供现成的分页控件,百度了一圈,也没有发现aspx中好用的类似AspNetPager成熟控件,网上现有的一些分页代码,很多也是基于1...

    菩提树下的杨过
  • 闲来无事,倒腾了一个简单的silverlight视频播放器

    近二日闲来无事,把silverlight的官方文档瞅了瞅,倒腾了一个简单的视频播放器,顺便也测试了下能否播放传说中的h.264,最终效果如下: ? http:/...

    菩提树下的杨过
  • 委托示例(利用委托对不同类型的对象数组排序)

    using System; using System.Collections.Generic; using System.Text; namespac...

    菩提树下的杨过
  • 【面试题】SpringCloud架构中如何保证定时任务只在一个服务在执行

    版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/...

    林老师带你学编程
  • Caffe Data层 - ImageDataLayer

    AIHGF
  • 高质量编码------属性查询

    为了实现灵活查询供水管网,根据地址,材质,管径组合汇总查询和条件查询。(图片数据经过处理,不涉及地理坐标保密信息)

    MiaoGIS
  • 吴军北京来信:人工智能应该变成通识教育,区块链不是炒概念

    大数据文摘
  • 1313 质因数分解

    1313 质因数分解 2012年NOIP全国联赛普及组 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 青铜 Bronze 题目...

    attack
  • 新手学Linux(二)----使用 Vagrant 打造跨平台开发环境(一)

    做Web开发少不了要在本地搭建好开发环境,虽然说目前各种脚本都有对应的Windows版,甚至是一键安装包,但很多时候和Windows环境的相性并不是那么好,各...

    令仔很忙
  • Java 解决Emoji表情过滤问题

    UTF-8编码有可能是两个、三个、四个字节。Emoji表情是4个字节,而Mysql的utf8编码最多3个字节,所以数据插不进去。

    崔笑颜

扫码关注云+社区

领取腾讯云代金券