Add Data to Datagrid Without Closing new View (WPF MVVM) CRUD
I recommend to pass a reference of the save command and the current modified item from the CaixasViewModel
to the CaixasDetalhesViewModel
. Then let the CaixasViewModel
modify its Caixas
collection, as it is the owner.
You should not show dialogs from the view model. Show it from code-behind (see example below).
The following example should give you a raw idea about the pattern: let the main view model create and initialize the dialog view model with a corresponding save command and the item to modify (add and edit). The dialog view model only exposes the item for modification to the user and executes the save command that it received from the main view model. The main view model knows how to handle the target item e.g., create new instance (create new dialog) or copy instance (edit dialog).
The main idea is to create a copy of the original item model that you want to edit. When the save button is clicked, the changes are committed by copying the new data back to the original model (edit a copy).
The example also makes use of the reusable RelayCommand
, to help to improve readability by reducing code complexity. You can find the original implementation at Microsoft Docs: Relaying Command Logic. Simply copy the short code to a class named RelayCommand
in your project and use it instead of implementing new command classes for each command.
CaixasViewModel.cs
class CaixasViewModel
{
public ObservableCollection<Caixa> Caixas { get; private set; }
// The "Save" command of the create dialog.
public ICommand AddCaixaCommand => new RelayCommand(ExecuteAddCaixa);
// The "Save" command of the edit dialog
public ICommand UpdateCaixaCommand
=> new RelayCommand(ExecuteUpdateCaixa, commandParameter => this.CaixaSelecionado != null);
public ICommand RemoveCaixaCommand => new RelayCommand(ExecuteRemoveCaixa);
// Clears the current EditSource by creating a new empty Caixa item
public ICommand NewCaixaCommand => new RelayCommand(ExecuteNewCaixa);
private CaixasDetalhesViewModel CurrentCaixasDetalhesViewModel { get; set; }
public CaixasDetalhesViewModel GetEditCaixaViewModel
{
var editSource = this.CaixaSelecionado.Clone();
this. CurrentCaixasDetalhesViewModel =
new EditCaixasDetalhesViewModel(
this.ReplaceCaixaCommand,
editSource);
return this.CurrentCaixasDetalhesViewModel;
}
public CaixasDetalhesViewModel GetCreateCaixaViewModel
{
var editSource = new Caixa();
this.CurrentCaixasDetalhesViewModel =
new CreateCaixasDetalhesViewModel(
this.AddCaixaCommand,
this.NewCaixaCommand,
editSource);
return this.CurrentCaixasDetalhesViewModel;
}
private void ExecuteAddCaixa(object commandParameter)
{
var createdCaixa = (Caixa)commandParameter;
this.Caixas.Add(createdCaixa);
// Create a new temp copy to avoid editing the original instance directly
var editableCreatedCaixa = createdCaixa.Clone();
this.CurrentCaixasDetalhesViewModel.EditSource = editableCreatedCaixa;
}
private void ExecuteNewCaixa(object commandParameter)
{
// Update the dialog with a new empty Caixa item
var emptyCaixa = new Caixa();
this.CurrentCaixasDetalhesViewModel.EditSource = emptyCaixa;
}
private void ExecuteUpdateCaixa(object commandParameter)
{
Caixa editedCaixa = (Caixa)commandParameter;
var originalCaixa = this.Caixas.First(caixa => caixa.Id == editedCaixa.Id);
// Copy data
originalCaixa.Posto = editedCaixa.Posto;
...
}
private void ExecuteRemoveCaixa(object commandParameter)
=> this.Caixas.Remove((Caixa)commandParameter);
}
CreateCaixasDetalhesViewModel.cs
class CreateCaixasDetalhesViewModel : INotifyPropertyChanged
{
public Caixa EditSource { get; set; }
public ICommand SaveCommand { get; }
public ICommand ClearEditSourceCommand { get; }
public CreateCaixasDetalhesViewModel(
ICommand saveCommand,
ICommand newCommand,
Caixa editSource)
{
this.SaveCommand = saveCommand;
this.ClearEditSourceCommand = newCommand;
this.EditSource = editSource;
}
}
EditCaixasDetalhesViewModel.cs
class EditCaixasDetalhesViewModel : INotifyPropertyChanged
{
public Caixa EditSource { get; set; }
public ICommand SaveCommand { get; }
public EditCaixasDetalhesViewModel(
ICommand saveCommand,
Caixa editSource)
{
this.SaveCommand = saveCommand;
this.EditSource = editSource;
}
}
AssistenciasCaixas.xaml
<!-- DataContext is the CaixasViewModel -->
<Window>
<StackPanel>
<DataGrid x:Name="dgCaixas" />
<Button Content="Open Edit Dialog" Click="OnEditDialogButtonClicked" />
<Button Content="Open Create New Dialog" Click="OnCreateDialogButtonClicked" />
</StackPanel>
</Window>
AssistenciasCaixas.xaml.cs
partial class AssistenciasCaixas : Window
{
private void OnEditDialogButtonClicked(object sender, EventArgs e)
{
var viewModel = this.DataContex as CaixasViewModel;
CaixasDetalhesViewModel caixaDetalhes = viewModel.GetEditCaixaViewModel();
var dc = new EditDetalhesCaixa(caixaDetalhes);
_ = dc.ShowDialog(); // Owner is automatically set on ShowDialog()
// The following code is now handled in the CaixasViewModel
//if (dc.DialogResult.HasValue && dc.DialogResult.Value)
//{
// viewModel.Caixas.Add(caixa);
// viewModel.CaixaSelecionado = caixa;
//}
}
private void OnCreateDialogButtonClicked(object sender, EventArgs e)
{
var viewModel = this.DataContex as CaixasViewModel;
CaixasDetalhesViewModel caixaDetalhes = viewModel.GetCreateCaixaViewModel();
var dc = new CreateDetalhesCaixa(caixaDetalhes);
_ = dc.ShowDialog(); // Owner is automatically set on ShowDialog()
// The following code is now handled in the CaixasViewModel
//if (dc.DialogResult.HasValue && dc.DialogResult.Value)
//{
// viewModel.Caixas.Add(caixa);
// viewModel.CaixaSelecionado = caixa;
//}
}
}
CreateCaixaDetalhes.xaml
<!-- DataContext is the CreateCaixasDetalhesViewModel -->
<Window>
<StackPanel>
<TextBox Text="{Binding EditSource.Motivo}" />
<Button Content="Save"
Command="{Binding SaveCommand}"
CommandParameter="{Binding EditSource}" />
<Button Content="New"
Command="{Binding ClearEditSourceCommand}" />
</StackPanel>
</Window>
EditCaixaDetalhes.xaml
<!-- DataContext is the EditCaixasDetalhesViewModel -->
<Window>
<StackPanel>
<TextBox Text="{Binding EditSource.Motivo}" />
<Button Content="Save"
Command="{Binding SaveCommand}"
CommandParameter="{Binding EditSource}" />
</StackPanel>
</Window>
Caixa.cs
public class Caixa : ICloneable
{
public int Id
{
get => _id;
set => SetField(ref _id, value);
}
...
// Assumes that the property Posto is not editable via dialog,
// otherwise clone Posto too (deep clone).
object ICloneable.Clone() => MemberwiseClone();
public Caixa Clone() => ((ICloneable)this).Clone() as Caixa;
}