【问题标题】:Display total sale from sum of datagrid column in C# WPF application在 C# WPF 应用程序中显示数据网格列总和的总销售额
【发布时间】:2017-04-12 13:56:51
【问题描述】:

我无法理解如何将 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>

【问题讨论】:

  • 请将相关代码放入问题中。如果有的话,还要解释“总销售额”和TotalPrice 之间的区别。如果它们相同,那么您要解决的问题是什么?如果它们不同,为什么不以相同的方式进行呢?
  • 我在 txtbox 上根本看不到任何绑定...发布代码会有所帮助...
  • 总价是按数量*单价计算的每个订单项。我还取出了不起作用的绑定,因为它不起作用。总销售额是所有订单项总价的总和。

标签: c# wpf data-binding datagrid sum


【解决方案1】:
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();
}

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

【讨论】:

  • 谢谢,您为我提供了完成所有工作所需的帮助。我还能够按照您的建议弄清楚如何在 SalesOrder 类中实现它!
【解决方案2】:

您的文本框中没有绑定:

<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);
}

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

【讨论】:

  • 这有效,但只有一次。您还需要订阅项目的属性已更改,并在 TotalSale 应更改时通知。
  • @Xiaoy312 - 确实。
  • 谢谢,我希望我可以标记多个答案,这是两个答案的组合,帮助我把它放在一起。
【解决方案3】:

谢谢,我现在可以使用了!

我已在 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;

 }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-07-13
    • 2023-02-05
    • 1970-01-01
    • 2015-09-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多