Xamarin MVVM Pass Data to other ViewModels and shared ViewModel - xamarin

I have 2 questions.
First=> How can i pass value between 2 ViewModels ?
For example, I m adding data and showing it into the MainPAGE, and simultaneously I want to show the same data ( Observable Collection ) in the ChildPAGE too. Inside the ChildPAGE Xaml I assigned the BindingContext to ViewModel and I assigned listview’s data source to that Observable Collection .But I couldn’t make it work . I tried some examples, but I couldn’t manage it to work. If the data load in the ChildPAGE’s constructor then it works else doesn’t work. I thought , I would improve the performance with using one ObservableCollection, but I think, the mechanism in MVVM is different.
So how can I use one ObservableCollection in 2 pages.
Second => How can I pass data between ViewModels without using the constructor.
Example: I Have 2 Pages ( MainPage and ChidPage) and 2 ViewModels ( MainVM and ChildVM ) .
Situation => If I would pass data from MainPage to ChildPage , I would send data within the constructor .But I want to get data from Childpage to MainPage . So PopAsync doesn’t have a constructor. I also tried EventHandler but it doesn’t work.
Is the only solution is Messaging center? Or what do you advice for better performance? Also does the MessagingCenter reduces the performance because of high usage of RAM?
NOTE: ( I want to learn the mvvm architecture, so I don’t want to use other MVVM Frameworks. I want to get the idea of MVVM and C# comprehensively.)
Thanks in advance

You could create a model class that both view models depend on. The model itself would hold the observable collection, while the view models reference the observable collection. This is a good way to share data across various view models throughout the life of your app. Usually you will want to make the model a singleton to ensure that it really is the same data.
If you don't want to use messaging center, you can use an MVVM framework which comes with other benefits. I use Prism to simplify navigation and you can pass navigation parameters along.
Finally, and this is not the best option usually, you may maintain data in the App object. You can set values in the Properties collection. This is not advisable for complex objects.
EDIT
First you would have some data transfer object which the ObservableCollection would contain, unless you're just holding integers or something.
public class MyDTO
{
//fields for Data Transfer Object
}
DTO's are often among your models, but sometimes you need a composite class to hold DTO's and collections of DTO's
Here is a simple Model that would contain your ObservableCollection
public class MyCollectionModel
{
#region Singleton Pattern
private MyCollectionModel()
{
}
public static MyCollectionModel Instance { get; } = new MyCollectionModel();
#endregion
private ObservableCollection<MyDTO> _dtos;
public ObservableCollection<MyDTO> MyObservableCollection
{
get { return _dtos; }
set { _dtos = value; }
}
}
Note that this implements the Singleton pattern. It could even implment INotifyPropertyChanged as well.
Next your view models, imagine a MyVM1 and MyVM2, would reference the ObservableCollection in your Model with something like this.
public class MyVM1 : INotifyPropertyChanged // Do the same with MyVM2, which would be the binding context for view 2
{
private MyCollectionModel _model;
public MyVM1 ()
{
_model = MyCollectionModel.Instance;
}
private ObservableCollection<MyDTO> myVar;
public ObservableCollection<MyDTO> MyProperty //Bind to this in your View1
{
get
{
return _model.MyObservableCollection;
}
set
{
myVar = value;
NotifyPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
I'm leaving quite a few things out here, and don't really have time to build an entire project just to test. I hope this helps.

You normally use await navService.PushAsync(new MyPage2(dataToShare)); to pass data between pages/VMs. This involves using constructors. Here is a simple example of it: https://stackoverflow.com/a/47873920/1508398
Since you don't want to use constructors, you may load the data on your child page from your service. Alternatively, you may cache the data on the local storage (client-side) and simply load it from there in your child/main page.

Related

Use NavigateAsync in Prism.Forms with an already existant ViewModel

Scenario: I have view, view model and model for PickList and PickLine. The PickListViewModel contains an ObservableCollection<PickLineViewModel> and the PickList model contains a List<PickLine>. My PickList page contains a ListView which is bound to the ObservableCollection<PickLineViewModel> and if a line is tapped NavigateAsync is called to navigate to the tapped PickLine.
Normally, when I call NavigateAsync Prism navigates to the page, locates the view model, creates an instance of it and binds this instance to the view. But in this case the view model instance that should be bound to the page is already existant (as an element of my ObservableCollection) and I don't want the Prism ViewModelLocator to create a new instance, due to the fact that it then would have to get data from web service and I try to keep the number of web service calls as low as possible.
Also I can't use models in the ObservableCollection because the view model contains properties which are only used for UI purposes so these properties should definitely not be part of the model, but the UI properties I'm talking about are needed in the PickList page and in the PickLine page.
Tl;dr: Is there any way in Prism.Forms to provide a view model instance
on navigating to a page that will than be bound to it?
Your problem is that you are confusing what a Model is and what a ViewModel is. In this case what you should have is:
PickLine (Model)
PickLineView (View)
PickLineViewModel (ViewModel)
PickLineListView (View)
PickLineListViewModel (ViewModel)
Containing your ObservableCollection of PickLine not PickLineViewModel
Without seeing your precise code, I'm going to take a guess from experience here that your code probably looks something like the following in principal:
public ObservableCollection<PickLineViewModel> Items { get; set; }
public DelegateCommand<PickLineViewModel> ItemTappedCommand { get; }
public void OnNavigatedTo(INavigationAware parameters)
{
var picks = _dataService.GetPickLines();
Items = new ObservableCollection<PickLineViewModel>(
picks.Select(x => new PickLineViewModel
{
Model = x,
ItemTappedCommand = ItemTappedCommand
});
}
I see code like the above a lot from people who confuse the difference between a Model and ViewModel, and often from people who don't understand how to properly bind back to the ViewModel from something like a Cell in a ListView.
What you should have instead is code that is more like:
public ObservableCollection<PickLine> Items { get; set; }
public DelegateCommand<PickLine> ItemTappedCommand { get; }
public void OnNavigatedTo(INavigationAware parameters)
{
var picks = _dataService.GetPickLines();
Items = new ObservableCollection<PickLine>(picks);
}
Is there any way in Prism.Forms to provide a view model instance on navigating to a page that will than be bound to it?
No, as far as I know, Prism Forms does not provide view model-first navigation (as opposed to Prism on WPF which does).
You should be able to work around this, though, by passing your existing view model as parameter and making the ViewModelLocator-created view model an "empty shell" that redirects everything to the actual view model.

MvvmCross passing data from One viewmodel to another viewmodel

I am working on Xamarin Android Application.I am using MvvmCross pattern for ViewModels.
Now,I want to pass data from one ViewModel to another viewmodel but don't want to navigate to that ViewModel. Instead of navigating to that ViewModel I want to navigate to another ViewModel.
e.g: I have three ViewModels V1,V2 and V3.Now I want to pass data from V1 to V2 but want to navigate to V3.
Is that possible ?
Look into the MvvmCross Messenger to do this: https://github.com/MvvmCross/MvvmCross-Plugins/tree/master/Messenger
You need to subscribe for something on your viewmodel:
public class LocationViewModel
: MvxViewModel
{
private readonly MvxSubscriptionToken _token;
public LocationViewModel(IMvxMessenger messenger)
{
_token = messenger.Subscribe<LocationMessage>(OnLocationMessage);
}
private void OnLocationMessage(LocationMessage locationMessage)
{
Lat = locationMessage.Lat;
Lng = locationMessage.Lng;
}
// remainder of ViewModel
}
An alternative to the mentioned messenger plugin I would suggest to save the data you need to share in a "service", being the service a singleton class managed by MvvmCross:
CreatableTypes()
.EndingWith("Service")
.AsInterfaces()
.RegisterAsLazySingleton();
In your view models you can use that singleton just by adding it to the constructor:
public WhateverViewModel(IService service)
The service will be singleton so your data will persists over the application live cycle.
So, in one of your view models you could do:
service.SharedData = new SharedData();
In another view model:
this.data = service.SharedData
The easiest way to do this is using the Messenger Plugin from MvvmCross.You can subscribe a certain kind of message in V2 and publish that message in V1 and then navigate to V3 in a seperate step.
// subscribing to a certain message type
this.logoutToken = this.messenger.Subscribe<LogoutMessage>(this.HandleLogoutMessage);
// Creating and sending a message
var logoutMessage = new LogoutMessage(this, "You have been logged out.");
this.messenger.Publish(logoutMessage);
Note: It is important to assign the MessageToken to a member variable (like in the other answer), because otherwise it will get cleaned up by the garbage collector.

Model binding in controller when form is posted - why to use view model instead of class from domain model?

I'm still reasonably new to ASP.NET MVC 3. I have come across view models and their use for passing data from a controller to the view. In my recent question on model binding two experts suggested that I should use view models for model binding as well.
This is something I haven't come across before. But both guys have assured me that it is best practise. Could someone maybe shed some light on the reasons why view models are more suitable for model binding?
Here is an example situation: I have a simple class in my domain model.
public class TestParent
{
public int TestParentID { get; set; }
public string Name { get; set; }
public string Comment { get; set; }
}
And this is my controller:
public class TestController : Controller
{
private EFDbTestParentRepository testParentRepository = new EFDbTestParentRepository();
private EFDbTestChildRepository testChildRepository = new EFDbTestChildRepository();
public ActionResult ListParents()
{
return View(testParentRepository.TestParents);
}
public ViewResult EditParent(int testParentID)
{
return View(testParentRepository.TestParents.First(tp => tp.TestParentID == testParentID));
}
[HttpPost]
public ActionResult EditParent(TestParent testParent)
{
if (ModelState.IsValid)
{
testParentRepository.SaveTestParent(testParent);
TempData["message"] = string.Format("Changes to test parents have been saved: {0} (ID = {1})",
testParent.Name,
testParent.TestParentID);
return RedirectToAction("ListParents");
}
// something wrong with the data values
return View(testParent);
}
}
So in the third action method which gets invoked when an HTTP POST arrives I used TestParent for model binding. This felt quite convenient because the browser page that generates the HTTP POST request contains input fields for all properties of TestParent. And I actually thought that's the way the templates that Visual Studio provides for CRUD operations work as well.
However the recommendation that I got was that the signature of the third action method should read public ActionResult EditParent(TestParentViewModel viewModel).
It sounds appealing at first, but as your models and view actions get increasingly complex, you start to see the value of using ViewModels for (most) everything, especially input scenarios.
Case 1 - Most web frameworks are susceptible to over-posting. If you are binding straight to your domain model, it is very possible to over-post data and maliciously change something not belonging to the user. I find it cleaner to bind to an input view model than have long string lists of white lists or black lists, although there are some other interesting ways with binding to an interface.
Case 2 - As your input grows in complexity, you'll run into times when you need to submit and validate fields not directly in the domain model ('I Agree' checkboxes, etc)
Case 3 - More of a personal thing, but I find model binding to relational domain objects to be a giant pain at times. Easier to link them up in AutoMapper than deal with MVC's modelbinder for complicated object graphs. MVC's html helpers also work more smoothly against primitive types than deep relational models.
The negatives of using ViewModels is that it isn't very DRY.
So the moral of the story is, binding to domain models can be a viable solution for simple things, but as the complexity increases, it becomes easier to have a separate view model and then map between the two.

ASP.net MVC - Model binding excludes class fields?

In a recent project - i've hit an unexpected snag.
A class with simple public fields (note not properties) doesn't seem to want to play nice with the
ASP.net MVC 3.0 model binder.
Is this by design?
besides changing the fields to properties - any options here?
update
The reason for the simple fields (instead of properties) is because I'm working with a shared library between MVC and a Script Sharp project. Script sharp supports properties - but it becomes a mess with javascript in the view (using knockout.js)
So despite what i'd love (which is to just use properties) i'm using public fields in my dto classes.
I wanted to avoid having multiple definitions of the same classes. sigh
Is this by design?
Yes, only properties with public getter/setters work with the default model binder.
besides changing the fields to properties - any options here?
That's what you should do as you should be using view models anyway. And view models are specifically designed for the views. So changing the fields to properties is the correct way to do. Now if you don't follow good practices and try to use your domain models as input/output to actions then you will have to write a custom model binder that works with fields. It will be a lot of work though.
I know it's against the c# team's philosophy, but I think fields are clean in poco classes. Seems like less moving parts to me. Anyway,
Here's a model binder that will load fields. It's fairly trivial. Note that you can use a new object with Activator.CreateInsance or start with an existing object as a starting point. I was using Yes/No dropdowns for bool, you may have check boxes, in which case unfortunately you'll have to loop through fieldinfos and look for missing form inputs b/c html doesn't submit form items if it's a false checkbox.
~/Binders/FieldModelBinder.cs
using System;
using System.Reflection;
using System.Web.Mvc;
namespace MyGreatWebsite.Binders
{
public class FieldModelBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var obj = controllerContext.HttpContext.Session["CurrentObject"];// Activator.CreateInstance(bindingContext.ModelType);
var form = controllerContext.HttpContext.Request.Form;
foreach (string input in form)
{
FieldInfo fieldInfo = obj.GetType().GetField(input);
if (fieldInfo == null)
continue;
else if (fieldInfo.FieldType == typeof(bool))
fieldInfo.SetValue(obj, form[input] == "Yes");
else
fieldInfo.SetValue(obj, Convert.ChangeType(form[input], fieldInfo.FieldType));
}
return obj;
}
}
}
Startup.cs
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
ModelBinders.Binders.Add(typeof(FieldModelBinder), new FieldModelBinder());
}
}
Usage
[HttpPost]
public ActionResult MyGreatAction([ModelBinder(typeof(FieldModelBinder))] MyGreatProject.MyGreatNamespace.MyGreatType instance, string myGreatParameters)
{
DoSomethingGreatWithMyInstance();
return View();
}

Model View Presenter - Handling more complex UI controls

I'm investigating the Model View Presenter pattern. So far I like what I see, however pretty much all of the articles, webcasts, podcasts etc. I've seen, read or heard tend to deal with setting and retrieving simple types from textboxes and I'm struggling to understand how to deal with more complicated UI controls.
For example, imagine I have a CheckedListBox. In this CLB I want to display all available choices and the choices selected for a given instance (imagine a Friend class with a FavouriteIceCreamFlavours List). I can fill the list box easily, but how then would I set which are selected (say on a subsequent edit of this friend). Also, how would I then persist those changes back to the underlying Friend object?
Another candidate would be a TreeView. Suppose by right clicking a node in the TV I want the user to be able to delete that node - what's the best approach of communicating that action back to the Presenter?
Cheers,
Lenny.
(PS I'm developing in a C# 3.5/WinForms environment)
I'm only new to this MVP thing as well. But I'll have a go at what I would do. What I would do with the treeview is just handle the delete inside the view as it's just UI events, but if you some kind of database logic or something then you could do this.
I would have:
Presenter Interface:
Interface IPresenter
{
bool DeleteItem(string itemName);
}
View Class:
class View : IView
{
IPresenter presenter = new Presenter(this);
void DeleteButtonClick(//SomeEventArgs)
{
bool vaild = this.presentor.DeleteItem(//Get the selected item);
if (vaild)
{ //Delete the item from the tree view }
}
}
Presenter Class:
class Presenter : IPresenter
{
public bool DeleteItem(string itemName)
{
// Check for valid delete.
return true or false
}
}
Hopefully that should work.

Resources