Reputation: 400
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
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
Reputation: 3916
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
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
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