I have a WPF application which is written with an implementation of MVVM. There's no extra framework for the MVVM pattern.
My entities from EF db first are wrapped in their own viewmodels and I have a modelcontroller to load them into their viewmodels from a 'window' viewmodel.
Example of an entity viewmodel:
public class PurchaseOrderViewModel : ViewModels.ViewModelBase
{
private someType _prop;
public someType Prop
{
get
{
return _prop;
}
set
{
_prop = value;
OnPropertyChanged();
}
}
// ...
// Other Properties
// ...
public PurchaseOrderViewModel() {
// default constructor for LINQ
}
public PurchaseOrderViewModel(purchaseorder entity)
{
// load values from entity in properties
}
}
Example of a window viewmodel:
public class MainViewModel: ViewModels.ViewModelBase
{
private IModelController modelController = new ModelController();
private List<PurchaseOrderViewModel> _poList;
public List<PurchaseOrderViewModel> POList
{
get
{
return _poList;
}
set
{
_poList = value;
OnPropertyChanged();
}
}
// ...
// Other Properties
// ...
public MainViewModel()
{
POList = modelController.GetPurchaseOrders();
}
}
Example of ModelController:
public class ModelController : IModelController
{
public List<PurchaseOrderViewModel> GetPurchaseOrders()
{
using (var model = new DBContext())
{
return model.purchaseorders
.Select(new PurchaseOrderViewModel { /* assign properties */ })
.ToList();
}
}
}
Where am I supposed to save this wrapped viewmodel (PurchaseOrderViewModel) once the user is done editing it? As I see it, there are 2 options:
Create a save function in each viewmodel that points back to the modelController, but this feels like an inappropriate approach.
Create a save function in the modelcontroller and pass the viewmodel as an argument
It's most likely that I'm missing something in the MVVM pattern, but please point me in the right direction. Thank you!
EDIT: I excluded the view (MainView) from the info provided, but this view binds directly to the properties exposed by MainViewModel.
First up, I problably wouldn't name it ModelController as that's slightly confusing makes people think you are speaking MVC. Instead, if you call it xxxxService (e.g. PurchaseOrdersService) it makes more sense and it no longer feels "inappropriate" because having a VM delegate the actual work is what many users of IoC do. Plus it keeps your VM clean.
NOTE: By "service" I don't necessarily mean that your VM will be calling a WCF service directly (nor should you). Service is just a means to achieve something in an abstract and encapsulated way on behalf of clients. Examples include:
saving information to a DB
getting the current log mechanism
They can even be facades whereby they create a WCF client proxy and call a remote service on your behalf without you having to know the details.
So a typical flow is:
Command >> View code behind >> VM >> Service
The reason I include the view's code behind is that typically this is where you:
Catch exceptions
The starting point of async/await for asynchonous calls to your VM and service
Now when you pass context fromt the VM back to the service, there is no rule on what exactly you pass however I see no reason to pass VM to the service because that contains information the service doesn't care about.
Just pass the M which your VM should have bound to in the first place and continued to update via binding.
Related
I am new to MVVM pattern and Caliburn.Micro. I've read some tutorials on how to get started, but I'm confused about the Model part of MVVM in the context of Caliburn.
I want to create my first MVVM application and I have some design questions:
In tutorials, the Model was presented as simple property in
ViewModel. How should I manage more complex models? Is there any
naming convention? Obviously, there should be some external classes
made for my models, but how should I communicate between my models
and the view?
How should I keep references to many instances of one complex model?
For ex. cumtomers (instances of Customer model class)
Is there a possibility to manipulate one model class in many
ViewModels? How should I store my model reference, so it'll be
visible from different ViewModels?
Where should I put my code for more complex model manupulation/file,
database storage? How should I invoke such code? I'm not asking here
about SQLConnections, but MVVM best practices. :)
Thanks in advance for any help :)
EDIT:-------------------------------------------------------
Thank you for your anwser. I uderstand the topic more clearly, but I'm still confused about some details.
For an example, let's assume this little application. I have a form that allows me to add a new Customer. It has a few fields like Name, Surname etc.
After pressing the button, I invoke the addCustomer command in the ViewModel. I want my program to store the newly created customer inside the database.
My view also has the List control (whatever), which displays my customers as raw strings (like "Name: John, Surname: Doe, Address: ..." I know it's dumb to make it like this, but i need an example of model manipulation (like .toString()))
For this example, I've created a bunch of stuff to illustrate my vision of that process:
fields - it's a set of form fields like Name, Surname etc.
customerSet - it's a set of Customer class to store all created
customers
.addToDatabase(fields) - a method which puts newly created customer
to the database
.getStrings - a method which prepares a set of strings to be
displayed by the list in CustomerView
I think about 2 approaches that would be good for a solution:
First approach. I don't like this one. The only advantage is, that
ViewModel handles all the logic inside application. Sharing model
would be a serious problem here, because saving methods are bound to
the ViewModel class.
Second, MVC like approach. To me it's the most intuitive one. But - I
don't know where should I store CustomersModel object, so few
ViewModels could have access to it.
Which is the better one? Or maybe another approach that is more suitable for MVVM?
Another problem is: Where should I put my method that will load all the Customers from the database, so they could be displayes on the list? In "get method" inside viewmodel, or inside a model class?
In tutorials, the Model was presented as simple property in ViewModel.
How should I manage more complex models? Is there any naming
convention? Obviously, there should be some external classes made for
my models, but how should I communicate between my models and the
view?
Your models should represent whatever it is they need to whether it's a customer, account, etc. The view models job is to handle the interaction between the view and models.
How should I keep references to many instances of one complex model?
For ex. cumtomers (instances of Customer model class)
Generally, you will map complex models to more friendly format for display, you can do it manually or use a tool like AutoMapper.
Is there a possibility to manipulate one model class in many
ViewModels? How should I store my model reference, so it'll be visible
from different ViewModels?
If you're working with a local db you can pass IDs around. If it's a service you could persist the model locally for other view models to work with. You could also inject a singleton, ISharedData, into view models that need to work with shared data.
Where should I put my code for more complex model manupulation/file,
database storage? How should I invoke such code? I'm not asking here
about SQLConnections, but MVVM best practices. :)
Create services for more complex model manipulation / business logic. Inject the services into view models that require them. ICustomerService, IAccountService, etc.
EDIT:-------------------------------------------------------
You're first approach is correct. To your point about sharing the model being a serious problem because saving methods are bound to the view model class. The view model will have a SaveCustomerCommand that is fired when the button is clicked, because of its binding.
The SaveCustomerCommand will persist the CustomerModel, regardless of how the CustomerModel is persisted. So if its a database, the view model might have a reference to a context and issue a _db.Save(CustomerModel). If another view model needs to manipulate a CustomerModel, it will do so by using the context. The view model could also have a reference to a CustomerService that handles the crud for the CustomerModel.
Here's how this might look:
public class AddCustomerViewModel : Screen
{
private readonly ICustomerService _customerService;
public AddCustomerViewModel(ICustomerService customerService)
{
_customerService = customerService;
}
//If button is named x:Name="SaveCustomer" CM will
//bind it by convention to this method
public void SaveCustomer(Customer customer)
{
_customerService.Save(customer);
}
}
public class CustomerListViewModel : Screen
{
private readonly ICustomerService _customerService;
private List<CustomerDisplayModel> _customers;
public CustomerListViewModel(ICustomerService customerService)
{
_customerService = customerService;
}
public List<CustomerDisplayModel> Customers
{
get { return _customers; }
set
{
_customers = value;
NotifyOfPropertyChange();
}
}
//only fires once, unlike OnActivate()
protected override void OnInitialize()
{
var customers = _customerService.LoadAllCustomers();
//could just use the model but this shows how one might map from
//the domain model to a display model, AutoMapper could be used for this
Customers = customers.Select(c => new CustomerDisplayModel(c)).ToList();
}
}
public interface ICustomerService
{
List<Customer> LoadAllCustomers();
void Save(Customer customer);
}
//same as button, Label named x:Name="CustomerName" will bind
// to CustomerName
public class CustomerDisplayModel
{
private readonly Customer _customer;
public CustomerDisplayModel(Customer customer)
{
_customer = customer;
}
public string CustomerName
{
get { return _customer.Name; }
set { _customer.Name = value; }
}
public string Surname
{
get { return _customer.Surname; }
set { _customer.Surname = value; }
}
public string Address
{
get { return _customer.Address; }
set { _customer.Address = value; }
}
}
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public string Address { get; set; }
}
Whose responsibility is it to populate the values in an ASP MVC 5 architecture (C#, EF), for e.g. if we have PurchaseRecordsViewModel , PurchaseRecords Domain Model , PurchaseController
Does the code to populate data (time, cost etc) go it the viewmodel, right in its very own the viewmodel go in the PurchaseRecordsViewModel ?
Or, does the code go in the Action method of the PurchaseController
View models are typically just dumb collections of properties. Populating a view model typically rests inside of your service layer or, if you don't have one, your action method.
Think of the roles this way.
A domain model is a direct mapping to a database table.
A view model is a collection of properties needed to display a view.
A service layer gets/uses one or more domain models and populates a view model.
A service layer also can take a view model and create/update one or more domain models
A controller action method is the glue between the two. It calls a service layer to get (GET) a view model and passes it to a view. These action methods also take (POST) a view model and pass it to the service layer to do whatever needs to be done to it.
Another question typically asked is why can't I use domain models for a view? You can, but typically you run into things like, needing data from more than one domain model, not needing all the properties that are in the domain model and lastly, you now would have to worry about properties being updated on the domain model that you did not intend.
Expanding upon Tommy's answer, here is some code to go along with his description.
//Controller
public ActionResult Index()
{
List<OrderViewModel>() model = new List<OrderViewModel>();
model = new ServiceClass().GetOrders();
return View(model);
}
//here is your Service Class, this layer transfers the Domain Model into your ViewModel
public List<OrderViewModel> GetOrders()
{
List<OrderDomain> model = new List<OrderDomain>();
model = new DataAccess().GetOrders();
List<OrderViewModel> viewModel = new List<OrderViewModel>();
foreach (var order in model)
{
OrderViewModel vm = new OrderViewModel();
vm.OrderId = order.OrderId;
vm.OrderName = order.OrderName;
viewModel.Add(vm);
}
return viewModel;
}
//some DataAccess class, this class is used for database access
Public List<OrderDomain> GetOrders()
{
List<OrderDomain> model = new List<OrderDomain>();
using (var context = new MyEntities())
{
model = (from x in context.Order
select new OrderDomain
{
OrderId = x.OrderId,
OrderName = x.OrderName
}).ToList();
}
return model;
}
Edit:
This seems to be a mildly popular answer so I would like to mention I no longer follow this pattern. Instead I've been using mediatr and vertical slice architecture.
Ideally, PurchaseRecordViewModel should populate itself by getting PurchaseRecordsDomainModel. It should contain simple mapping of properties, and possibly some formatting of the output you're going to use in your view.
PurchaseRecordsViewModel
public class PurchaseRecordsViewModel
{
public IEnumerable<PurchaseRecordViewModel> PurchaseRecords {get;set;}
}
PurchaseRecordViewModel
public class PurchaseRecordViewModel
{
public DateTime Date {get;set;}
public decimal Cost {get;set;}
// .... some other properties
public PurchaseRecordsViewModel(PurchaseRecordsDomainModel domainModel)
{
Date = domainModel.Date;
Cost = domainModel.Cost;
// .... some other property mappings
}
}
What your action method on PurchaseController should do, is orchestrating the process of getting your PurchaseRecordsDomainModel, creation of PurchaseRecordsViewModel from PurchaseRecordsDomainModel and passing it to the View. Action method itself shouldn't contain any code that deals with connecting and retrieving data from database (in your case querying EF context), or any business logic. You should try to have loosely coupled modules, talking to each other via abstractions, this way you will ensure your application is maintainable, extensible and testable.
Also, try to draw clear separation between various layers of your system. For example, it is not a good idea to have EF entities as Domain Model Entites. You don't want your business logic layer to depend on data access layer, think of it this way, what if at some point of time in the future, you are moving away from EF and using some other ORM or even other technology to store and query data. You don't want to change business logic layer just because you're changing your data access layer. So to go from words to code in your case.
Considering that you already have your view and view model, I would create PurchaseRecordsService class in domain layer(please note depending in your case you might not use Repositories, but some other technique, this example is mainly to illustrate my point)
public class PurchaseRecordsService
{
private readonly IPurchaseRecordsRepository _purchaseRecordsRepository;
public PurchaseRecordsService(IPurchaseRecordsRepository purchaseRecordsRepository)
{
if(purchaseRecordsRepository == null)
{
throw new ArgumentNullException("purchaseRecordsRepository");
}
_purchaseRecordsRepository = purchaseRecordsRepository;
}
public IEnumerable<PurchaseRecordsDomainModel> GetPurchaseRecords()
{
// trivial case, real code can be more complex
return _purchaseRecordsRepository.GetPurchaseRecords();
}
}
Then in your domain layer, you could define IPurchaseRecordsRepository
public interface IPurchaseRecordsRepository
{
IEnumerable<PurchaseRecordsDomainModel > GetPurchaseRecords();
}
The idea is, our PurchaseRecordsService needs a way to communicate with databases, so whoever uses it, must supply implementation of IPurchaseRecordsRepository. Next step is to move to our data access layer and create implementation class of IPurchaseRecordsRepository.
public class EfPurchaseRecordsRepository: IPurchaseRecordsRepository
{
private readonly EfObjectContext _objectContext;
public EfPurchaseRecordsRepository(string connectionString)
{
_objectContext = new EfObjectContext(connectionString);
}
public IEnumerable<PurchaseRecordsDomainModel > GetPurchaseRecords()
{
var purchaseRecords = (from p in _objectContext.PurchaseRecords
....
select p).AsEnumerable();
return purchaseRecords .Select(p => p.ConvertToDomainPurchaseRecord());
}
}
And the last piece - we need to define our Action in PurchaseController
public class PurchaseController: Controller
{
private readonly IPurchaseRecordsRepository _repository;
public PurchaseController(IPurchaseRecordsRepository repository)
{
if(repository == null)
{
throw new ArgumentNullException("repository");
}
_repository = repository;
}
public ActionResult Index()
{
var purchaseRecordsService = new PurchaseRecordsService(_repository);
var purchaseRecordsViewModel = new PurchaseRecordsViewModel();
var purchaseRecords = purchaseRecordsService.GetPurchaseRecords();
foreach(var purchaseRecord in purchaseRecords)
{
var purchaseRecordViewModel = new PurchaseRecordViewModel(purchaseRecord);
purchaseRecordsViewModel.PurchaseRecords.Add(purchaseRecordViewModel);
}
return View(purchaseRecordsViewModel);
}
}
To recap, what we have is loosely coupled code, our Presentation and Data Access Layers don't know about each other, and they depend only on Domain layer. If you need, you can replace MVC front end with WPF for example, move from EF to another technology, your code is testable.
Ideally, your view model should be unaware of your domain model, so I'd say that you put your population logic in your controller, perhaps packed away in some sort of mapping/population utility class.
But remember, when it comes to questions about where to put certain logic, personal preference goes a long way.
Does anyone know how to view an existing IMvxViewModel?
In my app, I have already created a bunch of ViewModels (PhotoViewModel) inside of another view model. They exist as a property on the parent ViewModel (AlbumViewModel). It would be very nice to just show a particular instance of a PhotoViewModel instead of creating a new instance of that view model when I want to view it.
public class AlbumViewModel : MvxViewModel {
public ObservableCollection<PhotoViewModel> Photos
{
get { return GetValue(() => Photos); }
set { SetValue(value, () => Photos); }
}
}
public class PhotoViewModel : MvxViewModel { }
I was wondering if there was a way, other then creating my own IMvxViewModelLocator, to accomplish this task. I think having a protected method on the MvxNavigationObject called View could be really helpful both for new developers using the framework as well as performance. We'd be able to skip all of the reflection done currently to instantiate a view model.
The default ShowViewModel mechanism in MvvmCross uses page-based navigation - this navigation has to use Uris on WindowsPhone and Intents on Android.
Because of this, MvvmCross does not allow navigation by 'rich' objects - simple serialisable POCOs are Ok, but complicated 'rich' objects are not supported.
This is further essential because of 'tombstoning' - if your app/page/activity is later rehydrated then you cannot be sure of what historic View or ViewModel objects are actually in your history "back" stack.
If you want to navigate by rich object then the best way is to store those rich objects in a lookup service and to then navigate by some key/index into the lookup. However, I would personally call those lookedup objects Models rather than ViewModels (but the boundary does sometimes become blurred!)
Although based on MvvmCross v1 code, this question still gives quite a good background to this - What is the best way to pass objects to "navigated to" viewmodel in MVVMCross?
Some more up-to-date explanations include:
How to pass data across screens using mvvmcross
Custom types in Navigation parameters in v3
https://github.com/slodge/MvvmCross/wiki/ViewModel--to-ViewModel-navigation (under construction)
One final thing....
... the MvvmCross manifesto insists that MvvmCross is very open to customisation ...
Because of this you can override MvvmCross navigation and view model location if you want to. To do this, creating your own IMvxViewModelLocator would probably be a good way to start.
After some testing, below is a proposed solution. I'm not 100% in love with it, but it does work and provide the type developer experience I was looking for. So lets dig in.
To start, all of my ViewModels (VM) inherit from a base VM, AVM. This abstract base class supports looking up of an object as a public static method. It's a little gross, but it works well if you're willing to sip on the Kool-Aid. Below is the portion of the class that's relevant to this problem:
public abstract class AVM : MvxViewModel {
private static readonly Dictionary<Guid, WeakReference> ViewModelCache = new Dictionary<Guid, WeakReference>();
private static readonly string BUNDLE_PARAM_ID = #"AVM_ID";
private Guid AVM_ID = Guid.NewGuid();
private Type MyType;
protected AVM()
{
MyType = this.GetType();
ViewModelCache.Add(AVM_ID, new WeakReference(this));
}
public static bool TryLoadFromBundle(IMvxBundle bundle, out IMvxViewModel viewModel)
{
if (null != bundle && bundle.Data.ContainsKey(BUNDLE_PARAM_ID))
{
var id = Guid.Parse(bundle.Data[BUNDLE_PARAM_ID]);
viewModel = TryLoadFromCache(id);
return true;
}
viewModel = null;
return false;
}
private static IMvxViewModel TryLoadFromCache(Guid Id)
{
if (ViewModelCache.ContainsKey(Id))
{
try
{
var reference = ViewModelCache[Id];
if (reference.IsAlive)
return (IMvxViewModel)reference.Target;
}
catch (Exception exp) { Mvx.Trace(exp.Message); }
}
return null;
}
protected void View()
{
var param = new Dictionary<string, string>();
param.Add(BUNDLE_PARAM_ID, AVM_ID.ToString());
ShowViewModel(MyType, param);
}
In order to get this all wired up, you have to create a custom view model locator. Here's the custom locator:
public class AVMLocator : MvxDefaultViewModelLocator
{
public override bool TryLoad(Type viewModelType, IMvxBundle parameterValues, IMvxBundle savedState, out IMvxViewModel viewModel)
{
if (AVM.TryLoadFromBundle(parameterValues, out viewModel))
return true;
return base.TryLoad(viewModelType, parameterValues, savedState, out viewModel);
}
}
Lastly you have to wire up. To do so, go into your App.cs and override CreateDefaultViewModelLocator like so:
protected override IMvxViewModelLocator CreateDefaultViewModelLocator()
{
return new AVMLocator();
}
You're all set. Now in any of your derived ViewModels that are already alive and well, you can do the following:
myDerivedVM.View();
There's still some more I need to do (like making sure the WeakReferences do their job and I don't have memory leaks and some additional error handling), but at the very least it's the experience I was going for. The last thing I did was add the following command to the AVM base class:
public MvxCommand ViewCommand
{
get { return new MvxCommand(View); }
}
Now you can bind that command to any UI object and when invoked, it'll launch that view with that very instance of the VM.
Stuart, thanks for your help in steering me in the right direction. I'd be interested in hearing your feedback on the solution I provided. Thanks for all of your work with MVVMCross. It really is a very beautiful bit of code.
Cheers.
I've been working on an MVVM application in C# but consistiently run into some problems when working with the collections of ViewModels my View digests. Specifically, they all tend to relate to the issue of the Model being a private member of the ViewModel.
An example of this is creating new ViewModels (as requested by the View). For some preamble (although you might not need these to help me) here are example Model and ViewModel classes:
Private Class Model()
{
public string Name { get; set; }
}
Public Class ViewModel()
{
Private Model _Model;
Public Void ViewModel(Model model)
{
_Model = model;
}
Public String Name
{
get
{
return _Model.Name;
}
set
{
_Model.Name = value;
}
}
}
The entire model is never directly exposed as a public member of the ViewModel. The MainWindowViewModel handles collections of Models (private, the view cant see these) and ViewModels (public for View digestion):
Public Class MainWindowViewModel
{
Private List<Model> _NamesModel;
Private ObservableCollection<ViewModel> _NamesViewModel;
Public Void MainWindowViewModel()
{
//Lets pretend we have a service that returns a list of models
_NamesModel = Service.Request();
foreach(Model model in _NamesModel)
{
ViewModel viewmodel = new ViewModel(model);
_NamesViewModel.Add(viewmodel);
}
}
Public ObservableCollection<ViewModel> NamesViewModel
{
get
{
return _NamesViewModel;
}
}
}
Now thats the preamble but now I have a problem. How do I add a new ViewModel? Do methods within my view create a new ViewModel and populate that? Being a purist, I'm assuming the View should not be allowed to create or populate Models at all. Should my ViewModel contain a constructor that accepts nothing (i.e. no underlying model) and instead creates a blank to populate?
These kinds of issues keep coming up with a "pure" MVVM approach. I've had to create a public method in my ViewModel (bool compare(Model model)) that will compare a model (ready for deletion etc.) to it's internal one. If the models were publicly exposed (breaking purity) then it would be much easier to do stuff like find the ViewModel thats connected to a Model.
I can sympathize with some of those problems. I recently wrote an MVVM application where similar questions came up frequently. One of the tricks is to decide - definitively - which class is going to be responsible for Model instances. Do you want it to be your MainWindowViewModel? Or your NameViewModel? You don't want to share the responsibilities of creating/deleting the model between both of those classes; you'll have quite a logistical nightmare.
Secondly, even a "pure" MVVM approach doesn't dictate that you can't expose the model publicly. You said yourself that doing so would save you a lot of headache: DO IT. MVVM dictates only that the ViewModel has no knowledge/access of the View. There are many "official" MVVM examples that go so far as to implement their Model using the INotifyPropertyChanged interface, and bind directly to properties on the Model.
Personally, I think I would dictate control of the NameModel to the NameViewModel. This means that you should remove the list of NameModels completely from the MainWindowViewModel. If you want to give the NameViewModel an optional constructor which takes a Model, that would be fine too.
I'm a fan of this approach:
public NameViewModel : ViewModelBase
{
public NameModel Model
{
get { /* get stuff */ }
set { /* set stuff */ }
}
// Default constructor creates its own new NameModel
public NameViewModel()
{
this.Model = new NameModel();
}
// Constructor has a specific model dictated to it
public NameViewModel(NameModel model)
{
this.Model = model;
}
//Model wrapper properties
public String Name
{
get { return Model.Name; }
set { Model.Name = value; }
}
}
and...
public class MainWindowViewModel
{
Private ObservableCollection<ViewModel> _NameViewModels;
Public Void MainWindowViewModel()
{
//Lets pretend we have a service that returns a list of models
var nameModels = Service.Request();
foreach(Model model in nameModels)
{
ViewModel viewmodel = new NameViewModel(model);
NameViewModel.Add(viewmodel);
}
}
Public ObservableCollection<ViewModel> NameViewModels
{
get
{
return _NameViewModels;
}
}
}
In this way your MainWindowViewModeldoesn't keep an entirely separate copy of the Models; it only tracks the NameViewModels. Each NameViewModel is responsible for its own underlying model, while still making the option available to have a specific model passed to it during construction.
All the creation-related issues can be resolved with introduction of factory design pattern. The factory will take care of creating view models basing on model that was provided.
public class MainWindowViewModel
{
private List<Model> _NamesModel;
private ObservableCollection<ViewModel> _NamesViewModel;
private IViewModelFactory factory;
public void MainWindowViewModel(IViewModelFactory factory)
{
//Lets pretend we have a service that returns a list of models
_NamesModel = Service.Request();
_NamesViewModel = factory.CreateNamesViewModels(_NamesModel);
}
public ObservableCollection<ViewModel> NamesViewModel
{
get
{
return _NamesViewModel;
}
}
}
What is more, you could even get rid of Service dependency in view model and move it to the factory itself, thus reducing the need to keep model in view model (admittedly though, removal of model might not work in more complex scenarios):
public ObservableCollection<ViewModel> CreateNamesViewModels()
{
var models = Service.Request();
return new ObservableCollection(models.Select(m => new ViewModel(m)));
}
Also, your main window view model can expose commands that utilize factory to create any new instances. This way, no model is leaking to view and also no creation details are exposed (since commands will hide actual implementation).
I´ve got 2 ViewModels (ConfigurationViewModel and EditConfigurationViewModel). In the ConfigurationViewModel I've got the following code:
public ConfigurationViewModel()
{
NewConfigCommand = new MvxRelayCommand(DoNewConfig);
EditConfigCommand = new MvxRelayCommand<ConfigurationSet>(DoEditConfig);
}
private void DoNewConfig()
{
this.RequestNavigate<EditConfigurationViewModel>();
}
private void DoEditConfig(ConfigurationSet config)
{
this.RequestNavigate<EditConfigurationViewModel>(new { id = config.Id.ToString() });
}
In the EditConfigurationViewModel I've got the following code:
public EditConfigurationViewModel()
{
Configuration = new ConfigurationSet();
}
public EditConfigurationViewModel(string id)
{
Configuration = ConfigDataStore.GetConfiguration(Guid.Parse(id));
}
What I want to achieve is something very simple... In the ConfigurationViewModel when the NewConfigCommand is fired, I want to navigate to the EditConfigurationViewModel, and use the parameterless constructor. When the EditConfigCommand is fired I want to use the constructor that receives a string.
The problem with this code is that no matter what command is fired, the parameterless constructor is allways used and the code never reaches the other constructor.
I did some experiments, by removing the parameterless constructor, and the result was that the other constructor is called and I get the expected result for the EditConfigurationCommand, but if I try to fire the NewConfigurationCommand an exception is throw due too the inesxistence of a parameterless constructor (so far so good).
Unfortunately, at this moment I don't have VS2010 installed, so I'm not able to debug through PCL code... I've done some "eye debug" and found this class MvxViewModelLocator. I think the problem is somewhere here. Maybe in the DoLoad method when it tries to get the MethodInfo...
At this point I just wanted to know if I'm doing something wrong or if this is the expected result. Meanwhile I think I'll take a chance on installing VS2010 and pray that it won´t break anything...
On the PCL debugging issue, why not just add a Win8 or WP7/8 UI - then you can debug through the PCL code...
On the main question - about how to use multiple constructors... I'd suggest you don't.
For me, edit and new are two different views and two different viewmodels - they may share common properties and common layout - but this can be achieved using inheritance, using UserControls, using include axml, etc.
For an example of what I generally use for new and edit see https://github.com/slodge/MvvmCross/tree/vnext/Sample%20-%20CustomerManagement/CustomerManagement/CustomerManagement/ViewModels
If you do insist on carry on using one viewmodel, then you could consider using a 'magic value' for New - e.g. if Guid.Empty is passed then that means new?
Alternatively, you could just drop your parameterless constructor and could add a default value to the second one:
public EditConfigurationViewModel(string id = null)
{
Guid value;
if (id == null || !Guid.TryParse(id, out value))
{
Configuration = new ConfigurationSet();
}
else
{
Configuration = ConfigDataStore.GetConfiguration(value);
}
}
I think that would work?
Finally, if none of that seems suitable to you, then you could consider overriding the ViewModel construction mechanism.
To help with this, there's a fairly detailed recent post on how to write your own default ViewModelLocator for MvvmCross - see http://slodge.blogspot.co.uk/2013/01/navigating-between-viewmodels-by-more.html
Using this approach, you could create a much more custom navigation model - or if this is the only special view model, then I suspect you could create a default viewModelLocator like:
public class MyViewModelLocator
: MvxDefaultViewModelLocator
{
public override bool TryLoad(Type viewModelType, IDictionary<string, string> parameterValueLookup,
out IMvxViewModel model)
{
if (viewModelType == typeof(EditConfigurationViewModel))
{
string id;
if (parameterValueLookup.TryGetValue("id", out id))
{
model = new EditConfigurationViewModel(id);
}
else
{
model = new EditConfigurationViewModel();
}
return true;
}
return base.TryLoad(viewModelType, parameterValueLookup, IMvxViewModel model);
}
}
and register that locator in App.cs using:
protected override IMvxViewModelLocator CreateDefaultViewModelLocator()
{
return new MyViewModelLocator();
}