阅读 117

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

文章分类
后端
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐