How to get the data of the selected DataGrid row in MVVM? If I understand correctly, the data of the selected DataGrid line falls into the SelectedProduct, and from there it is distributed to the TextBox. Besides all this, I need to get the elements of the selected row ProductName, ProductID, TotalSold (to use them for other purposes and classes). Tried it this way: SelectedProduct.ProductName - does not work.

public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } } public class ProductModel : INotifyPropertyChanged { private Int32 _ProductID; private String _ProductName; private Int32 _TotalSold; public Int32 TotalSold { get { return _TotalSold; } set { _TotalSold = value; OnPropertyChanged("TotalSold"); } } public String ProductName { get { return _ProductName; } set { _ProductName = value; OnPropertyChanged("ProductName"); } } public Int32 ProductID { get { return _ProductID; } set { _ProductID = value; OnPropertyChanged("ProductID"); } } public ProductModel(Int32 productID, String productName, Int32 totalSold) { this.ProductID = productID; this.ProductName = productName; this.TotalSold = totalSold; } public event PropertyChangedEventHandler PropertyChanged; public void OnPropertyChanged(String propertyName) { PropertyChangedEventHandler handler = PropertyChanged; if (handler != null) { this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } } public class ProductGridViewModel { private ProductModel _SelectedProduct; private ObservableCollection<ProductModel> _Products; public ObservableCollection<ProductModel> Products { get { return _Products; } set { _Products = value; } } public ProductModel SelectedProduct { get { return _SelectedProduct; } set { _SelectedProduct = value; } } public ProductGridViewModel() { Products = new ObservableCollection<ProductModel>(); GenerateProducts(); } private void GenerateProducts() { for (int x = 0; x < 100; x++) { this.Products.Add(new ProductModel(x, "Product #" + x, x + 50)); } } } <Window x:Class="MVVM.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:ViewModel="clr-namespace:MVVM" Title="MainWindow" Height="350" Width="420" Background="Gray"> <Window.Resources> <ViewModel:ProductGridViewModel x:Key="ProductViewModel"/> </Window.Resources> <Grid DataContext="{StaticResource ResourceKey=ProductViewModel}"> <Grid.ColumnDefinitions> <ColumnDefinition Width="300"/> <ColumnDefinition Width="100"/> </Grid.ColumnDefinitions> <DataGrid Width="500" Grid.Column="0" AutoGenerateColumns="False" ItemsSource="{Binding Products}" SelectedItem="{Binding SelectedProduct, Mode=TwoWay}"> <DataGrid.Columns> <DataGridTextColumn IsReadOnly="True" Header="Product ID" Binding="{Binding ProductID, UpdateSourceTrigger=PropertyChanged}" /> <DataGridTextColumn IsReadOnly="True" Header="Product Name" Binding="{Binding ProductName, UpdateSourceTrigger=PropertyChanged}" /> <DataGridTextColumn IsReadOnly="True" Header="Total Sold" Binding="{Binding TotalSold, UpdateSourceTrigger=PropertyChanged}" /> </DataGrid.Columns> </DataGrid> <StackPanel Height="100" Background="Wheat" Margin="10" Orientation="Vertical" Grid.Column="1"> <TextBlock FontWeight="Bold" Width="100" TextWrapping="Wrap">Update your product info!</TextBlock> <TextBox Width="100" Text="{Binding SelectedProduct.ProductName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" /> <TextBox Width="100" Text="{Binding SelectedProduct.TotalSold, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" /> </StackPanel> </Grid> </Window> 
  • set { _SelectedProduct = value; } set { _SelectedProduct = value; } - where is the call to the PropertyChanged event? - Bulson
  • @Bulson for this, you also need INotifyPropertyChanged implement - tym32167
  • set {_SelectedProduct = value; } - works without PropertyChanged. I applied PropertyChanged to it, too, no result. - Anton
  • "set {_SelectedProduct = value;} - works without PropertyChanged" - you also need to read about the bindings and about this event + think a little about what it is done for. When you select a string in a datagrid, the value of the SelectedProduct property changes - this seems to be obvious, and then, it does not seem obvious to you that in order "... from there are heard in TextBox ..." these text boxes found that they had new values ​​have appeared and they need to be re-read. - Bulson
  • Bulson, thank you for the clarification. TextBox works with and without PropertyChanged in SelectedProduct. There are no problems with TextBox. My task is to get separate elements of the selected DataGrid row, namely ProductName, ProductID, TotalSold. I am applying PropertyChanged in SelectedProduct - no results. - Anton

1 answer 1

work example

Model class

 public class ProductModel : INotifyPropertyChanged { //INPC public event PropertyChangedEventHandler PropertyChanged; //ctor public ProductModel(Int32 productID, String productName, Int32 totalSold) { if (string.IsNullOrEmpty(productName)) { throw new ArgumentException(nameof(productName)); } ProductID = productID; ProductName = productName; TotalSold = totalSold; } private Int32 _TotalSold; public Int32 TotalSold { get { return _TotalSold; } set { _TotalSold = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(TotalSold))); } } private String _ProductName; public String ProductName { get { return _ProductName; } set { _ProductName = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ProductName))); } } private Int32 _ProductID; public Int32 ProductID { get { return _ProductID; } set { _ProductID = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ProductID))); } } } 

View class

 public class ProductGridViewModel : INotifyPropertyChanged { private readonly IMainWindow _mainWindow; //INPC public event PropertyChangedEventHandler PropertyChanged; //ctor public ProductGridViewModel(IMainWindow mainWindow) { this._mainWindow = mainWindow ?? throw new ArgumentNullException(nameof(mainWindow)); Products = new ObservableCollection<ProductModel>(); GenerateProducts(); } //--Properties /// <summary> /// Продукты /// </summary> private ObservableCollection<ProductModel> _Products; public ObservableCollection<ProductModel> Products { get { return _Products; } set { _Products = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Products))); } } /// <summary> /// Выбранный продукт /// </summary> private ProductModel _SelectedProduct; public ProductModel SelectedProduct { get { return _SelectedProduct; } set { _SelectedProduct = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SelectedProduct))); ShowSelectedProductCommand.RaiseCanExecuteChanged(); //обновляем доступность кнопки } } //--Methods private void GenerateProducts() { for (int x = 0; x < 100; x++) { this.Products.Add(new ProductModel(x, "Product #" + x, x + 50)); } } //--Commands private RelayCommand _ShowSelectedProductCommand; public RelayCommand ShowSelectedProductCommand { get => _ShowSelectedProductCommand = _ShowSelectedProductCommand ?? new RelayCommand(OnShowSelected, CanShowSelected); } private bool CanShowSelected() { if (SelectedProduct == null) return false; return true; } private void OnShowSelected() { string message = $"{SelectedProduct.ProductID}- {SelectedProduct.ProductName}"; _mainWindow.ShowMessage(message); } } 

Xaml

 <Grid x:Name="_gridProducts"> <Grid.ColumnDefinitions> <ColumnDefinition Width="300"/> <ColumnDefinition Width="100"/> </Grid.ColumnDefinitions> <DataGrid Width="500" Grid.Column="0" AutoGenerateColumns="False" ItemsSource="{Binding Products}" SelectedItem="{Binding SelectedProduct, Mode=TwoWay}"> <DataGrid.Columns> <DataGridTextColumn IsReadOnly="True" Header="Product ID" Binding="{Binding ProductID, UpdateSourceTrigger=PropertyChanged}" /> <DataGridTextColumn IsReadOnly="True" Header="Product Name" Binding="{Binding ProductName, UpdateSourceTrigger=PropertyChanged}" /> <DataGridTextColumn IsReadOnly="True" Header="Total Sold" Binding="{Binding TotalSold, UpdateSourceTrigger=PropertyChanged}" /> </DataGrid.Columns> </DataGrid> <StackPanel Height="100" Background="Wheat" Margin="10" Orientation="Vertical" Grid.Column="1"> <TextBlock FontWeight="Bold" Width="100" TextWrapping="Wrap">Update your product info!</TextBlock> <TextBox Width="100" Text="{Binding SelectedProduct.ProductName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" /> <TextBox Width="100" Text="{Binding SelectedProduct.TotalSold, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" /> <Button Content="Смотреть" Command="{Binding ShowSelectedProductCommand}"/> </StackPanel> </Grid> 

Codbihind

 public interface IMainWindow { void ShowMessage(string message); } public partial class MainWindow : Window, IMainWindow { public MainWindow() { InitializeComponent(); // var viewModel = new ProductGridViewModel(this); this._gridProducts.DataContext = viewModel; } public void ShowMessage(string message) { string caption = "Вы выбрали"; MessageBox.Show(message, caption, MessageBoxButton.OK, MessageBoxImage.Information); } } 

The whole example can be downloaded here.

  • Hmm, what is INPC for Products here for? After all, we do not re-initialize it all the time, we have it alone. And that means that there is no sense in notifying about the change of this property. After all, public ObservableCollection<ProductModel> Products { get; set; } public ObservableCollection<ProductModel> Products { get; set; } or am I mistaken? - EvgeniyZ
  • @EvgeniyZ in this particular case, you are right, but I did an example for the author of the question with an eye to the future. In short, do not judge strictly :) - Bulson
  • I just always see that people implement for properties like ObservableCollection INPC and this, as for me, they mislead others. Very often I see newbies that INPC put on absolutely all properties, seeing this in others)) At first I thought that maybe I don’t know what and for ObservableCollection this is necessary, but not ... So as for me for beginners, you should immediately submit correct information. Well, austerity, oh, you have a lot of lines of code here you can "cut off", I did not say anything about it)) Well, so like for your efforts;) - EvgeniyZ
  • @EvgeniyZ, thanks for the positive attitude ... Well, you intrigued me directly ... "you can cut off a lot of code here, give a hint at least a couple of places, which is not so. - Bulson
  • one
    @EvgeniyZ is really an example, because I took the author’s code completely, and changed something and added it. It will be easier for him to trace the difference in what was written by him and what I have changed. - Bulson