Страницы

Поиск по вопросам

пятница, 20 декабря 2019 г.

Отлов события изменения ObservableCollection

#c_sharp #wpf #mvvm


Есть приложение в котором пользователь может указывать название продукта и его цену,
по нажатию на кнопку эти данные вносятся в ObservableCollection и сразу же отображаются
в DataGrid. Также в окне присутствует textbox в котором должна отображаться средняя
цена всех внесенных продуктов. Проблема в следующем: Нужно чтобы пользователь в DataGrid
мог изменять цену уже внесённых продуктов после чего сразу же должна изменяться средняя
цена (AvaragePrice) всех продуктов, а этого не происходит, данные в коллекции изменяются,
но изменения вышеуказанного свойства не происходит:
MainWindow.xaml:


    
        
        
    

    

        
            
            
        

    
    
        
        
            
            
            
        

        
            
                
                
            
            
                
                
            

            

        


Ответы

Ответ 1



Смотрите в общем как можно поступить (за основу взял ответ с En SO)... Подготовка XAML разметка Для примера нам (мне) понадобится набросать небольшой View: INotifyPropertyChanged Также нам понадобится INotifyPropertyChanged, для удобства создадим отдельный класс: public class VM : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } Реализация Итак, скажем, у нас есть VM для наших предметов и некая модель самого предмета: C моделью предмета думаю все понятно, просто объявляем необходимые свойства (в моем случае это имя и цена). public class ItemModel : VM { public string Name { get; set; } private int price; public int Price { get => price; set { price = value; OnPropertyChanged(); } } } В VM у нас пока будет все тоже, но реализуем коллекцию предметов и среднюю цену, а также, давайте сделаем метод, который будет обновлять цену: public class ItemsViewModel : VM { public ObservableCollection Items { get; set; } = new ObservableCollection(); private int avaragePrice; public int AvaragePrice { get => avaragePrice; set { avaragePrice = value; OnPropertyChanged(); } } public void UpdatePrice() { AvaragePrice = Items.Sum(x => x.Price) / Items.Count; } } И так, теперь у вас есть выбор 1. Подписывать все ItemModel на событие изменение. 2. Использовать BindingList. 1. Используем ObservableCollection Для начала подпишемся на событие изменения коллекции в нашей VM: public ItemsViewModel() { Items.CollectionChanged += ItemsOnCollectionChanged; } private void ItemsOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { if (e.OldItems != null) { foreach (INotifyPropertyChanged item in e.OldItems) item.PropertyChanged -= UpdatePrice; } if (e.NewItems != null) { foreach (INotifyPropertyChanged item in e.NewItems) item.PropertyChanged += UpdatePrice; } } Изменим немного метод обновления: public void UpdatePrice(object sender, PropertyChangedEventArgs e) { AvaragePrice = Items.Sum(x => x.Price) / Items.Count; } Вроде все... И так, что здесь происходит? Суть в следующем: ObservableCollection оповещает только если в коллекцию добавляется, либо что то удаляется. В этом случае мы при добавление предмета в коллекцию проходимся по всем его значениям и подписываемся на событие изменения, если наш предмет в коллекции реализует INotifyPropertyChanged. При удаление делаем обратное, то есть отписываемся. Таким образом, все Model внутри коллекции будут подписаны на событие обновление цены. 2. Используем BindingList Есть довольно классная штука в WPF, как BindingList. У нее есть событие ListChanged, которое в свою очередь оповещает о любом (вроде) изменении в коллекции. Перепишем ObservalCollection на BindingList. В конструкторе подпишемся на событие изменения, ну и обновление цены можно положить внутрь, я думаю...: public ItemsViewModel() { Items.ListChanged += ItemsOnListChanged; } private void ItemsOnListChanged(object sender, ListChangedEventArgs e) { if (e.ListChangedType == ListChangedType.ItemChanged) { AvaragePrice = Items.Sum(x => x.Price) / Items.Count; } } public BindingList Items { get; set; } = new BindingList(); Ну тут, я думаю, все понятно и объяснять не нужно. Если у нас значение изменено, то обновляем цену (тут можете тип выбрать нужный). В общем, результат у нас будет примерно следующий:

Комментариев нет:

Отправить комментарий