How to open an item from Listview in new window and edit it?

Juliette 1 Reputation point
2022-01-15T18:57:48.707+00:00

Related to the question here (how-to-update-listview-using-mvvm-pattern-and-obse.html), I would like to know how to do the following:

1) Save the list of users in database? (The Database should update itself when adding, editing or deleting items in the listview).
2) Open the selected item in Window1 and edit it and then save it again?
3) The window should be closed after clicking the save button. How should this be done?

Windows Presentation Foundation
Windows Presentation Foundation
A part of the .NET Framework that provides a unified programming model for building line-of-business desktop applications on Windows.
2,775 questions
{count} votes

2 answers

Sort by: Most helpful
  1. Peter Fleischer (former MVP) 19,321 Reputation points
    2022-01-16T12:42:28.4+00:00

    Hi Juliette,
    I build a Core5 demo with ADO.NET. Try it:

    XAML MainWindow:

    <Window x:Class="WpfApp1.Window007"  
            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:WpfApp007"  
            mc:Ignorable="d"  
            Title="Window007" Height="450" Width="800">  
      <Window.Resources>  
        <local:ViewModel x:Key="vm"/>  
        <Style TargetType="{x:Type Button}">  
          <Setter Property="Margin" Value="5"/>  
          <Setter Property="Width" Value="75"/>  
        </Style>  
      </Window.Resources>  
      <Grid DataContext="{StaticResource vm}">  
        <Grid.RowDefinitions>  
          <RowDefinition/>  
          <RowDefinition Height="Auto"/>  
          <RowDefinition Height="Auto"/>  
        </Grid.RowDefinitions>  
        <DataGrid ItemsSource="{Binding View}" AutoGenerateColumns="False">  
          <DataGrid.Columns>  
            <DataGridTextColumn Header="Name" Binding="{Binding Name}"/>  
            <DataGridTemplateColumn>  
              <DataGridTemplateColumn.CellTemplate>  
                <DataTemplate>  
                  <StackPanel Orientation="Horizontal">  
                    <Button Content="Edit" Command="{Binding Edit, Source={StaticResource vm}}" CommandParameter="{Binding}" Margin="5"/>  
                    <Button Content="Delete" Command="{Binding Delete, Source={StaticResource vm}}" CommandParameter="{Binding}" Margin="5"/>  
                  </StackPanel>  
                </DataTemplate>  
              </DataGridTemplateColumn.CellTemplate>  
            </DataGridTemplateColumn>  
          </DataGrid.Columns>  
        </DataGrid>  
        <StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Right">  
          <Button Content="Create DB" Command="{Binding Cmd}" CommandParameter="Create" Margin="5"/>  
          <Button Content="Load data" Command="{Binding Cmd}" CommandParameter="Load" Margin="5"/>  
          <Button Content="Add" Command="{Binding Cmd}" CommandParameter="Add" Margin="5"/>  
        </StackPanel>  
        <TextBlock Grid.Row="2" Text="{Binding Status}" TextWrapping="Wrap"/>  
      </Grid>  
    </Window>  
    

    XAML DetailWindow:

    <Window x:Class="WpfApp1.Window007Detail"  
            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:WpfApp007"  
            mc:Ignorable="d"  
            Title="Details" Height="200" Width="400"  
            local:ViewModelDetail.AttProp = "True">  
      <Window.Resources>  
        <Style TargetType="{x:Type Label}">  
          <Setter Property="Margin" Value="5"/>  
          <Setter Property="HorizontalAlignment" Value="Right"/>  
        </Style>  
        <Style TargetType="{x:Type TextBox}">  
          <Setter Property="Margin" Value="5"/>  
        </Style>  
        <Style TargetType="{x:Type Button}">  
          <Setter Property="Margin" Value="5"/>  
          <Setter Property="Width" Value="100"/>  
        </Style>  
      </Window.Resources>  
      <Grid>  
        <Grid.RowDefinitions>  
          <RowDefinition Height="Auto"/>  
          <RowDefinition Height="Auto"/>  
          <RowDefinition/>  
        </Grid.RowDefinitions>  
        <Grid Grid.Row="0" DataContext="{Binding Row}">  
          <Grid.RowDefinitions>  
            <RowDefinition Height="Auto"/>  
            <RowDefinition Height="Auto"/>  
            <RowDefinition/>  
          </Grid.RowDefinitions>  
          <Grid.ColumnDefinitions>  
            <ColumnDefinition Width="Auto"/>  
            <ColumnDefinition/>  
          </Grid.ColumnDefinitions>  
          <Label Grid.Row="0" Grid.Column="0" Content="Name:"/>  
          <TextBox Grid.Row="0" Grid.Column="1" Text="{Binding Name}"/>  
        </Grid>  
        <StackPanel Grid.Row="1" Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Right">  
          <Button Content="Save" Command="{Binding}" CommandParameter="Save"/>  
          <Button Content="Cancel" Command="{Binding}" CommandParameter="Cancel"/>  
        </StackPanel>  
      </Grid>  
    </Window>  
    

    and code:

    using System;  
    using System.Collections.ObjectModel;  
    using System.ComponentModel;  
    using System.Data;  
    using System.Data.SqlClient;  
    using System.Runtime.CompilerServices;  
    using System.Windows;  
    using System.Windows.Data;  
    using System.Windows.Input;  
    using WpfApp1;  
      
    namespace WpfApp007  
    {  
      public class ViewModel : INotifyPropertyChanged  
      {  
        Model model = new Model(WpfApp1.Properties.Settings.Default.cnSQL);  
        private CollectionViewSource cvs = new CollectionViewSource();  
        public ICollectionView View { get => cvs.View; }  
      
        public ICommand Cmd { get => new RelayCommand(CmdExec, CanCmdExec); }  
      
        private void CmdExec(object obj)  
        {  
          try  
          {  
            IsWorking = true;  
            switch (obj.ToString())  
            {  
              case "Load":  
                model.LoadData();  
                cvs.Source = model.GetData();  
                OnPropertyChanged(nameof(View));  
                Status = "Data loaded!";  
                break;  
              case "Add":  
                ViewModelDetail vm = new ViewModelDetail() { Row = model.GetNewData() };  
                Window007Detail wnd = new Window007Detail() { DataContext = vm };  
                if (wnd.ShowDialog().Value) model.AddData(vm.Row);  
                Status = "Data added!";  
                break;  
              case "Create":  
                model.Create();  
                Status = "DataTable created!";  
                break;  
              default:  
                break;  
            }  
          }  
          catch (Exception ex) { Status = ex.Message; }  
          finally { IsWorking = false; }  
        }  
      
        private bool CanCmdExec(object obj) => !IsWorking;  
      
        public ICommand Edit { get => new RelayCommand(CmdEdit, CanCmdExec); }  
      
        private void CmdEdit(object obj)  
        {  
          IsWorking = true;  
          ViewModelDetail vm = new ViewModelDetail() { Row = (Data)obj };  
          Window007Detail wnd = new Window007Detail() { DataContext = vm };  
          if (wnd.ShowDialog().Value) model.SaveData(); else model.RejectData();  
          IsWorking = false;  
        }  
      
        public ICommand Delete { get => new RelayCommand(CmdCancel, CanCmdExec); }  
      
        private void CmdCancel(object obj)  
        {  
          IsWorking = true;  
          model.DeleteData((Data)obj);  
          IsWorking = false;  
        }  
      
        private bool _isWorking = false;  
        private bool IsWorking  
        {  
          get => this._isWorking;  
          set { this._isWorking = value; OnPropertyChanged(nameof(Cmd)); }  
        }  
      
        private string _status;  
        public string Status  
        {  
          get => this._status;  
          set { this._status = value; OnPropertyChanged(); }  
        }  
      
        public event PropertyChangedEventHandler PropertyChanged;  
        private void OnPropertyChanged([CallerMemberName] string propName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));  
      }  
      
      public class ViewModelDetail : ICommand  
      {  
        public Data Row { get; set; }  
        public Window Wnd;  
      
        public static readonly DependencyProperty AttPropProperty =  
            DependencyProperty.RegisterAttached("AttProp", typeof(bool), typeof(ViewModelDetail), new PropertyMetadata(false, OnPropChanged));  
        public static bool GetAttProp(DependencyObject obj) => (bool)obj.GetValue(AttPropProperty);  
        public static void SetAttProp(DependencyObject obj, bool par) => obj.SetValue(AttPropProperty, par);  
        private static void OnPropChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)  
        {  
          Window wnd = d as Window;  
          if (wnd == null) return;  
          wnd.Loaded += (s,e) => ((ViewModelDetail)(wnd.DataContext)).Wnd = wnd;  
        }  
      
        public event EventHandler CanExecuteChanged;  
        public bool CanExecute(object parameter) => true;  
        public void Execute(object parameter)  
        {  
          switch (parameter.ToString())  
          {  
            case "Save":  
              Wnd.DialogResult = true;  
              Wnd.Close();  
              break;  
            case "Cancel":  
              Wnd.DialogResult = false;  
              Wnd.Close();  
              break;  
            default:  
              break;  
          }  
        }  
      }  
      
      public class Model  
      {  
        public Model(string connectionString) => cnString = connectionString;  
        string cnString;  
      
        private DataTable dt = new DataTable();  
        string sqlSelect = "SELECT * FROM Table1";  
      
        ObservableCollection<Data> col = new ObservableCollection<Data>();  
      
        internal ObservableCollection<Data> GetData()  
        {  
          col.Clear();  
          foreach (DataRow row in dt.Rows) col.Add(new Data(row));  
          return col;  
        }  
      
        internal Data GetNewData() => new Data(dt.NewRow());  
      
        internal void AddData(Data d)  
        {  
          dt.Rows.Add(d.row);  
          SaveData();  
          col.Add(d);  
        }  
      
        internal void RejectData() => dt.RejectChanges();  
      
        internal void LoadData()  
        {  
          using (SqlConnection cn = new SqlConnection(cnString))  
          using (SqlDataAdapter da = new SqlDataAdapter(sqlSelect, cn))  
            da.Fill(dt);  
        }  
      
        internal void DeleteData(Data d)  
        {  
          d.row.Delete();  
          SaveData();  
          col.Remove(d);  
        }  
      
        internal void SaveData()  
        {  
          using (SqlConnection cn = new SqlConnection(cnString))  
          using (SqlDataAdapter da = new SqlDataAdapter(sqlSelect, cn) { AcceptChangesDuringUpdate = true })  
          {  
            da.RowUpdated += (s, e) =>  
            {  
              SqlCommand idCmd = new SqlCommand("SELECT @@IDENTITY", e.Command.Connection);  
              if (e.StatementType == StatementType.Insert)  
              {  
                var newID = idCmd.ExecuteScalar();  
                foreach (DataColumn col in e.Row.Table.Columns)  
                  if (col.AutoIncrement)  
                  {  
                    e.Row[col.ColumnName] = newID;  
                    break;  
                  }  
              }  
            };  
            SqlCommandBuilder cb = new SqlCommandBuilder(da);  
            da.Update(dt);  
          }  
        }  
      
        internal void Create()  
        {  
          using (SqlConnection cn = new SqlConnection(cnString))  
          {  
            cn.Open();  
            using (SqlCommand cmd = new SqlCommand("", cn))  
            {  
              // delete previous table in SQL Server 2016 and above  
              cmd.CommandText = "DROP TABLE IF EXISTS Table1;";  
              cmd.ExecuteNonQuery();  
              // Create Table  
              cmd.CommandText = "CREATE Table [Table1]([ID] int Identity, [Name] nvarchar(255), CONSTRAINT [PK_Table1] PRIMARY KEY ([ID]))";  
              cmd.ExecuteNonQuery();  
              // demo Data  
              cmd.CommandText = "INSERT Table1(Name) VALUES('Name 1');";  
              cmd.ExecuteNonQuery();  
            }  
          }  
        }  
      }  
      
      public class Data  
      {  
        public Data(DataRow datarow) => row = datarow;  
        public DataRow row;  
        public int ID { get => row.Field<int>("ID"); set { row["ID"] = value; } }  
        public string Name { get => row.Field<string>("Name"); set { row["Name"] = value; } }  
      }  
      
      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:

    165358-x.gif


  2. Peter Fleischer (former MVP) 19,321 Reputation points
    2022-01-16T17:47:13.437+00:00

    Hi Juliette,
    you can use separate buttons in Window for Edit and Delete selected row in DataGrid. Try following demo (Window008 with part of last demo).

    <Window x:Class="WpfApp1.Window008"  
            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:WpfApp008"  
            mc:Ignorable="d"  
            Title="Juliette-5582_220116" Height="450" Width="800">  
      <Window.Resources>  
        <local:ViewModel x:Key="vm"/>  
        <Style TargetType="{x:Type Button}">  
          <Setter Property="Margin" Value="5"/>  
          <Setter Property="Width" Value="75"/>  
        </Style>  
      </Window.Resources>  
      <Grid DataContext="{StaticResource vm}">  
        <Grid.RowDefinitions>  
          <RowDefinition/>  
          <RowDefinition Height="Auto"/>  
          <RowDefinition Height="Auto"/>  
        </Grid.RowDefinitions>  
        <DataGrid ItemsSource="{Binding View}" SelectedItem="{Binding SelectedItem}"/>  
        <StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Right">  
          <Button Content="Edit" Command="{Binding Edit}" Margin="5"/>  
          <Button Content="Delete" Command="{Binding Delete}" Margin="5"/>  
          <Button Content="Create DB" Command="{Binding Cmd}" CommandParameter="Create" Margin="5"/>  
          <Button Content="Load data" Command="{Binding Cmd}" CommandParameter="Load" Margin="5"/>  
          <Button Content="Add" Command="{Binding Cmd}" CommandParameter="Add" Margin="5"/>  
        </StackPanel>  
        <TextBlock Grid.Row="2" Text="{Binding Status}" TextWrapping="Wrap"/>  
      </Grid>  
    </Window>  
    

    and ViewModel:

    using System;  
    using System.ComponentModel;  
    using System.Runtime.CompilerServices;  
    using System.Windows;  
    using System.Windows.Data;  
    using System.Windows.Input;  
    using WpfApp007;  
    using WpfApp1;  
      
    namespace WpfApp008  
    {  
      public class ViewModel : INotifyPropertyChanged  
      {  
        Model model = new Model(WpfApp1.Properties.Settings.Default.cnSQL);  
        private CollectionViewSource cvs = new CollectionViewSource();  
        public ICollectionView View { get => cvs.View; }  
      
        public object SelectedItem { get; set; }  
      
        public ICommand Cmd { get => new RelayCommand(CmdExec, CanCmdExec); }  
      
        private void CmdExec(object obj)  
        {  
          try  
          {  
            IsWorking = true;  
            switch (obj.ToString())  
            {  
              case "Load":  
                model.LoadData();  
                cvs.Source = model.GetData();  
                OnPropertyChanged(nameof(View));  
                Status = "Data loaded!";  
                break;  
              case "Add":  
                ViewModelDetail vm = new ViewModelDetail() { Row = model.GetNewData() };  
                Window007Detail wnd = new Window007Detail() { DataContext = vm };  
                if (wnd.ShowDialog().Value) model.AddData(vm.Row);  
                Status = "Data added!";  
                break;  
              case "Create":  
                model.Create();  
                Status = "DataTable created!";  
                break;  
              default:  
                break;  
            }  
          }  
          catch (Exception ex) { Status = ex.Message; }  
          finally { IsWorking = false; }  
        }  
      
        private bool CanCmdExec(object obj) => !IsWorking;  
      
        public ICommand Edit { get => new RelayCommand(CmdEdit, CanCmdExec); }  
      
        private void CmdEdit(object obj)  
        {  
          Data d = SelectedItem as Data;  
          if (d == null) return;  
          IsWorking = true;  
          ViewModelDetail vm = new ViewModelDetail() { Row = d};  
          Window007Detail wnd = new Window007Detail() { DataContext = vm };  
          if (wnd.ShowDialog().Value) model.SaveData(); else model.RejectData();  
          IsWorking = false;  
        }  
      
        public ICommand Delete { get => new RelayCommand(CmdDelete, CanCmdExec); }  
      
        private void CmdDelete(object obj)  
        {  
          Data d = SelectedItem as Data;  
          if (d == null) return;  
          IsWorking = true;  
          model.DeleteData(d);  
          IsWorking = false;  
        }  
      
        private bool _isWorking = false;  
        private bool IsWorking  
        {  
          get => this._isWorking;  
          set { this._isWorking = value; OnPropertyChanged(nameof(Cmd)); }  
        }  
      
        private string _status;  
        public string Status  
        {  
          get => this._status;  
          set { this._status = value; OnPropertyChanged(); }  
        }  
      
        public event PropertyChangedEventHandler PropertyChanged;  
        private void OnPropertyChanged([CallerMemberName] string propName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));  
      }  
    }  
    

    Result:

    165425-x.gif


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.