GabeFC
GabeFC

Reputation: 400

In MVVMCross, is it possible to close a viewmodel and pass values back to the previous viewmodel in the navigation stack?

Consider the following example. I have three view models, ViewModel_A, ViewModel_B, and ViewModel_Values.

I want to be able to navigate to ViewModel_Values from either ViewModel_A or ViewModel_B, select a value from ViewModel_Values, then return that value to the calling view model.

Is there a way of passing arguments to previous view models in the navigation stack so that I can simply call ViewModel_Values.Close(this), thereby ensuring that the ViewModels_Values is decoupled from any other view models and can be used with arbitrary "parent" view models?

Upvotes: 8

Views: 6146

Answers (4)

subwizzll
subwizzll

Reputation: 21

In my case of trying to navigate in this pattern:

//pseudo code
"ModelA" => "ModelB<List<MyObject>>" => "ModelC<MyObject>" 

OR

//pseudo code
"ModelA" => "ModelC<MyObject>" 

I used the following work around in my ViewDestroy() override of ModelB<List>:

    private bool destroyView = true;
    public bool DestroyView
    {
        get => destroyView;

        set
        {
            destroyView = value;
            RaisePropertyChanged(() => DestroyView);

        }

    }

    public override void ViewDestroy(bool viewFinishing)
    {
        viewFinishing = DestroyView;
        base.ViewDestroy(viewFinishing);
    }

    private async Task ModifySelectedObject()
    {
        DestroyView = false;
        MyObject obj = SelectedObject;
        MyObject modifiedObj = await _navigationService.Navigate<ModifySingleViewModel, MyObject, MyObject>(new MyObject());

        if (modifiedObj != null)
        {
            obj = modifiedObj;
        }
        else
        {
            await Application.Current.MainPage.DisplayAlert("", "No changes made.", "OK");
        }
        DestroyView = true;
    }

This keeps the original

"await _navigationService.Navigate<ModifyMultipleViewModel, List, List>(new MyObject);"

from ModelA open when navigating to ModelC from ModelB, but still allows the ViewDestroy Method to close otherwise.

Upvotes: 0

fmaccaroni
fmaccaroni

Reputation: 3916

MvvmCross 5 onwards

From MvvmCross 5 you can use the new IMvxNavigationService that allows you to have a much richer navigation. One of the new features is the possibility to await a value from another ViewModel after navigating to it and should be the approach to take after MvvmCross 5 instead of Messenger, e.g.:

public class ViewModel_A : MvxViewModel
{
    private readonly IMvxNavigationService _navigationService;
    public ViewModel_A(IMvxNavigationService navigation)
    {
        _navigationService = navigationService;
    }

    public override async Task Initialize()
    {
        //Do heavy work and data loading here
    }

    public async Task SomeMethod()
    {
        var result = await _navigationService.Navigate<ViewModel_Values, MyObject, MyReturnObject>(new MyObject());
        //Do something with the result MyReturnObject that you get back
    }
}

public class ViewModel_Values : MvxViewModel<MyObject, MyReturnObject>
{
    private readonly IMvxNavigationService _navigationService;
    public ViewModel_Values(IMvxNavigationService navigation)
    {
        _navigationService = navigationService;
    }

    public override void Prepare(MyObject parameter)
    {
        //Do anything before navigating to the view
        //Save the parameter to a property if you want to use it later
    }

    public override async Task Initialize()
    {
        //Do heavy work and data loading here
    }

    public async Task SomeMethodToClose()
    {
        // here you returned the value
        await _navigationService.Close(this, new MyReturnObject());
    }
}

More info here HIH

Upvotes: 11

WickedW
WickedW

Reputation: 2591

Installing & using the MvxMessenger plugin is a great way to decouple view model communication in MvvmCross -

In your case, you could set up a new message -

public class ValuesChangedMessage : MvxMessage
{      
    public ValuesChangedMessage(object sender, int valuea, string valueb)
        : base(sender)
    {
        Valuea = valuea;
        Valueb = valueb;        
    }

    public int Valuea { get; private set; }
    public string Valueb { get; private set; }
}

In ViewModel_Values, you would act on / publish your UX changes with -

_mvxMessenger.Publish<ValuesChangedMessage>(new ValuesChangedMessage(this, 1, "boo!"));

And in ViewModel_A, ViewModel_B you would subscribe and act on them (as your ViewModel A / B would be still in the navigation stack when you pushed ViewModel_Values from them, so they could receive the message) -

private MvxSubscriptionToken _messageToken;              

_messageToken = _mvxMessenger.Subscribe<ValuesChangedMessage>(async message =>
        {
            // use message.Valuea etc ..
        });

More infos here -

https://www.mvvmcross.com/documentation/plugins/messenger?scroll=644 https://www.youtube.com/watch?feature=player_embedded&v=HQdvrWWzkIk

Upvotes: 0

Edrian Dragneel
Edrian Dragneel

Reputation: 57

Use messaging center. Here is the sample code.

//for trigger
MessagingCenter.Send<object> (this, "Hi");

//put this where you want to receive your data
MessagingCenter.Subscribe<object> (this, "Hi", (sender) => {
    // do something whenever the "Hi" message is sent
});

Upvotes: 4

Related Questions