3

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

zleao
  • 445
  • 3
  • 12

1 Answers1

4

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();
}
Stuart
  • 66,722
  • 7
  • 114
  • 165
  • As usual, a very detailed answer :) Thank you for the help. In this case I'll just use the default value in the constructor and remove the parameterless one... In the future in more complex scenarios, if I can i'll use inheritance and/or ViewModel costumization. – zleao Jan 17 '13 at 09:55
  • I've just tried to use the constructor with default value in the EditConfigurationViewModel `public EditConfigurationViewModel(string id = null)`. When I try to navigate to this ViewModel, an exception is thrown: **Cirrious.MvvmCross.Exceptions.MvxException: Failed to load ViewModel** – zleao Jan 17 '13 at 10:57
  • I've figured out. There's an issue when trying to use constructors with default values. For some reason, the ViewModel load fails. I can have a look after I re-arrange my development environment to debug PCL code... – zleao Jan 17 '13 at 11:13
  • If that fails, then just use Guid.Empty as a special id - it's just C# :) – Stuart Jan 17 '13 at 12:15