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: