zleao
zleao

Reputation: 445

MVVMCross Passing values to ViewModel that has 2 constructors

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...

Upvotes: 3

Views: 2487

Answers (1)

Stuart
Stuart

Reputation: 66882

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();
}

Upvotes: 4

Related Questions