WPF使用 INotifyPropertyChanged 实现数据驱动
WPF使用 INotifyPropertyChanged 实现数据驱动
如下图,有这么一个常见需求,在修改表单明细的苹果价格时,总价会改变,同时单据总和也随之改变。
按照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; }
我们再调试运行一次
完美!!!
来源https://www.cnblogs.com/luguangguang/p/14925663.html