1

I have 2 buttons, that both are to show a new viewmodel, but with different data passed to the viewmodel. The data is passed is not completely different, so they both inherit from the same base. Unfortunately, it seems like the data passed to the Init() method of the viewmodel loses its runtime type information on the way? Using a debugger (or code), the passed data is only recognised as the base-class - NOT the derived class that was actually constructed and passed to the ShowViewModel<>() method.

Is what im trying to do not possible?

public class BasePasser
{

}

public class PasserA : BasePasser
{

}

public class PasserB : BasePasser
{

}


public class ViewModelOne : MvxViewModel
{
    // ...
    private void DoSwitchViewModelA()
    {
        var tobepassed = new PasserA {
            // ...
            };

        ShowViewModel<ViewModelTwo>(tobepassed);
    }

    private void DoSwitchViewModelB()
    {
        var tobepassed = new PasserB {
            // ...
            };

        ShowViewModel<ViewModelTwo>(tobepassed);
    }
}

public class ViewModelTwo : MvxViewModel
{
    // ...
    public async Task Init(BasePasser passed)
    {
        if(passed is PasserA)
        {
            // ...
        }
        else if(passed is PasserB)
        {
            // ...
        }
        else
        {
            // Always called/hit
            throw new InvalidOperationException("Unknown data passed");
        }
    }
}
user1202032
  • 1,430
  • 3
  • 15
  • 36

1 Answers1

5

Showing a ViewModel in MvvmCross is actually a pretty complicated process.

When you call ShowViewModel(param) Mvx will serialize the param value into a name/value dictionary. At this point, the Type information for param is lost.

When the ViewModel is instantiated, it will eventually look for any Init methods defined on the ViewModel.

If you look in Cirrious.MvvmCross.ViewModels.MvxViewModelExtensions.CallBundleMethod() you'll see that it first looks for a method with a single parameter of type IMvxBundle. Then it looks for a single parameter that is not a simple type (like string, int, etc.)

If it finds this type, it assumes it's an object that has been serialized in the ShowViewModel() call. It then attempts to deserialize the value based on the Type of the parameter in the Init method, NOT the original type.

This is why you only see the base type, since Mvx doesn't have the type information for the initial call, and when deserializing, only sees the base type from the Init method.

The workaround is to just put all properties into a single class. Only set the ones that you need and create a discriminator property that specifies which "type" it is.

var param = new Passer { PasserType = "A", ParamA = "ValueA", ... etc. }

You also have the option of using named method arguments.

See the Init section from https://github.com/MvvmCross/MvvmCross/wiki/View-Model-Lifecycle

Kiliman
  • 18,460
  • 3
  • 39
  • 38
  • Excellent answer, thank you for the explanation. Link to the class you're referring to: https://github.com/MvvmCross/MvvmCross/blob/3.5/Cirrious/Cirrious.MvvmCross/ViewModels/MvxViewModelExtensions.cs – user1202032 May 16 '15 at 13:35
  • 1
    It is possible to replace the serialization with Json instead (this was originally how Mvx worked). If you did this and if you enabled Json.Net's to name all types, then you should be able to get the serialization to work. One way to do the Json serialization is shown in http://stackoverflow.com/questions/19058173/ – Stuart May 17 '15 at 04:56