How can I update a Datagrid from another window

John Sparks 111 Reputation points
2023-12-26T16:56:40.4+00:00

I have a main window with a DataGrid displaying the properties of "Car" Objects from a list of Car Objects. When I user selects a row I grab the selected row, cast it to an a Car object and then display the properties of the Object in another window using this code.

private void CardDataGrid_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
CarDetailsWindow CarDetails = new();
CarDetails.Car = (Car)CarDataGrid.CurrentItem;
CarDetails.Show();
}

The new window captures changes in the properties of the Car (make etc). I can then save the object to a file fine. However I'd like to be able to update the the DataGrid with the updated Car when the window is closed and the user is returned to the DataGrid window.

How can I do this? Any of the Car Properties can change, including updating the Car name. and later on I'd like users to be able to open a few CarDetails Windows (to compare Cars), so I cant rely on currently item as this might change. Do I need to give each car a unique reference or can I use the Index of the originally selected row and store it to then update using that as a reference?

I need to know how to update the right row and how to update the Object its based on.

Thanks!

Developer technologies | Windows Presentation Foundation
Developer technologies | C#
0 comments No comments
{count} votes

Accepted answer
  1. Hui Liu-MSFT 48,676 Reputation points Microsoft External Staff
    2023-12-27T05:41:56.4833333+00:00

    Hi,@ John Sparks. Welcome to Microsoft Q&A.

    The following example opens another window for editing when an item in the DataGrid is selected, and then updates the DataGrid. You could try to refer to it .

    MainWindow.xaml:

     <StackPanel>
            
            <DataGrid ItemsSource="{Binding Cars}" SelectedItem="{Binding SelectedCar, Mode=TwoWay}"
                         CanUserDeleteRows="False" SelectionMode="Single" SelectedIndex="{Binding SelectedIndex}" 
                      AutoGenerateColumns="False"  MouseDoubleClick="DataGrid_MouseDoubleClick" >
                <DataGrid.Columns>
                    <DataGridTextColumn   Header="Make"  Binding="{Binding Path=Make, Mode=TwoWay}"  Width="105"   IsReadOnly="True" />
                    <DataGridTextColumn  Header="Mode"  Binding="{Binding Path=Model, Mode=TwoWay}"  Width="75"  IsReadOnly="True" />
                    <DataGridTextColumn   Header="Year "   Binding="{Binding Path=Year, StringFormat=d, Mode=TwoWay}"  Width="75"  IsReadOnly="True" />
                
                </DataGrid.Columns>
            </DataGrid>
           
        </StackPanel>
    

    MainWindow.xaml.cs:

     public partial class MainWindow : Window  , INotifyPropertyChanged
      {
        CarViewModel vm= new CarViewModel();
        public MainWindow()
        {
          InitializeComponent();
          DataContext=vm;
        }
        private void DataGrid_MouseDoubleClick(object sender, MouseButtonEventArgs e)
        {
          if (sender is DataGrid grid && grid.SelectedItems.Count == 1)
          {
            var selectedCar = grid.SelectedItem as Car;
            if (selectedCar != null)
            {
              DetailWindow detailWindow = new DetailWindow(selectedCar);
              detailWindow.DataContext=vm;
              detailWindow.ShowDialog();
             
            }
          }
        }
        public event PropertyChangedEventHandler PropertyChanged;
    
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
          PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
      }
      public class CarViewModel : INotifyPropertyChanged
      {
        public ObservableCollection<Car> Cars { get; set; } = new ObservableCollection<Car>();
    
        public CarViewModel()
        {
          LoadCars();
          UpdateBindingGroup = new BindingGroup { Name = "Group1" };
          CancelCommand = new RelayCommand(DoCancel);
          SaveCommand = new RelayCommand(DoSave);
          AddCommand = new RelayCommand(AddCar);
          DeleteUserCommand = new RelayCommand(DeleteCar);
        }
        private Car _selectedCar;
    
        public Car SelectedCar
        {
          get { return _selectedCar; }
          set
          {
            _selectedCar = value;
            OnPropertyChanged();
          }
        }
        public int SelectedIndex { get; set; }
        private void LoadCars()
        {
          Cars.Add(new Car { Make = "Toyota", Model = "Camry", Year = new DateTime(2021,7,8) });
          Cars.Add(new Car { Make = "Honda", Model = "Civic", Year = new DateTime(2021, 7, 8) });
          Cars.Add(new Car { Make = "Ford", Model = "Focus", Year = new DateTime (2021,7,8   ) });
        }
        private BindingGroup _UpdateBindingGroup;
        public BindingGroup UpdateBindingGroup
        {
          get
          {
            return _UpdateBindingGroup;
          }
          set
          {
            if (_UpdateBindingGroup != value)
            {
              _UpdateBindingGroup = value;
              OnPropertyChanged("UpdateBindingGroup");
            }
          }
        }
        public RelayCommand CancelCommand { get; set; }
        public RelayCommand SaveCommand { get; set; }
        public RelayCommand AddCommand { get; set; }
        public RelayCommand DeleteUserCommand { get; set; }
    
        void DoCancel(object param)
        {
          UpdateBindingGroup.CancelEdit();
          if (SelectedIndex == -1)    //This only closes if new - just to show you how CancelEdit returns old values to bindings  
            SelectedCar = null;
        }
    
        void DoSave(object param)
        {
          UpdateBindingGroup.CommitEdit();
          var car = SelectedCar as Car;
          if (SelectedIndex == -1)
          {
            Cars.Add(car);
            OnPropertyChanged("Cars");  
          }
          else
            OnPropertyChanged("Cars");
    
          SelectedCar = null;
        }
    
        void AddCar(object param)
        {
    
    
          SelectedCar = null; 
          var car = new Car();
          SelectedCar = car;
        }
    
        void DeleteCar(object parameter)
        {
          var car = SelectedCar as Car;
          if (SelectedIndex != -1)
          {
            Cars.Remove(car);
            OnPropertyChanged("Cars"); 
          }
          else
            SelectedCar = null; 
        }
        public event PropertyChangedEventHandler PropertyChanged;
    
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
          PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
      }
    
      public class SelectedItemToItemsSource : IValueConverter
      {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
          if (value == null) return null;
          return new List<object>() { value };
        }
    
        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
          throw new NotImplementedException();
        }
      }
      public class Car : INotifyPropertyChanged
      {
        private string _make;
        public string Make
        {
          get { return _make; }
          set
          {
            if (_make != value)
            {
              _make = value;
              OnPropertyChanged();
            }
          }
        }
        private string _model;
        public string Model
        {
          get { return _model; }
          set
          {
            if (_model != value)
            {
              _model = value;
              OnPropertyChanged();
            }
          }
        }
        private DateTime _year;
        public DateTime Year
        {
          get { return _year; }
          set
          {
            if (_year != value)
            {
              _year = value;
              OnPropertyChanged();
            }
          }
        }
        public event PropertyChangedEventHandler PropertyChanged;
    
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
          PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
      }
    
    
    

    DetailWindow.xaml:

     <Window.Resources>
            <local:SelectedItemToItemsSource x:Key="SelectedItemToItemsSource"/>
            <DataTemplate x:Key="UserGrid">
                <Border Background="Chocolate" BorderBrush="Black" BorderThickness="1" CornerRadius="5" >
                    <Grid Margin="10">
                        <Grid.RowDefinitions>
                            <RowDefinition/>
                            <RowDefinition/>
                            <RowDefinition/>
                            <RowDefinition/>
                         
                            <RowDefinition/>
                            <RowDefinition/>
                        </Grid.RowDefinitions>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition/>
                            <ColumnDefinition/>
                        </Grid.ColumnDefinitions>
                        <TextBlock Text=" Make" Grid.Row="1" Grid.Column="0"/>
                        <TextBlock Text="Mode" Grid.Row="2" Grid.Column="0"/>
                        <TextBlock Text="Year" Grid.Row="3" Grid.Column="0"/>
                        <TextBox Text="{Binding Make, BindingGroupName=Group1, UpdateSourceTrigger=Explicit}" Grid.Column="1" Grid.Row="1"/>
                        <TextBox Text="{Binding Model, BindingGroupName=Group1, UpdateSourceTrigger=Explicit}" Grid.Column="1" Grid.Row="2"/>
                        <TextBox Text="{Binding Year, BindingGroupName=Group1, UpdateSourceTrigger=Explicit}" Grid.Column="1" Grid.Row="3"/>
                        
                        <StackPanel Orientation="Horizontal" Grid.Row="10" Grid.ColumnSpan="2" HorizontalAlignment="Right" Margin="5,5,5,5">
                            <Button Foreground="White" Background="Green" Content="Cancel" Command="{Binding DataContext.CancelCommand, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}" Margin="4,0"/>
                            <Button Foreground="White" Background="Green" Content="Delete" Command="{Binding DataContext.DeleteUserCommand, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}" Margin="4,0"/>
                            <Button Foreground="White" Background="Green" Content="Save" Command="{Binding DataContext.SaveCommand, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}" Margin="4,0"/>
                            <Button Foreground="White" Background="Green" Content="Add" Command="{Binding DataContext.AddCommand, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}" Margin="4,0"/>
                        </StackPanel>
                    </Grid>
                </Border>
            </DataTemplate>
        </Window.Resources>
        <StackPanel>
            <ItemsControl BindingGroup="{Binding UpdateBindingGroup, Mode=OneWay}"  VerticalAlignment="Top" Margin="5,5,5,5" Grid.Column="1"  
                          ItemTemplate="{StaticResource UserGrid}"  ItemsSource="{Binding SelectedCar, Converter={StaticResource SelectedItemToItemsSource}}"    />
        </StackPanel>
    

    Detail.xaml.cs:

    public partial class DetailWindow : Window
      {
     
        public DetailWindow(Car car)
        {
          InitializeComponent();
         
        }
      }  
      public class RelayCommand : ICommand
      {
        private readonly Action<object> _execute;
        private readonly Predicate<object> _canExecute;
    
        public RelayCommand(Action<object> execute)
            : this(execute, null)
        {
        }
    
        public RelayCommand(Action<object> execute, Predicate<object> canExecute)
        {
          if (execute == null)
            throw new ArgumentNullException("execute");
          _execute = execute;
          _canExecute = canExecute;
        }
    
        public bool CanExecute(object parameter)
        {
          return _canExecute == null ? true : _canExecute(parameter);
        }
    
        public event EventHandler CanExecuteChanged
        {
          add { CommandManager.RequerySuggested += value; }
          remove { CommandManager.RequerySuggested -= value; }
        }
    
        public void Execute(object parameter)
        {
          _execute(parameter);
        }
      }
    
    
    

    The result:

    4


    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.

    1 person found this answer helpful.

1 additional answer

Sort by: Most helpful
  1. Peter Fleischer (former MVP) 19,341 Reputation points
    2023-12-27T13:03:14.4666667+00:00

    Hi,
    try following demo:

    XAML:

    <Window x:Class="WpfApp1.Window130"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:WpfApp130"
            mc:Ignorable="d"
            Title="John Sparks_231226" Height="300" Width="300">
      <Window.Resources>
        <local:ViewModel x:Key="vm"/>
      </Window.Resources>
      <Grid DataContext="{StaticResource vm}" Margin="5">
        <DataGrid ItemsSource="{Binding View}" 
                  IsReadOnly="True" 
                  AutoGenerateColumns="False"
                  local:ViewModel.AttProp="True">
          <DataGrid.Columns>
            <DataGridTextColumn Binding="{Binding Make}"/>
            <DataGridTextColumn Binding="{Binding Model}"/>
          </DataGrid.Columns>
        </DataGrid>
      </Grid>
    </Window>
    

    XAML detail window:

    <Window x:Class="WpfApp1.Window130Detail"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:vm="clr-namespace:WpfApp130"
            mc:Ignorable="d"
            Title="Window130Sub" Height="200" Width="300"
            vm:ViewModelDetail.AttProp="True">
      <Grid>
        <Grid.RowDefinitions>
          <RowDefinition Height="auto"/>
          <RowDefinition Height="auto"/>
        </Grid.RowDefinitions>
        <Grid DataContext="{Binding View}">
          <Grid.ColumnDefinitions>
            <ColumnDefinition Width="auto"/>
            <ColumnDefinition/>
          </Grid.ColumnDefinitions>
          <Grid.RowDefinitions>
            <RowDefinition Height="auto"/>
            <RowDefinition Height="auto"/>
            <RowDefinition Height="auto"/>
          </Grid.RowDefinitions>
          <Label Grid.Row="0" Grid.Column="0" Content="Make:" HorizontalAlignment="Right" Margin="5"/>
          <TextBox Grid.Row="0" Grid.Column="1" Text="{Binding Make}" Margin="5"/>
          <Label Grid.Row="1" Grid.Column="0" Content="Model:" HorizontalAlignment="Right" Margin="5"/>
          <TextBox Grid.Row="1" Grid.Column="1" Text="{Binding Model}" Margin="5"/>
          <Label Grid.Row="2" Grid.Column="0" Content="Year:" HorizontalAlignment="Right" Margin="5"/>
          <TextBox Grid.Row="2" Grid.Column="1" Text="{Binding Year}" Margin="5"/>
        </Grid>
        <StackPanel Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="2" Orientation="Horizontal" HorizontalAlignment="Right">
          <Button Content="Save" Command="{Binding Cmd}" CommandParameter="Save" Margin="5"/>
          <Button Content="Delete" Command="{Binding Cmd}" CommandParameter="Delete" Margin="5"/>
          <Button Content="Cancel" Command="{Binding Cmd}" CommandParameter="Cancel" Margin="5"/>
        </StackPanel>
      </Grid>
    </Window>
    

    Code:

    using System;
    using System.Collections.ObjectModel;
    using System.ComponentModel;
    using System.Runtime.CompilerServices;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Input;
    using WpfApp1;
    
    namespace WpfApp130
    {
    	public class ViewModel : INotifyPropertyChanged
    	{
    		public ViewModel() => refreshData();
    		private CollectionViewSource cvs = new CollectionViewSource(); // View source for displaying
    		public ICollectionView View { get => cvs.View; }
    
    		private void refreshData()
    		{
    			cvs.Source = CarManger.GetData();
    			OnPropertyChanged(nameof(View));
    		}
    
    		public ICommand Cmd { get => new RelayCommand(CmdExec); }
    		private void CmdExec(object obj)
    		{
    			switch (obj.ToString())
    			{
    				case "Refresh":
    					refreshData();
    					break;
    				case "New":
    					Car newItem = new Car();
    					CmdEditExec(newItem);
    					break;
    				default:
    					break;
    			}
    		}
    		public ICommand CmdEdit { get => new RelayCommand(CmdEditExec); }
    		private void CmdEditExec(object obj)
    		{
    			Window130Detail wnd = new Window130Detail();
    			ViewModelDetail vm = new ViewModelDetail();
    			wnd.DataContext = vm;
    			Car item = obj as Car;
    			if (item != null)
    			{
    				vm.View = item;
    				bool? toSave = wnd.ShowDialog();
    				if (toSave.Value)
    				{
    					if (item.ID == 0) CarManger.AddData(item);
    					else CarManger.UpdateData(item);
    				}
    				else
    				{
    					if (item.ID != 0) CarManger.ReloadData(item);
    				}
    			}
    			refreshData();
    		}
    		public ICommand CmdDelete { get => new RelayCommand(CmdDeleteExec); }
    		private void CmdDeleteExec(object obj)
    		{
    			Car item = obj as Car;
    			if (item != null) CarManger.DeleteData(item);
    			refreshData();
    		}
    
    		public static readonly DependencyProperty AttPropProperty = DependencyProperty.Register("AttProp",
    typeof(bool), typeof(DataGrid), new UIPropertyMetadata(false, OnAttProp));
    		public static bool GetAttProp(DependencyObject obj) => (bool)obj.GetValue(AttPropProperty);
    		public static void SetAttProp(DependencyObject obj, bool value) => obj.SetValue(AttPropProperty, value);
    		private static void OnAttProp(DependencyObject depObj, DependencyPropertyChangedEventArgs e)
    		{
    			var dg = depObj as DataGrid;
    			if (dg == null) return;
    			if ((e.NewValue is bool) && (bool)(e.NewValue))
    				dg.MouseDoubleClick += Dg_MouseDoubleClick; ;
    		}
    		private static void Dg_MouseDoubleClick(object sender, MouseButtonEventArgs e)
    		{
    			DataGrid dg = sender as DataGrid;
    			if (dg == null) return;
    			ViewModel dc = dg.DataContext as ViewModel;
    			if (dc == null) return;
    			dc.CmdEditExec(dg.CurrentItem);
    		}
    
    		public event PropertyChangedEventHandler PropertyChanged;
    		private void OnPropertyChanged([CallerMemberName] String propertyName = "") =>
    			PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    	}
    
    	public class ViewModelDetail
    	{
    		public object View { get; set; }
    		public ICommand Cmd { get => new RelayCommand(CmdExec); }
    
    		private void CmdExec(object obj)
    		{
    			switch (obj.ToString())
    			{
    				case "Save":
    					SubWindow.DialogResult = true;
    					SubWindow.Close();
    					break;
    				case "Delete":
    					CarManger.DeleteData((Car)View);
    					SubWindow.DialogResult = true;
    					SubWindow.Close();
    					break;
    				case "Cancel":
    					SubWindow.DialogResult = false;
    					SubWindow.Close();
    					break;
    				default:
    					break;
    			}
    		}
    		public static readonly DependencyProperty AttPropProperty = DependencyProperty.Register("AttProp",
    	typeof(bool), typeof(Window), new UIPropertyMetadata(false, OnAttProp));
    		public static bool GetAttProp(DependencyObject obj) => (bool)obj.GetValue(AttPropProperty);
    		public static void SetAttProp(DependencyObject obj, bool value) => obj.SetValue(AttPropProperty, value);
    		private static void OnAttProp(DependencyObject depObj, DependencyPropertyChangedEventArgs e)
    		{
    			var wnd = depObj as Window;
    			if (wnd == null) return;
    			if ((e.NewValue is bool) && (bool)(e.NewValue))
    				wnd.Loaded += Wnd_Loaded;
    		}
    		private static void Wnd_Loaded(object sender, RoutedEventArgs e)
    		{
    			Window wnd = sender as Window;
    			if (wnd == null) return;
    			ViewModelDetail dc = wnd.DataContext as ViewModelDetail;
    			if (dc == null) return;
    			dc.SubWindow = wnd;
    		}
    		Window SubWindow { get; set; }
    	}
    
    	static class CarManger
    	{
    		static CarManger()
    		{
    			Cars.Add(new Car { ID = 1, Make = "Toyota", Model = "Camry", Year = new DateTime(2021, 7, 8) });
    			Cars.Add(new Car { ID = 2, Make = "Honda", Model = "Civic", Year = new DateTime(2021, 7, 8) });
    			Cars.Add(new Car { ID = 3, Make = "Ford", Model = "Focus", Year = new DateTime(2021, 7, 8) });
    		}
    		static ObservableCollection<Car> Cars { get; set; } = new ObservableCollection<Car>();
    		internal static void AddData(Car item) => Cars.Add(item);
    		internal static void DeleteData(Car view) => Cars.Remove(view);
    		internal static object GetData() => Cars;
    		internal static void ReloadData(Car item) { }
    		internal static void UpdateData(Car item) { }
    	}
    
    
    	public class Car : INotifyPropertyChanged
    	{
    		public int ID { get; set; }
    
    		private string _make;
    		public string Make
    		{
    			get => _make;
    			set { _make = value; OnPropertyChanged(); }
    		}
    
    		private string _model;
    		public string Model
    		{
    			get => _model;
    			set { _model = value; OnPropertyChanged(); }
    		}
    
    		private DateTime _year;
    		public DateTime Year
    		{
    			get => _year;
    			set { _year = value; OnPropertyChanged(); }
    		}
    
    		public event PropertyChangedEventHandler PropertyChanged;
    		private void OnPropertyChanged([CallerMemberName] String propertyName = "") =>
    			PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    	}
    
    	public class RelayCommand : ICommand
    	{
    		private readonly Predicate<object> _canExecute;
    		private readonly Action<object> _action;
    		public RelayCommand(Action<object> action) { _action = action; _canExecute = null; }
    		public RelayCommand(Action<object> action, Predicate<object> canExecute) { _action = action; _canExecute = canExecute; }
    		public void Execute(object o) => _action(o);
    		public bool CanExecute(object o) => _canExecute == null ? true : _canExecute(o);
    		public event EventHandler CanExecuteChanged
    		{
    			add { CommandManager.RequerySuggested += value; }
    			remove { CommandManager.RequerySuggested -= value; }
    		}
    	}
    }
    
    

    Result:

    x

    1 person found this answer helpful.

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.