I am building a WPF browser application with MVVM pattern.
I have a first page (ConsultInvoice) with a dataGrid. When I double click on one of the row I want to navigate to another page (EditInvoice) passing the selected row in argument to my constructor.
I know if I want do things properly I should use a dependency injection, but I don't really see how to use it here.
How can I simply pass this constructor?
ConsultInvoiceViewModel
private Invoice _selected;
public Invoice Selected
{
get
{
return _selected;
}
set
{
_selected = value;
OnPropertyChanged("Selected");
}
}
private void Edit()
{
EditInvoiceViewModel editInvoice = new EditInvoiceViewModel(Selected);
/* doing something here*/
}
public ICommand EditCommand
{
get
{
return editCommand ?? (editCommand = new RelayCommand(p => this.Edit(), p => this.CanEdit()));
}
}
EditInvoiceViewModel
public class EditInvoiceViewModel : ViewModelBase
{
public Context ctx = new Context();
Invoice invoice;
PreInvoice preInvoice;
#region properties
private ObservableCollection<PreInvoice> collection;
public ObservableCollection<PreInvoice> Collection
{
get
{
return collection;
}
set
{
collection = value;
OnPropertyChanged("Collection");
}
}
#endregion
public EditInvoiceViewModel(Invoice inv)
{
/* do stuff*/
}
}
Basically you should avoid passing such parameters into the ViewModels constructor, as wiring it with Inversion of Control/Dependency Injection becomes a pain. While you can use Abstract Factory pattern to resolve objects with runtime parameters, it's imho not suitable for ViewModels.
Instead I always suggest using a form of navigation pattern, similar to how Microsoft's Patterns & Practices team has done with Prism. There you have an
INavigationAware
interface which your ViewModels can implement. It has 2 methods,NavigateTo
andNavigateFrom
.And there is a navigation service. The navigation service will switch the views and before switching calling
NavigateFrom
in the current ViewModel (if it implements it. One can use it to check if data is saved and if necessary cancel the navigation. After the new View has been loaded and the ViewModel assigned to it, callNavigateTo
in the newly navigated ViewModel.Here you'd pass the parameters required for the ViewModel, in your case
invoiceId
. Try avoid passing whole models or complex objects. Use theinvoiceid
to fetch the invoice data and to populate your editing ViewModel.A basinc implementation from my former answer (can be found here):
An example of the
INavigationAware
interface used in Prism can be found on the projects github repository.This makes it easy to pass parameter and
async
load your data (where there isn't any clean way to do this via constructor, as you can'tawait
anasync
operation inside the constructor without locking, and doing this kind of things in the constructor is very discouraged).