ich ein Datagrid, wo ich brauche die Summe der Preisspalte eines verschachtelten Datenraster zu berechnen, etwa so:WPF Probleme Collection Ereignisse Einhaken
Ich versuche this example zu folgen, so dass mein Items beobachtbare Sammlung für jedes Person-Objekt wird über Änderungen benachrichtigt. Der Unterschied ist, dass ich es in einer Klasse implementiere, nicht in View Model.
public class Person : NotifyObject
{
private ObservableCollection<Item> _items;
public ObservableCollection<Item> Items
{
get { return _items; }
set { _items = value; OnPropertyChanged("Items"); }
}
private string _name;
public string Name
{
get { return _name; }
set { _name = value; OnPropertyChanged("Name"); }
}
public double Total
{
get { return Items.Sum(i => i.Price); }
set { OnPropertyChanged("Total"); }
}
public Person()
{
Console.WriteLine("0001 Constructor");
this.Items = new ObservableCollection<Item>();
this.Items.CollectionChanged += Items_CollectionChanged;
this.Items.Add(new Item());
}
private void Items_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
Console.WriteLine("0002 CollectionChanged");
if (e.NewItems != null)
foreach (Item item in e.NewItems)
item.PropertyChanged += Items_PropertyChanged;
if (e.OldItems != null)
foreach (Item item in e.OldItems)
item.PropertyChanged -= Items_PropertyChanged;
}
private void Items_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
Console.WriteLine("0003 PropertyChanged");
this.Total = Items.Sum(i => i.Price);
}
}
Der Code innerhalb der Konstruktor Ereignisse nicht einhaken, wenn ein neues Objekt initialisiert wird oder ein vorhandenes bearbeitet wurde. Daher wird das Items_PropertyChanged-Ereignis nie ausgelöst. Ich kann nur die gesamte Liste manuell aktualisieren. Was mache ich hier falsch?
Oder gibt es vielleicht einen anderen Ansatz, um die Summe für die Einkaufsliste jeder Person zu berechnen?
Unten ist der gesamte Code, wenn jemand sich auch interessiert es anzuschauen.
XAML
<Window x:Class="collection_changed_2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:collection_changed_2"
mc:Ignorable="d"
Title="MainWindow" SizeToContent="Height" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition />
</Grid.RowDefinitions>
<DataGrid x:Name="DataGrid1"
Grid.Row="0"
ItemsSource="{Binding DataCollection}"
SelectedItem="{Binding DataCollectionSelectedItem}"
AutoGenerateColumns="False"
CanUserAddRows="false" >
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Name}" Width="2*"/>
<DataGridTemplateColumn Header="Item/Price" Width="3*">
<DataGridTemplateColumn.CellTemplate >
<DataTemplate>
<DataGrid x:Name="DataGridItem"
ItemsSource="{Binding Items}"
SelectedItem="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.ItemsSelectedItem}"
Background="Transparent"
HeadersVisibility="None"
AutoGenerateColumns="False"
CanUserAddRows="false" >
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding ItemName}" Width="*"/>
<DataGridTextColumn Binding="{Binding Price}" Width="50"/>
<DataGridTemplateColumn Header="Button" Width="Auto">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel>
<Button Content="+"
Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.AddItem }"
Width="20" Height="20">
</Button>
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="Total" Binding="{Binding Total, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Width="Auto"/>
<DataGridTemplateColumn Header="Buttons" Width="Auto">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel VerticalAlignment="Center">
<Button Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.AddPerson}" Width="20" Height="20">+</Button>
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
<StackPanel Grid.Row="1" Margin="10">
<Button Width="150" Height="30" Content="Refresh" Command="{Binding Refresh}" />
</StackPanel>
</Grid>
</Window>
C#
using System;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using System.Windows;
using System.Windows.Data;
using System.Windows.Input;
namespace collection_changed_2
{
public class Item : NotifyObject
{
private string _itemName;
public string ItemName
{
get { return _itemName; }
set { _itemName = value; OnPropertyChanged("ItemName"); }
}
private double _price;
public double Price
{
get { return _price; }
set { _price = value; OnPropertyChanged("Price"); }
}
}
public class Person : NotifyObject
{
private ObservableCollection<Item> _items;
public ObservableCollection<Item> Items
{
get { return _items; }
set { _items = value; OnPropertyChanged("Items"); }
}
private string _name;
public string Name
{
get { return _name; }
set { _name = value; OnPropertyChanged("Name"); }
}
public double Total
{
get { return Items.Sum(i => i.Price); }
set { OnPropertyChanged("Total"); }
}
public Person()
{
Console.WriteLine("0001 Constructor");
this.Items = new ObservableCollection<Item>();
this.Items.CollectionChanged += Items_CollectionChanged;
this.Items.Add(new Item());
}
private void Items_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
Console.WriteLine("0002 CollectionChanged");
if (e.NewItems != null)
foreach (Item item in e.NewItems)
item.PropertyChanged += Items_PropertyChanged;
if (e.OldItems != null)
foreach (Item item in e.OldItems)
item.PropertyChanged -= Items_PropertyChanged;
}
private void Items_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
Console.WriteLine("0003 PropertyChanged");
this.Total = Items.Sum(i => i.Price);
}
}
public abstract class NotifyObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string property)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
public class RelayCommand : ICommand
{
private Action<object> executeDelegate;
readonly Predicate<object> canExecuteDelegate;
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null)
throw new NullReferenceException("execute");
executeDelegate = execute;
canExecuteDelegate = canExecute;
}
public RelayCommand(Action<object> execute) : this(execute, null) { }
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public bool CanExecute(object parameter)
{
return canExecuteDelegate == null ? true : canExecuteDelegate(parameter);
}
public void Execute(object parameter)
{
executeDelegate.Invoke(parameter);
}
}
public class ViewModel : NotifyObject
{
public ObservableCollection<Person> DataCollection { get; set; }
public Person DataCollectionSelectedItem { get; set; }
public Item ItemsSelectedItem { get; set; }
public RelayCommand AddPerson { get; private set; }
public RelayCommand AddItem { get; private set; }
public RelayCommand Refresh { get; private set; }
public ViewModel()
{
DataCollection = new ObservableCollection<Person>
{
new Person() {
Name = "Friedrich Nietzsche",
Items = new ObservableCollection<Item> {
new Item { ItemName = "Phone", Price = 220 },
new Item { ItemName = "Tablet", Price = 350 },
}
},
new Person() {
Name = "Jean Baudrillard",
Items = new ObservableCollection<Item> {
new Item { ItemName = "Teddy Bear Deluxe", Price = 2200 },
new Item { ItemName = "Pokemon", Price = 100 }
}
}
};
AddItem = new RelayCommand(AddItemCode, null);
AddPerson = new RelayCommand(AddPersonCode, null);
Refresh = new RelayCommand(RefreshCode, null);
}
public void AddItemCode(object parameter)
{
var collectionIndex = DataCollection.IndexOf(DataCollectionSelectedItem);
var itemIndex = DataCollection[collectionIndex].Items.IndexOf(ItemsSelectedItem);
Item newItem = new Item() { ItemName = "Item_Name", Price = 100 };
DataCollection[collectionIndex].Items.Insert(itemIndex + 1, newItem);
}
public void AddPersonCode(object parameter)
{
var collectionIndex = DataCollection.IndexOf(DataCollectionSelectedItem);
Person newList = new Person()
{
Name = "New_Name",
Items = new ObservableCollection<Item>() { new Item() { ItemName = "Item_Name", Price = 100 } }
};
DataCollection.Insert(collectionIndex + 1, newList);
}
private void RefreshCode(object parameter)
{
CollectionViewSource.GetDefaultView(DataCollection).Refresh();
}
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new ViewModel();
}
}
}
Ich versuche, Ihrer Logik zu folgen. Was muss ich als Argument an ein Item-Objekt übergeben, wenn ich es in ViewModel initialisiere? var smtn = neues Element (?) {ItemName = "Telefon", Preis = 220}; – Disodium