如下图,有这么一个常见需求,在修改表单明细的苹果价格时,总价会改变,同时单据总和也随之改变。

按照Winfrom事件驱动的思想来做的话,我们就需要在将UI的修改函数绑定到CellEdit事件中来实现。

但是对于WPF,我们完全可以利用WPF的 INotifyPropertyChanged 接口来实现。

首先我们通过nuget引入WPF常用的自动首先通知的第三方包 PropertyChanged.Fody ,它的作用是凡是实现了 INotifyPropertyChanged 的类的属性默认都会通知前端

然后建立订单和订单明细两个基本类,并实现 INotifyPropertyChanged 接口

   public class DJ : INotifyPropertyChanged
{
public int ID { get; set; }
public double SumPrice
{
get
{
return MXs.Sum(it => it.Price);
}
}
public ObservableCollection<Models.DJMX> MXs { get; set; } = new ObservableCollection<DJMX>();
public event PropertyChangedEventHandler PropertyChanged;
}
  public class DJMX : INotifyPropertyChanged
{
public object DJ { get; set; }
public object MainWindowViewModel { get; set; }
public string Name { get; set; }
private double price; public double Price
{
get { return price; }
set
{
price = value;
}
} public event PropertyChangedEventHandler PropertyChanged; }

前端代码

  <Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<DataGrid AutoGenerateColumns="False" CanUserAddRows="False" ItemsSource="{Binding DJs}">
<DataGrid.Columns>
<DataGridTextColumn Width="*" Header="订单号" Binding="{Binding ID}"/>
<DataGridTextColumn Width="*" Header="总价" Binding="{Binding SumPrice}"/>
</DataGrid.Columns>
<DataGrid.RowDetailsTemplate>
<DataTemplate>
<DataGrid AutoGenerateColumns="False" CanUserAddRows="False" SelectionUnit="CellOrRowHeader"
ItemsSource="{Binding MXs}">
<DataGrid.Columns>
<DataGridTextColumn Header="商品名" Width="100" Binding="{Binding Name}"/>
<DataGridTextColumn Header="价格" Width="100" Binding="{Binding Price, UpdateSourceTrigger=PropertyChanged}"/>
</DataGrid.Columns>
</DataGrid>
</DataTemplate>
</DataGrid.RowDetailsTemplate>
</DataGrid>
<StackPanel Grid.Row="1" VerticalAlignment="Center" Orientation="Horizontal">
<TextBlock Text="单据总和: "/>
<TextBlock Text="{Binding AllSumPrice}"/>
</StackPanel>
</Grid>

前端对应的ViewModel

  public class MainWindowViewModel : INotifyPropertyChanged
{
public MainWindowViewModel()
{
DJs = new ObservableCollection<Models.DJ>()
{
new Models.DJ(){ ID=1},
new Models.DJ(){ ID=2},
new Models.DJ(){ ID=3},
new Models.DJ(){ ID=4},
new Models.DJ(){ ID=5}
}; foreach (var dj in DJs)
{
dj.MXs = new ObservableCollection<Models.DJMX>()
{
new Models.DJMX() { Name="苹果", Price=100 },
new Models.DJMX() { Name="鸭梨", Price=200 },
new Models.DJMX() { Name="香蕉", Price=300 },
};
}
}
public double AllSumPrice
{
get
{
return DJs.Sum(it => it.SumPrice);
}
}
public ObservableCollection<Models.DJ> DJs { get; set; } = new ObservableCollection<Models.DJ>(); public event PropertyChangedEventHandler PropertyChanged; }

运行调试一下

发现价格修改并没有影响到总价和总和, 结果并不如预期的那样,我们分析一下:

来看总价和总和属性的定义,两个都是只读的,因为没有Set的属性,所以Fody是无法进行通知的,准确的说,是 PropertyChanged 没有设置到该属性。

例如,价格的属性代码完整其实是这样的

在价格属性改变后,会通过绑定价格属性的前端进行修改。

所以,如果我们想让价格修改的同时,总价和总和也要通知到,即可以在价格属性的Set方法中,增加通过SumPrice和AllSumPrice的代码。

而  PropertyChanged 需要传入一个当前属性所在的示例和当前属性的名称,在这里,我通过修改 OnPropertyChanged 增加一个 OnNavigationObjDJPropertyChanged 方法,

另外订单明细也需要定义两个新的obj属性用来存放需要通知的实例,达到类似EF导航属性的效果,最终的 DJMX 类代码如下

  public class DJMX : INotifyPropertyChanged
{
public object DJ { get; set; }
public object MainWindowViewModel { get; set; }
public string Name { get; set; }
private double price; public double Price
{
get { return price; }
set
{
price = value;
OnPropertyChanged(new PropertyChangedEventArgs("price")); OnNavigationObjDJPropertyChanged(DJ, new PropertyChangedEventArgs("SumPrice")); //new
OnNavigationObjDJPropertyChanged(MainWindowViewModel, new PropertyChangedEventArgs("AllSumPrice")); //new
}
} public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (PropertyChanged != null)
{
PropertyChanged(this, e);
}
}

     //new
public void OnNavigationObjDJPropertyChanged(object objTargert,PropertyChangedEventArgs e)
{
if (PropertyChanged != null&& objTargert!= null)
{
PropertyChanged(objTargert, e);
}
}
}

同时 ViewModel 的代码也需要在数据实例化时,增加传入两个通知的实例,代码如下:

    public class MainWindowViewModel : INotifyPropertyChanged
{
public MainWindowViewModel()
{
DJs = new ObservableCollection<Models.DJ>()
{
new Models.DJ(){ ID=1},
new Models.DJ(){ ID=2},
new Models.DJ(){ ID=3},
new Models.DJ(){ ID=4},
new Models.DJ(){ ID=5}
}; foreach (var dj in DJs)
{
dj.MXs = new ObservableCollection<Models.DJMX>()
{
new Models.DJMX() { DJ=dj, MainWindowViewModel=this, Name="苹果", Price=100 }, //changed
new Models.DJMX() { DJ=dj, MainWindowViewModel=this, Name="鸭梨", Price=200 }, //changed
new Models.DJMX() { DJ=dj, MainWindowViewModel=this, Name="香蕉", Price=300 }, //changed
};
}
}
public double AllSumPrice
{
get
{
return DJs.Sum(it => it.SumPrice);
}
}
public ObservableCollection<Models.DJ> DJs { get; set; } = new ObservableCollection<Models.DJ>(); public event PropertyChangedEventHandler PropertyChanged; }

我们再调试运行一次

 完美!!!

自动判断
中文
中文(简体)
中文(香港)
中文(繁体)
英语
日语
朝鲜语
德语
法语
俄语
泰语
南非语
阿拉伯语
阿塞拜疆语
比利时语
保加利亚语
加泰隆语
捷克语
威尔士语
丹麦语
第维埃语
希腊语
世界语
西班牙语
爱沙尼亚语
巴士克语
法斯语
芬兰语
法罗语
加里西亚语
古吉拉特语
希伯来语
印地语
克罗地亚语
匈牙利语
亚美尼亚语
印度尼西亚语
冰岛语
意大利语
格鲁吉亚语
哈萨克语
卡纳拉语
孔卡尼语
吉尔吉斯语
立陶宛语
拉脱维亚语
毛利语
马其顿语
蒙古语
马拉地语
马来语
马耳他语
挪威语(伯克梅尔)
荷兰语
北梭托语
旁遮普语
波兰语
葡萄牙语
克丘亚语
罗马尼亚语
梵文
北萨摩斯语
斯洛伐克语
斯洛文尼亚语
阿尔巴尼亚语
瑞典语
斯瓦希里语
叙利亚语
泰米尔语
泰卢固语
塔加路语
茨瓦纳语
土耳其语
宗加语
鞑靼语
乌克兰语
乌都语
乌兹别克语
越南语
班图语
祖鲁语

自动选择
中文
中文(简体)
中文(香港)
中文(繁体)
英语
日语
朝鲜语
德语
法语
俄语
泰语
南非语
阿拉伯语
阿塞拜疆语
比利时语
保加利亚语
加泰隆语
捷克语
威尔士语
丹麦语
第维埃语
希腊语
世界语
西班牙语
爱沙尼亚语
巴士克语
法斯语
芬兰语
法罗语
加里西亚语
古吉拉特语
希伯来语
印地语
克罗地亚语
匈牙利语
亚美尼亚语
印度尼西亚语
冰岛语
意大利语
格鲁吉亚语
哈萨克语
卡纳拉语
孔卡尼语
吉尔吉斯语
立陶宛语
拉脱维亚语
毛利语
马其顿语
蒙古语
马拉地语
马来语
马耳他语
挪威语(伯克梅尔)
荷兰语
北梭托语
旁遮普语
波兰语
葡萄牙语
克丘亚语
罗马尼亚语
梵文
北萨摩斯语
斯洛伐克语
斯洛文尼亚语
阿尔巴尼亚语
瑞典语
斯瓦希里语
叙利亚语
泰米尔语
泰卢固语
塔加路语
茨瓦纳语
土耳其语
宗加语
鞑靼语
乌克兰语
乌都语
乌兹别克语
越南语
班图语
祖鲁语

有道翻译
百度翻译
谷歌翻译
谷歌翻译(国内)

翻译 朗读 复制 正在查询,请稍候…… 重试 朗读 复制 复制 朗读 复制 via 百度翻译

WPF使用 INotifyPropertyChanged 实现数据驱动的更多相关文章

  1. WPF研究之道——数据驱动UI

    如果有人问你wpf和winform的区别,也许你会说,wpf的界面比较漂亮,wpf有诸多新的理念,的确如此.我今天想说的是wpf的数据驱动UI的理念. 传统的winform,想要更新界面内容,是不是必 ...

  2. WPF中INotifyPropertyChanged用法与数据绑定

    在WPF中进行数据绑定的时候常常会用到INotifyPropertyChanged接口来进行实现,下面来看一个INotifyPropertyChanged的案例. 下面定义一个Person类: usi ...

  3. wpf中INotifyPropertyChanged的用法

    using System;using System.Collections.Generic;using System.ComponentModel;using System.Linq;using Sy ...

  4. WPF Binding INotifyPropertyChanged 多线程 深入理解

    例子 先来看一个例子 Person.cs public class Person : ObservableObject,INotifyPropertyChanged { private string ...

  5. [WPF]入门理解Binding 数据驱动思想

    站在一个WinForm程序员的角度去考虑,他会做这样几件事情: 响应slider1的ValueChanged事件,在事件处理函数中让textBox1显示slider1的Value 响应textBox1 ...

  6. WPF 实现INotifyPropertyChanged .Net Framework 4.5

    自己动手写了一个基类来实现INotifyPropertyChanged接口,以后可以直接使用. using System.ComponentModel; using System.Runtime.Co ...

  7. WPF ObservableCollection,INotifyPropertyChanged

    xaml: <DockPanel Margin="10">                <StackPanel DockPanel.Dock="Rig ...

  8. WPF 之 INotifyPropertyChanged 接口的使用 (一)

    一.INotifyPropertyChanged 的基本概念 ​ INotifyPropertyChanged 的作用:通知客户端属性值已经更改.详细信息见:INotifyPropertyChange ...

  9. .NET: WPF Data Binding

    WPF是分离UI和Logic的最佳工具,不同于Window Form的事件驱动原理,WPF采用的是数据驱动,让UI成为了Logic的附属,达到分离的效果. 本篇主要讲讲wpf的精华:data bind ...

随机推荐

  1. python工业互联网应用实战14——单元测试覆盖率

    前面的章节我们完成了任务管理主要功能的开发及单元测试编写,可如何知道单元测试效果怎么样呢?测试充分吗?还有没有没有测到的地方呢? 本章节我们介绍一个统计测试代码覆盖率的利器Coverage,Cover ...

  2. QFNU-ACM 2019.5.23组队赛 2019山东省赛复现

    A.Calandar 题意:一年12个月,一个月30天,5天一周,已知某天的年月日星期数,求所给年月日的星期数是多少 思路:直接进行计算,其实每个月每年都是等长度的就使得计算的时候忽略年月,可以直接进 ...

  3. OpenCV on Mac OSX: A step-by-step guide

    I'm using OpenCV for my 4th year design project and setting it up was a huge pain. I had to look thr ...

  4. Spring 实现策略模式--自定义注解方式解耦if...else

    策略模式 定义 定义一簇算法类,将每个算法分别封装起来,让他们可以互相替换,策略模式可以使算法的变化独立于使用它们的客户端 场景 使用策略模式,可以避免冗长的if-else 或 switch分支判断 ...

  5. 用JIRA管理你的项目——(三)基于LDAP用户管理

    JIRA提供了基于LDAP方式的用户管理,也就是用户密码的管理交给LDAP,而JIRA只管理用户在系统中的角色. 要打开JIRA的LDAP设置,首先需要验证下你的LDAP服务是否正常! 几乎有所有的L ...

  6. Ubuntu 20.04 配置多网卡链路聚合

    Ubuntu 20.04 配置多网卡链路聚合 多网卡IP配置 首先查看网卡信息 root@it:~# ip add 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65 ...

  7. cento 7使用minicom串口调试工具

    cento 7使用minicom串口调试工具   因为要使用串口配置设备,所以考虑要用串口管理软件,windows下比较容易了,直接用超级终端也好,用CRT也可以,CentOS系统稍微麻烦一点,前几天 ...

  8. k8s健康检查(9)

    一.默认的健康检查 强大的自愈能力是 Kubernetes 这类容器编排引擎的一个重要特性.自愈的默认实现方式是自动重启发生故障的容器.除此之外,用户还可以利用 Liveness 和 Readines ...

  9. 9.1 ps:查看进程

    ps命令 用于列出执行ps命令的那个时刻的进程快照,就像用手机给进程照了一张照片.如果想要动态地显示进程的信息,就需要使用top命令,该命令类似于把手机切换成录像模式.因为ps命令的功能实在是太多了, ...

  10. 关于lua闭包导致引用无法释放内存泄露

    最近项目存在严重的内存泄漏问题,每次切level 会增加20M无法释放的内存,翻遍了项目用了多个工具,查询资料等 发现项目中两种存在内存泄露的情况 1.lua闭包的不当使用,对比包的引用要及时 释放. ...