Passing on variables from ViewModel to another View (MVVMCross)

David picture David · Apr 17, 2012 · Viewed 9.6k times · Source

For the past couple of weeks I've been working on developing a cross platform app (IOS/Android/WP7) using the MVVMCross framework. Today I ran into a problem I don't really know how to solve, so hopefully you can push me in the right direction.

In the IOS I have the following construction for navigating to another page (the code below is located in a ViewModel):

KeyValuePair<string,string> kvpAct1 = new KeyValuePair<string, string>("short", ".countertest5");

public IMvxCommand BeckhoffActuator1
{
    get
    {           
        return new MvxRelayCommand<Type>((type) => this.RequestNavigate<Beckhoff.BeckhoffActuatorViewModel>(kvpAct1));
    }
}

When this IMvxCommand is fired (button pressed) the next View is loaded, in this case the BeckhoffActuatorViewModel. In the code of the BeckhoffActuatorView I use the keyvaluepair from above:

public class BeckhoffActuatorView : MvxTouchDialogViewController<BeckhoffActuatorViewModel>
{

    ICollection<string> icol;

    public BeckhoffActuatorView(MvxShowViewModelRequest request) : base(request, UITableViewStyle.Grouped, null, true)
    {

        icol = request.ParameterValues.Values;

    }

    public override void ViewDidLoad()
    {
        //Code
    }
}

This construction is working fine in IOS, but I would like to use the same construction in my android App.

The code in the ViewModel hasn't changed since that's the whole idea of MVVM. But the code of the BackhoffActuatorView is different for Android:

public class BeckhoffActuatorView : MvxBindingActivityView<BeckhoffSensorViewModel>
{
    public ICollection<string> icol;

    public BeckhoffActuatorView()
    {
        Debug.WriteLine("Standard");
    }

    public BeckhoffActuatorView(MvxShowViewModelRequest request)
    {
        Debug.WriteLine("Custom");

        icol = request.ParameterValues.Values;
    }

    protected override void OnViewModelSet()
    {

        SetContentView(Resource.Layout.BeckhoffActuatorView);

    }
}

The code above isn't working, the MvxBindingActivityView doesn't seem to implement something similar to the ViewController I use in IOS. The code only come in the standard constructor, and when I leave that one out completely it won't compile/run.

Does anyone know know I can access the keyvaluepair I send with the RequestNavigate? Thank you!

Answer

Stuart picture Stuart · Apr 17, 2012

MVVMCross is very convention based - and it works on the idea of passing messages between ViewModels wherever possible.

If you navigate to a ViewModel using:

KeyValuePair<string,string> kvpAct1 = new KeyValuePair<string, string>("short", ".countertest5");

public IMvxCommand BeckhoffActuator1
{
    get
    {           
        return new MvxRelayCommand<Type>((type) => this.RequestNavigate<Beckhoff.BeckhoffActuatorViewModel>(kvpAct1));
    }
}

then you should be able to pick that up in the BeckhoffActuatorViewModel using the constructor:

public class BeckhoffActuatorViewModel : MvxViewModel
{
    public BeckhoffActuatorViewModel(string short)
    {
        ShortValue = short;
    }

    private string _shortValue;
    public string ShortValue
    {
        get
        {
            return _shortValue;
        }
        set
        {
            _shortValue = value;
            FirePropertyChanged("ShortValue");
        }
    }
}

And your views can then access ViewModel.ShortValue (for iOS this can be done after base.ViewDidLoad(), for Android after OnCreate() and for WP7 after OnNavigatedTo)

For an example of this, take a look at the TwitterSearch example:

This has a HomeViewModel which calls navigate using:

    private void DoSearch()
    {
        RequestNavigate<TwitterViewModel>(new { searchTerm = SearchText });
    }

and a TwitterViewModel which receives the searchTerm using the constructor:

    public TwitterViewModel(string searchTerm)
    {
        StartSearch(searchTerm);
    }

Please note that only strings are allowed in this message passing at present - but you can always serialise your own objects using JSON.Net - or you can extend the framework - it's open source.

Please note that only strings, ints, doubles and bools are allowed in this constructor parameter passing at present - this is due to serialisation requirements for Xaml Urls and for Android Intents. If you want to experiment with navigation using your own custom serialised objects, then please see http://slodge.blogspot.co.uk/2013/01/navigating-between-viewmodels-by-more.html.

Also, note that if you want to use the anonymous object navigation (RequestNavigate<TwitterViewModel>(new { searchTerm = SearchText });) then you will need to make sure that an InternalsVisibleTo attribute is set - see https://github.com/slodge/MvvmCrossTwitterSearch/blob/master/TwitterSearch.Core/Properties/AssemblyInfo.cs:

[assembly: InternalsVisibleTo("Cirrious.MvvmCross")]

Further... not for the faint-hearted... and this isn't "good mvvm code"... but if you really want/need to access the MvxShowViewModelRequest data inside an Android activity, then you can extract it from the incoming Intent - there's an Extras string containing the request (see the deserialisation in CreateViewModelFromIntent in https://github.com/slodge/MvvmCross/blob/master/Cirrious/Cirrious.MvvmCross/Android/Views/MvxAndroidViewsContainer.cs)