【问题标题】:Calculating total of a ListView column WPF C#计算 ListView 列 WPF C# 的总数
【发布时间】:2015-03-08 05:13:14
【问题描述】:

我正在为一家酒吧创建一个 EPOS 系统,我自己的项目只是为了测试我的技能。

我遇到了一个问题,我已经设法将所有产品放在 WrapPanel 中,并且在单击时我还设法让它们显示在 ListView 控件中。

但是,我似乎无法在 ListView 下方的标签中显示总计,本质上,每次将产品添加到 ListView 时,我都希望通过将“价格”列,并将它们显示在下面的标签中。但我什至无法通过按钮打印总数,更不用说自动打印了。

到目前为止,这是我的按钮代码。

不要推荐子项目,因为它在 WPF 中不起作用。

private void Button_Click_1(object sender, RoutedEventArgs e) {

     decimal total = 0;


     foreach (ListViewItem o in orderDetailsListView.Items)
     {
         total = total + (decimal)(orderDetailsListView.SelectedItems[1]);
     }  
     totalOutputLabel.Content = total;
}

【问题讨论】:

  • 请发布您的错误输出。

标签: c# wpf listview


【解决方案1】:

我在下面回答了您在同一程序上的另一个问题,您在我发布之前删除了该问题。它涵盖了更新价格,但还涵盖了更多内容(使用已删除问题中的信息)。


首先,如果您希望在列表中的项目更新时更新屏幕,您必须使该类实现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&lt;T&gt; 及其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));
        }
    }
}

【讨论】:

    【解决方案2】:

    刚刚对此进行了测试,您应该确保通过添加断点进入事件处理程序。如果不是,请确保您已将处理程序注册到 click 事件,例如:

    <Button Name="TestButton" Click="Button_Click_1"/>
    

    如果您正在使用 WPF,我强烈建议您在某个时候查看 MVVM 和数据绑定。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-12-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多