Есть приложение в котором пользователь может указывать название продукта и его цену, по нажатию на кнопку эти данные вносятся в ObservableCollection и сразу же отображаются в DataGrid. Также в окне присутствует textbox в котором должна отображаться средняя цена всех внесенных продуктов. Проблема в следующем: Нужно чтобы пользователь в DataGrid мог изменять цену уже внесённых продуктов после чего сразу же должна изменяться средняя цена (AvaragePrice) всех продуктов, а этого не происходит, данные в коллекции изменяются, но изменения вышеуказанного свойства не происходит:
MainWindow.xaml:
MainViewModel:
class MainViewModel : BaseModel
{
MainModel mainModel;
public MainViewModel()
{
// Инициализируем модель, подписываемся на изменение любого из её свойств
mainModel = new MainModel();
mainModel.PropertyChanged += (s, e) => { OnPropertyChanged(e.PropertyName); };
}
public string Name
{
get
{
return mainModel.Name;
}
set
{
mainModel.Name = value;
OnPropertyChanged();
}
}
public double Price
{
get
{
return mainModel.Price;
}
set
{
mainModel.Price = value;
OnPropertyChanged();
}
}
public RelayCommand AddProduct
{
get
{
return mainModel.AddProduct;
}
}
public ObservableCollection
public double AvaragePrice
{
get
{
return mainModel.AvaragePrice;
}
set
{
mainModel.AvaragePrice = value;
OnPropertyChanged();
}
}
}
MainModel:
class MainModel : BaseModel
{
public MainModel()
{
allProducts = new ObservableCollection
string name;
public string Name
{
get
{
return name;
}
set
{
name = value;
OnPropertyChanged();
}
}
double price;
public double Price
{
get
{
return price;
}
set
{
price = value;
OnPropertyChanged();
}
}
ObservableCollection
RelayCommand addProduct;
public RelayCommand AddProduct
{
get
{
return addProduct ?? (addProduct = new RelayCommand(obj =>
{
Product newProduct = new Product()
{
Price = this.Price,
Name = this.Name
};
AllProducts.Add(newProduct);
}));
}
}
double avaragePrice;
public double AvaragePrice
{
get
{
return avaragePrice;
}
set
{
avaragePrice = value;
OnPropertyChanged();
}
}
}
Ответ
Смотрите в общем как можно поступить (за основу взял ответ с 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
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
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
Ну тут, я думаю, все понятно и объяснять не нужно. Если у нас значение изменено, то обновляем цену (тут можете тип выбрать нужный).
В общем, результат у нас будет примерно следующий:
Комментариев нет:
Отправить комментарий