我在下面回答了您在同一程序上的另一个问题,您在我发布之前删除了该问题。它涵盖了更新价格,但还涵盖了更多内容(使用已删除问题中的信息)。
首先,如果您希望在列表中的项目更新时更新屏幕,您必须使该类实现INotifyPropertyChanged
public class OrderDetailsListItem : INotifyPropertyChanged
{
private string _name;
private decimal _price;
private int _quantity;
public string Name
{
get { return _name; }
set
{
if (value == _name) return;
_name = value;
OnPropertyChanged();
}
}
public decimal Price
{
get { return _price; }
set
{
if (value == _price) return;
_price = value;
OnPropertyChanged();
}
}
public int Quantity
{
get { return _quantity; }
set
{
if (value == _quantity) return;
_quantity = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
现在,当价格或数量发生变化时,它会让绑定知道该项目已更改。
接下来,您的if (OrderItem.Contains( 导致出现重复项的原因是您必须实现Equals((最好是GetHashCode())才能使Contains( 之类的东西起作用。
public class OrderDetailsListItem : INotifyPropertyChanged, IEquatable<OrderDetailsListItem>
{
//(Snip everything from the first example)
public bool Equals(OrderDetailsListItem other)
{
if (ReferenceEquals(null, other)) return false;
return string.Equals(_name, other._name);
}
public override bool Equals(object obj)
{
return Equals(obj as OrderDetailsListItem);
}
public override int GetHashCode()
{
return (_name != null ? _name.GetHashCode() : 0);
}
}
另外一点,不要在单击按钮时执行OrderItem.CollectionChanged +=,您将在每个集合更改事件时创建额外的事件调用。只需在构造函数中设置一个,这是您唯一需要的订阅。但是,还有一个更好的集合可以使用,BindingList<T> 及其ListChanged 事件。 BindingList 将在 ObserveableCollection 引发 CollectionChanged 的所有情况下引发 ListChange 事件,但此外,它还会在集合中的任何项目引发 INotifyPropertyChanged 事件时引发事件。
public MainWindow()
{
_orderItem = new BindingList<OrderDetailsListItem>();
_orderItem.ListChanged += OrderItemListChanged;
InitializeComponent();
GetBeerInfo();
//You will see why all the the rest of the items were removed in the next part.
}
private void OrderItemListChanged(object sender, ListChangedEventArgs e)
{
TotalPrice = OrderItem.Select(x => x.Price).Sum();
}
最后,我敢打赌你来自 Winforms 背景。 WPF 更多地基于绑定而不是 winforms,在我真正理解这一点之前,我曾经编写的代码很像你正在做的事情。所有这些对标签和集合的分配都应该在 XAML 中通过绑定完成,这允许对于像 INotifyPropertyChanged 事件这样的事情来自动更新屏幕而不需要函数调用。
这是您的程序的一个简单再现,它运行和使用绑定以及我谈到的所有其他内容。
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:myNamespace ="clr-namespace:WpfApplication2"
Title="MainWindow" Height="350" Width="525" >
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0">
<Button Content="{x:Static myNamespace:GlobalVariables._amstelProductName}" Click="amstelBeerButton_Click"/>
<TextBlock Text="{Binding TotalPrice, StringFormat=Total: {0:c}}"/>
</StackPanel>
<ListView Grid.Column="1" ItemsSource="{Binding OrderItem}">
<ListView.View>
<GridView>
<GridViewColumn DisplayMemberBinding="{Binding Path=Name}" Header="Name"/>
<GridViewColumn DisplayMemberBinding="{Binding Path=Price, StringFormat=c}" Header="Price"/>
<GridViewColumn DisplayMemberBinding="{Binding Path=Quantity, StringFormat=N0}" Header="Quantity"/>
</GridView>
</ListView.View>
</ListView>
</Grid>
</Window>
using System;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Windows;
namespace WpfApplication2
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public static readonly DependencyProperty TotalPriceProperty = DependencyProperty.Register(
"TotalPrice", typeof (decimal), typeof (MainWindow), new PropertyMetadata(default(decimal)));
private readonly BindingList<OrderDetailsListItem> _orderItem;
public MainWindow()
{
_orderItem = new BindingList<OrderDetailsListItem>();
_orderItem.ListChanged += OrderItemListChanged;
InitializeComponent();
DataContext = this;
GetBeerInfo();
}
public BindingList<OrderDetailsListItem> OrderItem
{
get { return _orderItem; }
}
public decimal TotalPrice
{
get { return (decimal) GetValue(TotalPriceProperty); }
set { SetValue(TotalPriceProperty, value); }
}
private void GetBeerInfo()
{
OrderItem.Add(new OrderDetailsListItem
{
Name = "Some other beer",
Price = 2m,
Quantity = 1
});
}
private void OrderItemListChanged(object sender, ListChangedEventArgs e)
{
TotalPrice = _orderItem.Select(x => x.Price).Sum();
}
private void amstelBeerButton_Click(object sender, RoutedEventArgs e)
{
//This variable makes me suspicous, this probibly should be a property in the class.
var quantityItem = GlobalVariables.quantityChosen;
if (quantityItem == 0)
{
quantityItem = 1;
}
var item = OrderItem.FirstOrDefault(i => i.Name == GlobalVariables._amstelProductName);
if (item == null)
{
OrderItem.Add(new OrderDetailsListItem
{
Name = GlobalVariables._amstelProductName,
Quantity = quantityItem,
Price = GlobalVariables._amstelPrice
});
}
else if (item != null)
{
item.Quantity = item.Quantity + quantityItem;
item.Price = item.Price*item.Quantity;
}
//The UpdatePrice function is nolonger needed now that it is a bound property.
}
}
public class GlobalVariables
{
public static int quantityChosen = 0;
public static string _amstelProductName = "Amstel Beer";
public static decimal _amstelPrice = 5;
}
public class OrderDetailsListItem : INotifyPropertyChanged, IEquatable<OrderDetailsListItem>
{
private string _name;
private decimal _price;
private int _quantity;
public string Name
{
get { return _name; }
set
{
if (value == _name) return;
_name = value;
OnPropertyChanged();
}
}
public decimal Price
{
get { return _price; }
set
{
if (value == _price) return;
_price = value;
OnPropertyChanged();
}
}
public int Quantity
{
get { return _quantity; }
set
{
if (value == _quantity) return;
_quantity = value;
OnPropertyChanged();
}
}
public bool Equals(OrderDetailsListItem other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return string.Equals(_name, other._name);
}
public event PropertyChangedEventHandler PropertyChanged;
public override bool Equals(object obj)
{
return Equals(obj as OrderDetailsListItem);
}
public override int GetHashCode()
{
return (_name != null ? _name.GetHashCode() : 0);
}
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}