Hi,@vitaminchik. Welcome to Microsoft Q&A.
According to your needs, in the MVVM mode, I have completed the calculation of the total amount of the item currently being edited in the collection.
To achieve the effect you described using the MVVM pattern, you could refer to the following code.
Install System.Windows.Interactivity.WPF and CommunityToolkit.Mvvm via NuGet
Model Layer
internal class ProductDetails:ObservableObject
{
private string name;
private int quantity;
private decimal price;
private decimal generalPrice;
private string data;
public string Name { get{ return name; } set { name = value;OnPropertyChanged(); } }
public int Quantity { get{ return quantity; } set { quantity = value; OnPropertyChanged(); } }
public decimal Price { get { return price; } set { price = value; OnPropertyChanged(); } }
public decimal GeneralPrice { get { return generalPrice; } set { generalPrice = value; OnPropertyChanged(); } }
public string Date { get { return data; } set { data = value; OnPropertyChanged(); } }
public ProductDetails(string name)
{
Name = name;
}
}
Notice
OnPropertyChanged():
When the data is updated, OnPropertyChanged can notify the view to update the data in time.
View Layer
<Window x:Class="Demo.MainWindow"
xmlns="[http://schemas.microsoft.com/winfx/2006/xaml/presentation](http://schemas.microsoft.com/winfx/2006/xaml/presentation)"
xmlns:x="[http://schemas.microsoft.com/winfx/2006/xaml](http://schemas.microsoft.com/winfx/2006/xaml)"
xmlns:d="[http://schemas.microsoft.com/expression/blend/2008](http://schemas.microsoft.com/expression/blend/2008)"
xmlns:mc="[http://schemas.openxmlformats.org/markup-compatibility/2006](http://schemas.openxmlformats.org/markup-compatibility/2006)"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.windows.Interactivity"
xmlns:local="clr-namespace:Demo"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<!--The bound resource is ProductDetails_Collection under MianViewModel-->
<ItemsControl Grid.Row="1" ItemsSource="{Binding ProductDetails_Collection}" x:Name="itemsControlOfPurchase" Margin="0,5,0,0" HorizontalAlignment="Center" Width="250" >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<WrapPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Center" Width="230" Margin="4">
<Border BorderBrush="Green" BorderThickness="1" Width="230">
<Grid x:Name="gridofCards">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="53*"></ColumnDefinition>
<ColumnDefinition Width="80*"></ColumnDefinition>
<ColumnDefinition Width="59*"/>
<ColumnDefinition Width="36*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" x:Name="textBlockOfProduct" Grid.Column="0" Padding="0 0 10 0" Width="NaN" Margin="6,10,73,10" Grid.ColumnSpan="2" Text="{Binding Name}" />
<TextBox Grid.Row="0" Text="{Binding Quantity,UpdateSourceTrigger=PropertyChanged}" Grid.Column="1" x:Name="textBoxOfQuantity" IsReadOnly="False" Padding="0 0 10 0" Width="NaN" Margin="20,10,22,10" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="TextChanged">
<i:InvokeCommandAction Command="{Binding Path=DataContext.CalculationGeneralQuantityCommand,RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ItemsControl,AncestorLevel=1}}" CommandParameter="{Binding}">
</i:InvokeCommandAction>
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
<TextBlock Grid.Row="0" Grid.Column="2" Text="pc." Padding="0 0 10 0" Width="NaN" Margin="10,10,8,10" Grid.ColumnSpan="2" />
<TextBlock Grid.Row="1" Grid.Column="0" Text="price" Padding="0 0 10 0" Width="NaN" Margin="6,3,0,3" />
<TextBox Grid.Row="1" Text="{Binding Price,UpdateSourceTrigger=PropertyChanged}" Grid.Column="1" x:Name="textBoxOfPrice" IsReadOnly="False" Padding="0 0 10 0" Width="NaN" Margin="20,3,22,3" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="TextChanged">
<i:InvokeCommandAction Command="{Binding Path=DataContext.CalculationGeneralQuantityCommand,RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ItemsControl,AncestorLevel=1}}" CommandParameter="{Binding}"></i:InvokeCommandAction>
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
<TextBlock Grid.Row="1" Grid.Column="1" Text="total $" Width="NaN" Margin="71,3,-32,3" Grid.ColumnSpan="3" />
<TextBox Grid.Row="1" Text="{Binding GeneralPrice}" IsReadOnly="True" Grid.Column="3" x:Name="textBoxOfTotalPrice" Width="NaN" Margin="0,3,2,3" />
</Grid>
</Border>
</WrapPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</Window>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
MainViewModel viewModel = new MainViewModel();
this.DataContext = viewModel;
}
}
Notice
1.UpdateSourceTrigger:
When UpdateSourceTrigger is set to PropertyChanged, each input will trigger the binding source update. The default UpdateSourceTrigger in TextBox is LostFocus, that is, the binding source is updated when the text box loses focus.
2.Interaction
<i:Interaction.Triggers>
<i:EventTrigger EventName="TextChanged">
<i:InvokeCommandAction Command="{Binding Path=DataContext.CalculationGeneralQuantityCommand,RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ItemsControl,AncestorLevel=1}}" CommandParameter="{Binding}">
</i:InvokeCommandAction>
</i:EventTrigger>
</i:Interaction.Triggers>
When the TextChanged event is triggered, the CalculationGeneralQuantityCommand is executed.
Here, relative binding is used to bind the CalculationGeneralQuantityCommand to the Command. AncestorType=ItemsControl,AncestorLevel=1 means: start from the current element to find the parent element and find the first ItemsControl.
Bind the current item to the CommandParameter, that is, pass the current item as a parameter to the CalculationGeneralQuantityCommand.
ViewModel Layer
internal class MainViewModel:ObservableObject
{
private ObservableCollection<ProductDetails> productDetails_Collection;
public ObservableCollection<ProductDetails> ProductDetails_Collection { get { return productDetails_Collection; } set { productDetails_Collection = value; OnPropertyChanged(); } }
public RelayCommand<ProductDetails> CalculationGeneralQuantityCommand { get; set; }
public MainViewModel() {
ProductDetails_Collection = new ObservableCollection<ProductDetails>() {
new ProductDetails("AA") { Price = 10, Quantity = 5, Date = "2024" , GeneralPrice = 10 * 5},
new ProductDetails("BB") { Price = 20, Quantity = 3, Date = "2023" , GeneralPrice = 20 * 3}
};
CalculationGeneralQuantityCommand = new RelayCommand<ProductDetails>(CalculationGeneralQuantity);
}
public void CalculationGeneralQuantity(ProductDetails productDetails)
{
productDetails.GeneralPrice = productDetails.Price * productDetails.Quantity;
}
}
Notice
RelayCommand:
Use RelayCommand to accept parameters and execute the method you want to execute
Additional
In the code you gave, there are TextBoxOfPrice_TextChanged and TextBoxOfQuantity_TextChanged.
Their function should be to convert the value type.
You could consider using IValueConverter. The relevant documents about IValueConverter are as follows: https://learn.microsoft.com/en-us/dotnet/desktop/wpf/data/how-to-convert-bound-data?view=netframeworkdesktop-4.8
At last
If you have questions, please describe your problem in as much detail as possible.
If the answer is the right solution, please click "Accept Answer" and kindly upvote it. If you have extra questions about this answer, please click "Comment".
Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.