これを達成する方法は、行の合計がユーザーによって編集されるたびに、および製品が に追加または削除されるたびに、合計金額を再計算することObservableCollection
です。
は、新しい行の合計が設定されたときにイベントをProduct
実装INotifyPropertyChanged
して発生させるため、 はそのイベントを処理して合計金額を再計算できます。PropertyChanged
ViewModel
ObservableCollection
CollectionChanged
にはアイテムが追加または削除されたときに発生するイベントがあるため、ViewModel
はそのイベントを処理して再計算することもできます。(製品が変更のみ可能で、ユーザーが追加/削除できないなどの場合、この部分は実際には必要ありません)。
この小さなプログラムを試して、どのように実行できるかを確認できます。
分離コード
public partial class MainWindow : Window
{
ViewModel vm = new ViewModel();
public MainWindow()
{
InitializeComponent();
vm.Products = new ObservableCollection<Product>
{
new Product { Name = "Product1", LineTotal = 10 },
new Product { Name = "Product2", LineTotal = 20 },
new Product { Name = "Product3", LineTotal = 15 }
};
this.DataContext = vm;
}
private void AddItem(object sender, RoutedEventArgs e)
{
vm.Products.Add(new Product { Name = "Added product", LineTotal = 50 });
}
private void RemoveItem(object sender, RoutedEventArgs e)
{
vm.Products.RemoveAt(0);
}
}
public class ViewModel : INotifyPropertyChanged
{
private ObservableCollection<Product> _products;
public ObservableCollection<Product> Products
{
get { return _products; }
set
{
_products = value;
// We need to know when the ObservableCollection has changed.
// On added products: hook up eventhandlers to their PropertyChanged events.
// On removed products: recalculate the total.
_products.CollectionChanged += (sender, e) =>
{
if (e.NewItems != null)
AttachProductChangedEventHandler(e.NewItems.Cast<Product>());
else if (e.OldItems != null)
CalculateTotalAmount();
};
AttachProductChangedEventHandler(_products);
}
}
private void AttachProductChangedEventHandler(IEnumerable<Product> products)
{
// Attach eventhandler for each products PropertyChanged event.
// When the LineTotal property has changed, recalculate the total.
foreach (var p in products)
{
p.PropertyChanged += (sender, e) =>
{
if (e.PropertyName == "LineTotal")
CalculateTotalAmount();
};
}
CalculateTotalAmount();
}
public void CalculateTotalAmount()
{
// Set TotalAmount property to the sum of all line totals.
TotalAmount = Products.Sum(p => p.LineTotal);
}
private decimal _TotalAmount;
public decimal TotalAmount
{
get { return _TotalAmount; }
set
{
if (value != _TotalAmount)
{
_TotalAmount = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("TotalAmount"));
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
public class Product : INotifyPropertyChanged
{
public string Name { get; set; }
private decimal _LineTotal;
public decimal LineTotal
{
get { return _LineTotal; }
set
{
if (value != _LineTotal)
{
_LineTotal = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("LineTotal"));
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
XAML:
<Window x:Class="WpfApplication3.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<DataGrid ItemsSource="{Binding Products}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Name}" />
<DataGridTextColumn Binding="{Binding LineTotal}" />
</DataGrid.Columns>
</DataGrid>
<Button Click="AddItem">Add item</Button>
<Button Click="RemoveItem">Remove item</Button>
<TextBlock>
<Run>Total amount:</Run>
<Run Text="{Binding TotalAmount}" />
</TextBlock>
</StackPanel>
</Window>