Passing data to mvvmcross viewmodel fragment

so I have activity with a tab with two fragments.

public class RecipeDetailActivity : BaseFragmentActivity<RecipeDetailViewModel> { protected override void OnCreate(Bundle savedInstanceState) { base.OnCreate(savedInstanceState); SetContentView(Resource.Layout.RecipeDetailView); AttachActionBar(); SupportActionBar.SetDisplayHomeAsUpEnabled(true); SupportActionBar.Title = "Recipe details"; var viewPager = FindViewById<ViewPager>(Resource.Id.main_view_pager); if (viewPager != null) { var fragments = new List<MvxViewPagerFragmentInfo>(); fragments.Add( new MvxViewPagerFragmentInfo("Ingrediente", typeof(RecipeFlavoursFragment), typeof(RecipeFlavoursViewModel))); fragments.Add( new MvxViewPagerFragmentInfo("Flavours", typeof(RecipeIngridientsFragment), typeof(RecipeIngridientsViewModel))); viewPager.Adapter = new MvxFragmentPagerAdapter(this, SupportFragmentManager, fragments); viewPager.Adapter = new MvxFragmentPagerAdapter(this, SupportFragmentManager, fragments); var tabLayout = FindViewById<TabLayout>(Resource.Id.main_tablayout); tabLayout.SetupWithViewPager(viewPager); } } } 

I am showing this page using the following code.

  private void SelectRecipe(RecipeModel recipe) { var recipeJson = JsonConvert.SerializeObject(recipe); ShowViewModel<RecipeDetailViewModel>(new { recipe = recipeJson }); } 

Now I would like to transfer some data to the child view models. RecipeFlavoursViewModel RecipeIngridientsViewModel

I have tried so far: Using the ValueObject parameter

  fragments.Add( new MvxViewPagerFragmentInfo("Ingrediente", typeof(RecipeFlavoursFragment), typeof(RecipeFlavoursViewModel), new { recipe = ViewModel.Recipe })); 

Using IMvxBundle

In RecipeDetailViewModel

  protected override void SaveStateToBundle(IMvxBundle bundle) { bundle.Data["Recipe"] = JsonConvert.SerializeObject(Recipe); base.SaveStateToBundle(bundle); } 

In RecipeIngridientsViewModel

 protected override void InitFromBundle(IMvxBundle parameters) { base.InitFromBundle(parameters); if (parameters.Data.Count != 0) { Recipe = JsonConvert.DeserializeObject<RecipeModel>(parameters.Data["recipe"]); } } 

None of them have worked so far. Any ideas what I'm doing wrong? Do I have to use the navigation service from MvvmCross 5 to be able to use InitFromBundle and SaveStateToBundle.

InitFromBundle , which is called every time my fragments are displayed, but the SaveStateToBundle of RecipeDetailViewModel is never called.

+6
source share
1 answer

To do this, you can use the MvxViewPagerFragmentPresentationAttribute so that the Presenter takes responsibility for displaying the fragments, and you just show the ViewModels by passing the Recipe parameter like any other, but at the moment there are some minor errors.

However, one way to solve this is to have the fragments you want to have in your ViewPager in your ViewPager and load them into Initialize so that you can then refer to them from RecipeDetailActivity :

Using Mvx 5, you can use the new Navigation to show ViewModels. If parts are opened from RecipeListViewModel , then:

 public class RecipeDetailViewModelArgs { public RecipeDetailViewModelArgs(RecipeModel recipe) { this.Recipe = recipe; } public RecipeModel Recipe { get; } } public class RecipeListViewModel : MvxViewModel { private readonly IMvxNavigationService navigationService; public RecipeListViewModel(IMvxNavigationService navigationService) { this.navigationService = navigationService; } private async Task SelectRecipe(RecipeModel recipe) { await this.navigationService.Navigate<RecipeDetailViewModel, RecipeDetailViewModelArgs>(new RecipeDetailViewModelArgs(recipe)); } } 

Then, in your ViewModel details, you simply cache the recipe, download the ViewModels files (ingredients and flavors) for the children, and set the recipe for them:

 public class RecipeDetailViewModel : MvxViewModel<RecipeDetailViewModelArgs> { private readonly IMvxViewModelLoader mvxViewModelLoader; private readonly IMvxJsonConverter jsonConverter; private RecipeModel recipe; public RecipeDetailViewModel(IMvxViewModelLoader mvxViewModelLoader, IMvxJsonConverter jsonConverter) { this.mvxViewModelLoader = mvxViewModelLoader; this.jsonConverter = jsonConverter; } public override void Prepare(RecipeDetailViewModelArgs parameter) { this.recipe = parameter.Recipe; } protected override void SaveStateToBundle(IMvxBundle bundle) { base.SaveStateToBundle(bundle); bundle.Data["RecipeKey"] = this.jsonConverter.SerializeObject(this.recipe); } protected override void ReloadFromBundle(IMvxBundle state) { base.ReloadFromBundle(state); this.recipe = this.jsonConverter.DeserializeObject<RecipeModel>(state.Data["RecipeKey"]); } public override async Task Initialize() { await base.Initialize(); this.InitializeChildrenViewModels(); } public RecipeFlavoursViewModel FlavoursViewModel { get; private set; } public RecipeIngridientsViewModel IngredientsViewModel { get; private set; } protected virtual void InitializeChildrenViewModels() { // Load each childre ViewModel and set the recipe this.FlavoursViewModel = this.mvxViewModelLoader.LoadViewModel(new MvxViewModelRequest<RecipeFlavoursViewModel>(null, null), null); this.FlavoursViewModel.Recipe = this.recipe; this.IngredientsViewModel = this.mvxViewModelLoader.LoadViewModel(new MvxViewModelRequest<RecipeIngridientsViewModel>(null, null), null); this.FlavoursViewModel.Recipe = this.recipe; } } 

Then, when you load the ViewPager , you can use another constructor MvxViewPagerFragmentInfo => public MvxViewPagerFragmentInfo (string title, string tag, Type fragmentType, IMvxViewModel viewModel, object parameterValuesObject = null) so that you can preload previously loaded ViewModels models:

 this.viewPager = view.FindViewById<ViewPager>(Resource.Id.viewPagerDetails); if (viewPager != null) { var fragments = new List<MvxViewPagerFragmentInfo>(); fragments.Add(new MvxViewPagerFragmentInfo("Ingredients", "RecipeIngridientsViewModelTag", typeof(RecipeIngridientsView), this.ViewModel.IngridientsViewModel)); fragments.Add(new MvxViewPagerFragmentInfo("Flavours", "RecipeFlavoursViewModelTag", typeof(RecipeFlavoursView), this.ViewModel.FlavoursViewModel)); this.viewPager.Adapter = new MvxFragmentPagerAdapter(this.Activity, this.ChildFragmentManager, fragments); } 

What is it.


By the way, if you do not want to use navigation or you do not use Mvx 5.x, then you simply initialize the child ViewModels in the void Start() method.

And in order to conclude that you want to change the values ​​of your Recipe in children, one simple way is to initialize Singleton with Recipe , and then just enter Singleton in the constructors so that you always have the link in the same Recipe, and you don’t need to pass the Recipe object back and forth to these ViewModels and merge the changes made from each of them. Additional Information at MvvmCross: Access Models by Link

E.I.V.

+4
source

Source: https://habr.com/ru/post/1275343/


All Articles