Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/wpf/12.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 通知WPF/C中可观察集合中的项目更改#_C#_Wpf_Mvvm_Data Binding_Prism - Fatal编程技术网

C# 通知WPF/C中可观察集合中的项目更改#

C# 通知WPF/C中可观察集合中的项目更改#,c#,wpf,mvvm,data-binding,prism,C#,Wpf,Mvvm,Data Binding,Prism,在我的WPF项目中,我有一个服务 EF模型定义为 public class Service { public int ID public string Name public decimal Price } 在我的viewmodel中,我有 public class ReceiptViewModel : BindableBase { private ObservableCollection<Service> _services; public Obser

在我的WPF项目中,我有一个
服务
EF模型定义为

public class Service
{
  public int ID
  public string Name
  public decimal Price
}
在我的viewmodel中,我有

public class ReceiptViewModel : BindableBase
{
    private ObservableCollection<Service> _services;
    public ObservableCollection<Service> Services
    {
        get { return _services; }
        set { SetProperty(ref _services, value, () => RaisePropertyChanged(nameof(Total))); }
    }

    public decimal Total => Services.Sum(s => s.Price);
}
公共类ReceiptViewModel:BindableBase { 私人可观测收集服务; 公共收集服务 { 获取{return\u services;} set{SetProperty(ref _services,value,()=>RaisePropertyChanged(nameof(Total));} } 公共十进制总数=>Services.Sum(s=>s.Price); } Total绑定到我视图中的textblock,而我的可观察集合绑定到一个itemscontrol,其中包含textbox


我希望每次用户从UI更改我收藏中的一个价格时,我的总文本块都会更改。我怎样才能做到这一点呢?

这很简单,但很乏味:

  • 创建一个实现INotifyPropertyChanged的
    ServiceViewModel
  • 将数据库中的集合映射到视图模型的可观察集合(
    ReceiptViewModel.Services
  • 聆听可观察集合的所有更改,并附加到所有添加项的
    PropertyChanged
    (并从所有删除项中分离,同时附加最初存在的所有项)
  • 每当其中一个
    ServiceViewModels
    发生更改时,设置新的
    ReceiptViewModel.Total
    (如果您将其设置为计算属性,则提升
    ReceiptViewModel.PropertyChanged
  • ServiceViewModel
    或视图模型集合更改时,也会更新数据库

  • 如果您改用
    事件聚合器
    ,第3步可能会更容易/更简单/更明显。

    您应该自己实现它,这是我的可观察收集示例(并且您还需要在更改收集项目时订阅并引发OnPropertyChanged(nameof(Total)),或者将collectionEx的实现更改为引发collection changed事件

        public class ObservableCollectionEx<T> : ObservableCollection<T> where T : INotifyPropertyChanged
    {
      public ObservableCollectionEx(IEnumerable<T> initialData) : base(initialData)
      {
          Init();
      }
    
      public ObservableCollectionEx()
      {
          Init();
      }
    
      private void Init()
      {
          foreach (T item in Items)
             item.PropertyChanged += ItemOnPropertyChanged;
    
          CollectionChanged += FullObservableCollectionCollectionChanged;
      }
    
      private void FullObservableCollectionCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
      {
          if (e.NewItems != null)
          {
             foreach (T item in e.NewItems)
             {
                if (item != null)
                   item.PropertyChanged += ItemOnPropertyChanged;
             }
          }
    
          if (e.OldItems != null)
          {
              foreach (T item in e.OldItems)
              {
                  if (item != null)
                      item.PropertyChanged -= ItemOnPropertyChanged;
              }
          }
      }
    
        private void ItemOnPropertyChanged(object sender, PropertyChangedEventArgs e)
            => ItemChanged?.Invoke(sender, e);
    
        public event PropertyChangedEventHandler ItemChanged;
    }
    
    公共类ObservableCollectionEx:ObservableCollection,其中T:INotifyPropertyChanged
    {
    公共ObservableCollectionEx(IEnumerable initialData):基(initialData)
    {
    Init();
    }
    公共可观测集合
    {
    Init();
    }
    私有void Init()
    {
    foreach(项目中的T项目)
    item.PropertyChanged+=ItemOnPropertyChanged;
    CollectionChanged+=FullObservableCollectionCollectionChanged;
    }
    私有void FullObservableCollectionCollectionChanged(对象发送方,NotifyCollectionChangedEventArgs e)
    {
    如果(如NewItems!=null)
    {
    foreach(e.NewItems中的T项)
    {
    如果(项!=null)
    item.PropertyChanged+=ItemOnPropertyChanged;
    }
    }
    如果(例如,OldItems!=null)
    {
    foreach(e.OldItems中的T项)
    {
    如果(项!=null)
    item.PropertyChanged-=ItemOnPropertyChanged;
    }
    }
    }
    私有无效项OnPropertyChanged(对象发送方,PropertyChangedEventArgs e)
    =>ItemChanged?.Invoke(发送方,e);
    公共事件属性ChangedEventHandler项已更改;
    }
    
    您的问题不是很清楚-您当前的实现会发生什么?有错误吗?我们应该注意的断点分析?如何在Xaml中实现绑定

    在提供更多信息之前,以下是您提供的一般答案

    我将充分利用Prism MVVM对您的ReceiptViewModel进行以下更改

    public class ReceiptViewModel : BindableBase
    {
        private ObservableCollection<Service> _services;
        public ObservableCollection<Service> Services
        {
            get { return _services; }
            set { SetProperty(ref _services, value); }
        }
    
        private decimal _total;
        public decimal Total
        {
            get { return _total; }
            set { SetProperty(ref _total, Services.Sum(s => s.Price)); }
        }
    
        public DelegateCommand PriceChangedCommand {get; set;}
        
        public ReceiptViewModel()
        {
           PriceChangedCommand = new DelegateCommand(OnPriceChanged);
        }
    
        private void OnPriceChanged()
        {
           Total = Services.Sum(s=>s.Price);
        }
    }
    

    安装下面的Nuget软件包--
    Microsoft.Xaml.Behaviors.Wpf

    我试图遵循@Haukinger和@Bandook的建议,我做到了这一点

    模型

    public class Service
    {
        public string Name { get; set; }
        public decimal Price { get; set; }
    }
    
    扩展模型的视图模型

    public class ServiceViewModel : Service, INotifyPropertyChanged
    {
        private decimal price;
        public event PropertyChangedEventHandler PriceChanged;
        public event PropertyChangedEventHandler PropertyChanged;
    
        public ServiceViewModel()
        {
        }
    
        public ServiceViewModel(decimal value)
        {
            this.price = value;
        }
    
        public new decimal Price
        {
            get { return price; }
            set
            {
                price = value;
                // Call OnPropertyChanged whenever the property is updated
                OnPropertyChanged();
            }
        }
    
        // Create the OnPropertyChanged method to raise the event
        // The calling member's name will be used as the parameter.
        protected void OnPropertyChanged([CallerMemberName] string price = null)
        {
            PriceChanged?.Invoke(this, new PropertyChangedEventArgs(price));
        }
    }
    
    主视图模型

    public class MainWindowViewModel : BindableBase
    {
        private string _title = "Prism Application";
        public string Title
        {
            get { return _title; }
            set { SetProperty(ref _title, value); }
        }
    
        private string _firstName = "TABET AOUL";
        public string FirstName
        {
            get { return _firstName; }
            set { SetProperty(ref _firstName, value, ()=> RaisePropertyChanged(nameof(FullName))); }
        }
    
        private string _lastName = "Abdelkrim";
        public string LastName
        {
            get { return _lastName; }
            set { SetProperty(ref _lastName, value, () => RaisePropertyChanged(nameof(FullName))); }
        }
    
        public string FullName => $"{FirstName} {LastName}";
    
        private ObservableCollection<ServiceViewModel> _services = new ObservableCollection<ServiceViewModel>();
        public ObservableCollection<ServiceViewModel> Services
        {
            get { return _services; }
            set { SetProperty(ref _services, value, () => RaisePropertyChanged(nameof(Total))); }
        }
    
        public decimal Total => Services.Sum(s => s.Price);
    
        public DelegateCommand AddServiceCommand { get; set; }
    
        public void AddService()
        {
            var item = new ServiceViewModel { Name = "Service", Price = 100 };
            item.PriceChanged += Item_PriceChanged;
            Services.Add(item);
            RaisePropertyChanged(nameof(Total));
        }
    
        public MainWindowViewModel()
        {
            var item = new ServiceViewModel { Name = "Service", Price = 100 };
            item.PriceChanged += Item_PriceChanged;
            Services.Add(item);
            AddServiceCommand = new DelegateCommand(new Action(AddService));
        }
    
        private void Item_PriceChanged(object sender, PropertyChangedEventArgs e)
        {
            RaisePropertyChanged(nameof(Total));
        }
    }
    
    公共类MainWindowViewModel:BindableBase { 私有字符串_title=“Prism应用程序”; 公共字符串标题 { 获取{return\u title;} 集合{SetProperty(ref _title,value);} } 私有字符串_firstName=“TABET AOUL”; 公共字符串名 { 获取{return\u firstName;} set{SetProperty(ref _firstName,value,()=>RaisePropertyChanged(nameof(FullName));} } 私有字符串_lastName=“Abdelkrim”; 公共字符串姓氏 { 获取{return\u lastName;} set{SetProperty(ref _lastName,value,()=>RaisePropertyChanged(nameof(FullName));} } 公共字符串FullName=>$“{FirstName}{LastName}”; 私有ObservableCollection_服务=新ObservableCollection(); 公共收集服务 { 获取{return\u services;} set{SetProperty(ref _services,value,()=>RaisePropertyChanged(nameof(Total));} } 公共十进制总数=>Services.Sum(s=>s.Price); 公共DelegateCommand AddServiceCommand{get;set;} 公共服务() { var item=newserviceviewmodel{Name=“Service”,Price=100}; item.PriceChanged+=item_PriceChanged; 服务。添加(项目); RaisePropertyChanged(名称(总数)); } 公共主窗口视图模型() { var item=newserviceviewmodel{Name=“Service”,Price=100}; item.PriceChanged+=item_PriceChanged; 服务。添加(项目); AddServiceCommand=新的DelegateCommand(新操作(AddService)); } 私有无效项\u价格已更改(对象发送方,PropertyChangedEventArgs e) { RaisePropertyChanged(名称(总数)); } }
    代码似乎工作得很好,当我更改一个集合项目的价格时,除了添加新项目外,UI中的总更改量,因此我在clickcommand上调用了RaisePropertyChanged手动更改


    欢迎有更优雅解决方案的人

    希望这能有所帮助:)
    我更改了一个价格
    此更改是来自数据库还是来自用户?数据库是否可以自行更改(即用户不做任何事情)?@Haukinger我编辑了我的问题,价格是s
    public class Service
    {
        public string Name { get; set; }
        public decimal Price { get; set; }
    }
    
    public class ServiceViewModel : Service, INotifyPropertyChanged
    {
        private decimal price;
        public event PropertyChangedEventHandler PriceChanged;
        public event PropertyChangedEventHandler PropertyChanged;
    
        public ServiceViewModel()
        {
        }
    
        public ServiceViewModel(decimal value)
        {
            this.price = value;
        }
    
        public new decimal Price
        {
            get { return price; }
            set
            {
                price = value;
                // Call OnPropertyChanged whenever the property is updated
                OnPropertyChanged();
            }
        }
    
        // Create the OnPropertyChanged method to raise the event
        // The calling member's name will be used as the parameter.
        protected void OnPropertyChanged([CallerMemberName] string price = null)
        {
            PriceChanged?.Invoke(this, new PropertyChangedEventArgs(price));
        }
    }
    
    public class MainWindowViewModel : BindableBase
    {
        private string _title = "Prism Application";
        public string Title
        {
            get { return _title; }
            set { SetProperty(ref _title, value); }
        }
    
        private string _firstName = "TABET AOUL";
        public string FirstName
        {
            get { return _firstName; }
            set { SetProperty(ref _firstName, value, ()=> RaisePropertyChanged(nameof(FullName))); }
        }
    
        private string _lastName = "Abdelkrim";
        public string LastName
        {
            get { return _lastName; }
            set { SetProperty(ref _lastName, value, () => RaisePropertyChanged(nameof(FullName))); }
        }
    
        public string FullName => $"{FirstName} {LastName}";
    
        private ObservableCollection<ServiceViewModel> _services = new ObservableCollection<ServiceViewModel>();
        public ObservableCollection<ServiceViewModel> Services
        {
            get { return _services; }
            set { SetProperty(ref _services, value, () => RaisePropertyChanged(nameof(Total))); }
        }
    
        public decimal Total => Services.Sum(s => s.Price);
    
        public DelegateCommand AddServiceCommand { get; set; }
    
        public void AddService()
        {
            var item = new ServiceViewModel { Name = "Service", Price = 100 };
            item.PriceChanged += Item_PriceChanged;
            Services.Add(item);
            RaisePropertyChanged(nameof(Total));
        }
    
        public MainWindowViewModel()
        {
            var item = new ServiceViewModel { Name = "Service", Price = 100 };
            item.PriceChanged += Item_PriceChanged;
            Services.Add(item);
            AddServiceCommand = new DelegateCommand(new Action(AddService));
        }
    
        private void Item_PriceChanged(object sender, PropertyChangedEventArgs e)
        {
            RaisePropertyChanged(nameof(Total));
        }
    }