WPF DataGrid餐厅账单:添加重复菜品时如何避免新增行仅更新Quantity列
Hey there! I get exactly what you're dealing with—adding the same "Seafood Alfredo" and ending up with two rows instead of just updating the quantity is super frustrating. Let's sort this out step by step.
The Core Idea
Instead of immediately adding a new row every time the user clicks "Add", we first check if the selected main course already exists in our data collection. If it does, we just increment its Quantity value; if not, we add a brand new entry.
Step 1: Define Your Menu Item Model
First, you need a model class for your menu items that implements INotifyPropertyChanged—this ensures that when the quantity updates, the DataGrid automatically reflects the change:
using System.ComponentModel; using System.Runtime.CompilerServices; public class MenuItem : INotifyPropertyChanged { private string _name; private decimal _price; private int _quantity; public string Name { get => _name; set { _name = value; OnPropertyChanged(); } } public decimal Price { get => _price; set { _price = value; OnPropertyChanged(); } } public int Quantity { get => _quantity; set { _quantity = value; OnPropertyChanged(); } } // Helper for property change notifications public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }
Step 2: Set Up Your Data Collection and Add Logic
In your window's code-behind (or ViewModel if you're using MVVM), you'll need an ObservableCollection<MenuItem> bound to your DataGrid. This collection automatically notifies the UI when items are added/removed or properties change.
Here's the code for the add button click handler:
using System.Collections.ObjectModel; using System.Linq; using System.Windows; public partial class RestaurantBillWindow : Window { // This collection is bound to your DataGrid public ObservableCollection<MenuItem> BillItems { get; set; } public RestaurantBillWindow() { InitializeComponent(); BillItems = new ObservableCollection<MenuItem>(); DataContext = this; // Set data context so bindings work } private void AddMenuItem_Click(object sender, RoutedEventArgs e) { // Get the selected main course from your ComboBox if (MainCourseComboBox.SelectedItem is ComboBoxItem selectedItem) { string itemName = selectedItem.Content.ToString(); decimal itemPrice = GetPriceForMenuItem(itemName); // Check if the item already exists in the bill var existingItem = BillItems.FirstOrDefault(item => item.Name == itemName); if (existingItem != null) { // Item exists—just increment quantity existingItem.Quantity += 1; } else { // Item doesn't exist—add a new row with quantity 1 BillItems.Add(new MenuItem { Name = itemName, Price = itemPrice, Quantity = 1 }); } } } // Helper method to get the price for a menu item (replace with your actual logic) private decimal GetPriceForMenuItem(string itemName) { // Example: hardcoded prices—you could load this from a database or config return itemName switch { "Seafood Alfredo" => 19.99m, "Steak Frites" => 24.99m, "Vegetarian Lasagna" => 16.99m, _ => 0m }; } }
Step 3: Update Your XAML
Make sure your DataGrid and ComboBox are properly bound to the collection and selected item:
<Window x:Class="RestaurantBillApp.RestaurantBillWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Restaurant Bill" Height="450" Width="800"> <Grid> <!-- Main Course ComboBox --> <ComboBox x:Name="MainCourseComboBox" HorizontalAlignment="Left" Margin="20,20,0,0" VerticalAlignment="Top" Width="200"> <ComboBoxItem>Seafood Alfredo</ComboBoxItem> <ComboBoxItem>Steak Frites</ComboBoxItem> <ComboBoxItem>Vegetarian Lasagna</ComboBoxItem> </ComboBox> <!-- Add Button --> <Button Content="Add to Bill" HorizontalAlignment="Left" Margin="230,20,0,0" VerticalAlignment="Top" Click="AddMenuItem_Click"/> <!-- DataGrid for Bill Items --> <DataGrid x:Name="BillDataGrid" ItemsSource="{Binding BillItems}" AutoGenerateColumns="False" Margin="20,60,20,20"> <DataGrid.Columns> <DataGridTextColumn Header="Main Course" Binding="{Binding Name}" IsReadOnly="True"/> <DataGridTextColumn Header="Price" Binding="{Binding Price, StringFormat=C}" IsReadOnly="True"/> <DataGridTextColumn Header="Quantity" Binding="{Binding Quantity}"/> <DataGridTextColumn Header="Total" Binding="{Binding Path=Price, StringFormat=C, Converter={StaticResource TotalConverter}}" IsReadOnly="True"/> </DataGrid.Columns> </DataGrid> </Grid> </Window>
Key Notes
- ObservableCollection: This is crucial because it tells the DataGrid when items are added/removed or when properties change (thanks to INotifyPropertyChanged on the model).
- Checking for Existing Items: We use
FirstOrDefaultto look for an item with the same name as the selected one. If you have a unique ID for each menu item, use that instead of the name for more accuracy. - Property Change Notifications: The
OnPropertyChangedmethod ensures that when we updateQuantity, the DataGrid immediately shows the new value without needing to refresh the entire collection.
This should fix your duplicate row issue—now clicking "Add" on "Seafood Alfredo" will just bump the quantity up instead of creating a new row.
内容的提问来源于stack exchange,提问作者Psycho Li

