显示C#WPF应用程序中datagrid列总和的总销售额

时间:2016-11-28 18:35:10

标签: c# wpf data-binding datagrid sum

enter image description here

我无法理解如何将UI上的总销售文本框绑定到数据网格中总价格的总和。我已经阅读了几篇文章,试图实现一个总和程序并绑定它,尝试使用集合更改通知,我无法让总销售工作。我有一个具有Quantity和UnitPrice属性的项目类,其计算的TotalPrice会在数量或单价变化时自动更新。项目列表包含在SalesOrder类中。 ui是我用来创建订单的主窗口。 我的代码位于:https://github.com/battondl/SalesOrder/tree/master/simpleShoppingListProgram 有关如何让UI文本框中的总价格自动更新的任何指导将非常感激。

数据网格中的每个订单项都具有基于数量*单价的总价格。

这是我想要更新的总销售价格,它是所有订单项总价的总和。

class Item : INotifyPropertyChanged
{
    private string partNumber;
    private int quantity;
    private decimal unitPrice;
    private DateTime orderDate;


    public string PartNumber
    {
        get { return partNumber; }
        set
        {
            partNumber = value;
            OnPropertyChanged();
        }
    }

    public int Quantity
    {
        get { return quantity; }
        set
        {
            quantity = value;
            OnPropertyChanged("Quantity");
            OnPropertyChanged("TotalPrice");
        }
    }
    public decimal UnitPrice
    {
        get { return unitPrice; }
        set
        {
            unitPrice = value;
            OnPropertyChanged("UnitPrice");
            OnPropertyChanged("TotalPrice");
        }
    }

    public DateTime OrderDate
    {
        get { return orderDate; }
        set
        {
            orderDate = value;
            OnPropertyChanged();
        }
    }

    public decimal TotalPrice => Quantity * UnitPrice;

    public Item()
    {
        PartNumber = "";
        Quantity = 0;
        UnitPrice = 0.00m;
        OrderDate = DateTime.Now;
    }


    public event PropertyChangedEventHandler PropertyChanged;
    private void OnPropertyChanged([CallerMemberName] string caller = "")
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(caller));
        }
    }


}



class SalesOrder 
{

    private string orderNumber;
    public string OrderNumber
    {
        get { return orderNumber; }
        set
        {
            orderNumber = value;
        }
    }

    private ObservableCollection<Item> items;

    public ObservableCollection<Item> Items
    {
        get { return items; }
    }

    public SalesOrder()
    {
        orderNumber = "";
        items = new ObservableCollection<Item>();

    }

    public decimal CalculateTotalPrice()
    {
        decimal total = 0.00m;
        foreach (Item item in items)
        {
            total += item.TotalPrice;
        }
        return total;
    }
}

public partial class MainWindow : Window
{
    private SalesOrder salesOrder;

    public MainWindow()
    {
        InitializeComponent();
    }

    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        salesOrder = new SalesOrder();
        dgSaleItems.ItemsSource = salesOrder.Items;

        tbx_totalSale.Text = salesOrder.CalculateTotalPrice().ToString();
    }
}


<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="*"/>
        <RowDefinition Height="6*"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <StackPanel Orientation="Vertical"
        Grid.Row="0">
        <StackPanel Orientation="Horizontal">
            <Label Content="OrderNumber: " />
            <TextBox Name="tbx_orderNumber" 
                     Width="100"/>
        </StackPanel>
    </StackPanel>
    <StackPanel Orientation="Vertical"
                Grid.Row="1">
        <DataGrid Name="dgSaleItems"
        Grid.Row="1"
                  AutoGenerateColumns="False">
            <DataGrid.Columns>
                <DataGridTextColumn x:Name="partNumber"
                                    Header="Part Number"
                                    Binding="{Binding Path=PartNumber}" />
                <DataGridTextColumn x:Name="quantity"
                                    Header="Quantity"
                                    Binding="{Binding Path=Quantity}" />
                <DataGridTextColumn x:Name="unitPrice"
                                    Header="Unit Price"
                                    Binding="{Binding Path=UnitPrice}" />
                <DataGridTextColumn x:Name="totalPrice"
                                    Header="Total Price"
                                    Binding="{Binding Path=TotalPrice}" />
                <DataGridTemplateColumn x:Name="orderDate"
                                        Header="Order Date">
                    <DataGridTemplateColumn.CellTemplate>
                            <DataTemplate>
                                <TextBlock Text="{Binding Path=OrderDate}"          />
                            </DataTemplate>
                        </DataGridTemplateColumn.CellTemplate>
                        <DataGridTemplateColumn.CellEditingTemplate>
                            <DataTemplate>
                            <DatePicker SelectedDate="{Binding Path=OrderDate}" />
                            </DataTemplate>
                        </DataGridTemplateColumn.CellEditingTemplate>
                    </DataGridTemplateColumn>
            </DataGrid.Columns>
        </DataGrid>
    </StackPanel>
    <StackPanel Orientation="Vertical"
                Grid.Row="7">
        <StackPanel Orientation="Horizontal">
            <Label Content="Total Sale: " />
            <TextBox x:Name="tbx_totalSale"
                     Width="100"/>
        </StackPanel>
    </StackPanel>
</Grid>

3 个答案:

答案 0 :(得分:0)

文本框中没有任何约束:

<TextBox x:Name="tbx_totalSale"
                 Width="100"/>

需要:

<TextBox x:Name="tbx_totalSale"
         Text="{Binding TotalSale}"
                 Width="100"/>

并且您需要向TotalSale类添加SaleOrder属性,以返回行的总和:

public decimal TotalSale
{
    get { return items.Sum(i => i.TotalPrice);
}

当在集合中添加或删除项目时,或者当行OnPropertyChanged("TotalSale")更改时,您需要发布TotalPrice事件。当数量或单价发生变化时,您已经在更新TotalPrice,因此您需要做的就是通过items集合向上传播更改。

答案 1 :(得分:0)

private void Window_Loaded(object sender, RoutedEventArgs e)
{
    salesOrder = new SalesOrder();
    dgSaleItems.ItemsSource = salesOrder.Items;

    tbx_totalSale.Text = salesOrder.CalculateTotalPrice().ToString();
}

您只是在窗口加载事件期间更新总销售额,但是您忘记了每次更改商品的数量或价格时都必须更新它。

以下是你如何做到这一点:

private void Window_Loaded(object sender, RoutedEventArgs e)
{
    salesOrder = new SalesOrder();
    dgSaleItems.ItemsSource = salesOrder.Items;

    salesOrder.Items.CollectionChanged += (s, args) =>
    {
        if (args.Action == NotifyCollectionChangedAction.Remove)
        {
            foreach (Item item in args.OldItems)
            {
                item.PropertyChanged -= UpdateTotalSale;
            }
        }
        else if (args.Action == NotifyCollectionChangedAction.Add)
        {
            foreach (Item item in args.NewItems)
            {
                item.PropertyChanged += UpdateTotalSale;
            }
        }
    };
}

private void UpdateTotalSale(object sender, PropertyChangedEventArgs e)
{
    tbx_totalSale.Text = salesOrder.CalculateTotalPrice().ToString();
}

理想情况下,您应该在INPC上实施SalesOrder,并将上述代码移到SalesOrder中,因为后面的代码应尽可能保持干净。

答案 2 :(得分:0)

enter image description here

谢谢,我现在就开始工作了!

我已将修改/添加到SalesOrder类

  public decimal TotalSale
  {
      get { return items.Sum(i => i.TotalPrice); }

  }

  public SalesOrder()
  {
      orderNumber = "";
      items = new ObservableCollection<Item>();

      Items.CollectionChanged += (s, args) =>
      {
          if (args.Action == NotifyCollectionChangedAction.Remove)
          {
              foreach (Item item in args.OldItems)
              {
                  item.PropertyChanged -= UpdateTotalSale;
              }
          }
          else if (args.Action == NotifyCollectionChangedAction.Add)
          {
              foreach (Item item in args.NewItems)
              {
                  item.PropertyChanged += UpdateTotalSale;
              }
          }
      };

  }

  private void UpdateTotalSale(object sender, PropertyChangedEventArgs e)
    {

        foreach (Window window in Application.Current.Windows)
        {
            if (window.GetType() == typeof(MainWindow))
            {
                (window as MainWindow).tbx_totalSale.Text =           CalculateTotalPrice().ToString();
            }
        }

    }

我还在xaml中添加了以下内容。

<StackPanel Orientation="Vertical"
                Grid.Row="7">
     <StackPanel Orientation="Horizontal">
            <Label Content="Total Sale: " />
            <TextBox x:Name="tbx_totalSale"
                     Text="{Binding TotalSale}"
                     Width="100"
                     x:FieldModifier="public"/>
</StackPanel>

最后,这对于背后的代码:

 private void Window_Loaded(object sender, RoutedEventArgs e)
 {
     salesOrder = new SalesOrder();
     dgSaleItems.ItemsSource = salesOrder.Items;

 }