Navigation should always be handled in the ViewModel.
You're on the right track with thinking that the perfect implementation of the MVVM design pattern would mean you could run your application entirely without Views, and you can't do that if your Views control your Navigation.
I usually have an ApplicationViewModel
, or ShellViewModel
, which handles the overall state of my application. This includes the CurrentPage
(which is a ViewModel) and the code for handling ChangePageEvents
. (It's often also used for other application-wide objects such as the CurrentUser, or ErrorMessages too)
So if any ViewModel, anywhere, broadcasts a ChangePageEvent(new SomePageViewModel)
, the ShellViewModel
will pickup that message and switch the CurrentPage
to whatever page was specified in the message.
I actually wrote a blog post about Navigation with MVVM if you're interested
Supposing you have a Product class, each product shouldn't know about other products in the list.
In your GUI, I suggest you use a ProductViewModel, responsible to validate all info. Therefore, there's no problem in making the ProductViewModel to have knowledge of other instances of the same type in its parent collection. After all validation succeeds, you could generate a collection of Product if necessary.
Here's some code trying to illustrate my suggestion:
Your data entity class
public class Product {
public int Id {get;set;}
public string Name {get;set;}
}
Your view model (responsible to validate everything)
//Represents the view model for the Product.
//This view model is responsible for validating information of the product.
//As part of this validation, it is necessary that this instance know
//details of the collection it belongs.
public class ProductViewModel : INotifyDataErrorInfo {
//This could be injected via constructor or property.
//Represents the collection of products, that requires validation
//See its definition below
private ProductViewModelCollection parentCollection;
public int Id {
get { return _id; }
set { if (ValidateId(value))) _id = value; }
}
public string Name {
get { return _name; }
set { if (ValidateName(value)) _name = value; }
}
//The methods below validate all info.
//Alternatively, you could implement the validation somewhere else,
//and inject the necessary validators into this viewModel instance.
private bool ValidateId(int id) {
//... validate the id...
foreach (ProductViewModel otherProduct in parentCollection) {
//...if necessary, perform additional validations comparing
//with other products in the parent collection
}
}
private bool ValidateName(string name) {
//... validate the name...
foreach (ProductViewModel otherProduct in parentCollection) {
//...if necessary, perform additional validations comparing
//with other products in the parent collection
}
}
//...implementation of INotifyDataErrorInfo...
}
Your collection of products, it is the source for the datagrid
public class ProductViewModelCollection : ObservableCollection<ProductViewModel>, IINotifyDataErrorInfo {
//...implementation of INotifyDataErrorInfo...
}
After this, you won't need to use built-in validation of UI components; you can implement everything in the view model, being able to unit test it accordingly. The knowledge across collection is implemented in the ViewModel (because your GUI requires it), not within your data entity.
UPDATE: Second approach (via injection):
//Represents the view model for the Product.
//This view model is responsible for validating information of the product.
//As part of this validation, it is necessary that this instance know
//details of the collection it belongs.
public class ProductViewModel : INotifyDataErrorInfo {
//Injected via constructor or property.
private IProductValidator validator;
//String properties, IF YOUR GUI ALLOWS user to type
//text in the fields
public string Id {
get { return _id; }
set { if (validator.ValidateId(value))) _id = value; }
}
public string Name {
get { return _name; }
set { if (validator.ValidateName(value)) _name = value; }
}
public Product GetProduct() {
//...generate Product object with valid info...
}
}
public class ProductValidator : IProductValidator {
//This could be injected via constructor or property.
//Represents the collection of products, that requires validation
//See its definition below
private ProductViewModelCollection parentCollection;
//The methods below validate all info.
//Alternatively, you could implement the validation somewhere else,
//and inject the necessary validators into this viewModel instance.
private bool ValidateId(string id) {
//... validate the id...
foreach (ProductViewModel otherProduct in parentCollection) {
//...if necessary, perform additional validations comparing
//with other products in the parent collection
}
}
private bool ValidateName(string name) {
//... validate the name...
foreach (ProductViewModel otherProduct in parentCollection) {
//...if necessary, perform additional validations comparing
//with other products in the parent collection
}
}
//...implementation of INotifyDataErrorInfo...
}
Best Answer
As you are dealing with location, you should probably use the Lifecycle library, main use case of this library is for your use-case only.
https://developer.android.com/topic/libraries/architecture/lifecycle
And for the Permissions I would ask in activity and use Observer pattern to send the location updates to the view model.