Pass diffrent viewmodel with other viewmodel to show page - c#

I have question. I use to use Send to pass the viewmodel to show other page as shown below:
public class UsersViewModel : IUsersViewModel
{
void ShowCars()
{
MessagingCenter.Send<IUsersViewModel>(this, "ShowCarsViewPage");
}
}
As you see above, i use this. Now i have situation that i need to pass diffrent viewmoodel inside UsersViewModel. I want to add ShowBuildings inside UsersViewModel. The problem is as it's diffrent viewmodel to be passed i cannot use this which leds me to use new keyword and pass all dependencies. How can i overcome that?
void ShowBuildings()
{
MessagingCenter.Send<IBuildingsViewModel>(new Buildings(new DataStorage()), "ShowBuildingsViewPage");
}
My first thought is to pass that view model i need to use into UsersViewModel's ctor but not sure if this is right way like to insert another view model into other view model?:
public class UsersViewModel : IUsersViewModel
{
private readonly IBuildingsViewModel _buildingviewmodel;
UsersViewMode(IBuildingsViewModel buildingviewmodel)
{
_buildingviewmodel = buildingviewmodel;
}
//So then:
void ShowBuildings()
{
MessagingCenter.Send<IBuildingsViewModel>(_buildingviewmodel, "ShowBuildingsViewPage");
}
}

Check xamarin publisher documentation. The third parameter is the payload data that is being sent.
MessagingCenter.Send<MainPage, string>(this, "Hi", "John");
Tip: Try to bundle multiple ViewModels with an interface and pass that. In this way you will not be tied to specific ViewModels interfaces.
interface IBuildingCollection : IEnumerable<Building>
{
}
class ViewModel1 : IBuildingsViewModel, IBuildingCollection
{
}
class ViewModel2 : IBuildingsViewModel, IBuildingCollection
{
}
class UsersViewModel : IUsersViewModel
{
void ShowBuildings(IBuildingCollection collection)
{
MessagingCenter.Send<IUsersViewModel, IBuildingCollection>(this, "ShowBuildingsViewPage", collection);
}
}
class ReceiverViewModel : IReceiverViewModel
{
public ReceiverViewModel()
{
MessagingCenter.Subscribe<IUsersViewModel, IBuildingCollection>(this, "ShowBuildingsViewPage", myDelegate);
}
public void myDelegate(IBuildingCollection buildings)
{
// Do something with buildings
}
}

Related

Generic class constraint where <T> is a type constraining the generic class

Perhaps not the most accurate title, but it's a little difficult to describe; perhaps you guys can help me out here? I'm writing a game using the MVC format, and I want each base class (controller, model, and view) to have a reference to their accompanying features, forming a sort of triangle (ie. A model has a reference to a controller that defines it, and a view that references it, etc. ) Much of these classes look like this:
public class Model {
public Controller controller;
public View view;
public void Connect (Controller controller, View view) {
this.controller = controller;
this.view = view;
}
}
This is okay, but whenever I intend to pull up a ChildModel's controller, I'll need to cast to the appropriate ChildController to obtain the correct version. I could make a utility method/getter to fetch an appropriately cast item, but I'd rather not rewrite this piece of code for each and every child class. I thought I could solve this issue by making the base classes generic, but now I'm running into an issue where the newly generic classes need references to the class that's trying to define them, hence:
public class Model<V, C> where V : View<?, C> where C : Controller<?, V> {
public Controller<?, V> controller;
public View<?, C> view;
public void Connect (Controller<?, V> controller, View<?, C> view) {
this.controller = controller;
this.view = view;
}
}
As you can see, this quickly gets messy in the base class. I don't know what symbol to place for (in reference to the example above) the Model that's attempting to define the constraints. Placing 'Model' into the question marks doesn't seem to compile either, as I run into a hellish boxing conversion issue.
Is there a way to accomplish what I'm after, or am I just trying to be too clever here? If this could work, I'd love to be able to declare child classes with the type constrained to their 'triangle', thus I could avoid needless casting or helper methods:
public class ChildModel<ChildView, ChildController> {
public ChildModel () {
this.controller <- calls ChildController type, not base type!
}
}
Anyone have any ideas?
It looks like you are confusing ownership with interactions. Ownership implies that one owns the other, while interactions imply how they communicate with one another. MVC primarily defines interactions between the three participants, though you could say that a view and controller both own a model.
In your code as shown, a class owns a property, therefore a controller class owns a view and a view owns a controller.
var model = new Model();
var view = new View<Controller<Model, View<Controller, Model>, ...
This doesn't work with generics in the way you would like because the interactions become circular. It is the chicken and the egg problem: chickens come from eggs which are laid by chickens. We can solve most of the problem by giving the controller ownership of the view, and both the controller and view ownership of a model.
public class Model
{
}
public interface IView<M>
{
M Model { get; }
}
public class MyView : IView<Model>
{
public MyView(Model model)
{
Model = model;
}
public Model Model
{
get;
}
}
public interface IController<V, M>
{
M Model { get; }
V View { get; }
}
public class MyController : IController<MyView, Model>
{
public MyController(MyView view, Model model)
{
View = view;
Model = model;
}
public Model Model
{
get;
}
public MyView View
{
get;
}
}
We still used generics to do this, and you have easy access to most of the information so far without introducing circular references.
class Program
{
public static void Main()
{
var model = new Model();
var view = new MyView(model);
var controller = new MyController(view, model);
}
}
Now if you want to make sure the view has a reference to the controller, you can do this via a property.
view.Controller = controller;
You could disregard everything I just showed you - and go the property injection route. This means instead of taking in the dependencies by the constructor, which creates circular reference restrictions on how the objects can be created, you can simply do this.
var model = new Model();
var view = new View();
var controller = new Controller();
model.View = view;
model.Controller = controller;
view.Controller = controller;
view.Model = model;
controller.View = view;
controller.Model = model;
Whatever method you use, the trick is to avoid the circular dependency issue that you have in your current code. Most MVC frameworks provide rich data binding which breaks the direct coupling between the classes, but if you don't have that, you have to either write something or find something, or work within the confinements of the language rules.
There are a lot of ways to solve this. As I wrote this there was another answer posted so you should also look at that.
Here's my suggestion.
1. You should use the Controller as the main part of your MVC pattern. The controller should get the information from the Mode, process it and then call the view.
Here's my base class for the Controller
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Inheritance.Classes
{
public class Controller<T, U> where T : Model, new() where U : View, new()
{
protected T _model;
protected U _view;
public Controller()
{
this._model = new T();
this._view = new U();
}
public Controller(T model, U view)
{
this._model = model;
this._view = view;
}
public string ParentFunction()
{
return "I'm the parent";
}
}
}
Note, I have also a Model and View base class. Since they are empty for the moment, I won't show you the code
Then, I can define my child classes. For example, I will make a PageController, PageModel and PageView. They will all inherite from their BaseClass.
Note : Once again, PageModel and PageView are empty. They are only used for the inheritance
PageController.cs
using Inheritance.Page;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Inheritance.Classes
{
public class PageController : Controller<PageModel, PageView>
{
public PageController():base()
{
}
public PageModel Model
{
get
{
return base._model;
}
}
}
}
So as you can see, you will specify the Model class and the View class only inside the PageController.
To use your classes, you can do as follow :
PageController controller = new PageController();
//We can access the parent function
Console.WriteLine(controller.ParentFunction());
//Function defined into the controller.
PageModel model = controller.Model;
I think this is what you want:
public class GameModel : Model
{
public int ID { get; set; }
}
public class GameView : View<GameModel, GameView>
{
public float FOV { get; set; }
}
public class GameController : GameView.BaseControler
{
// Set ID
public GameController()
{
Model.ID=100;
View.FOV=45f;
}
}
class Program
{
static void Main(string[] args)
{
var gm = new GameModel();
var view = new GameView();
var ctrl = new GameController();
view.Connect(gm, ctrl);
Debug.WriteLine(view.Model.ID);
}
}
public class Model
{
}
public class View<TModel,TView> where TModel : Model where TView : View<TModel, TView>
{
public TModel Model { get; private set; }
public BaseControler Controler { get; private set; }
public void Connect(TModel model, BaseControler controler)
{
this.Model=model;
this.Controler=controler;
this.Controler.Connect(model, this as TView);
}
public class BaseControler
{
public TView View { get; private set; }
public TModel Model { get; private set; }
public void Connect(TModel model, TView view)
{
this.Model=model;
this.View=view;
}
}
}

Is there an iOS override for when a view was popped back?

I have a MasterDetail Application, and a NavButton to add a new item. Im am able to successfully pop the view back, however I need to reload the table nice that is done. So how exactly can my MasterViewController know when it was popped so it could reload?
Heres the Pop code:
NavigationController.PopViewController(true);
It functions as expected, I just then need MasterViewController to reload the table, which I made a method for:
public void ReloadTable()
{
TableView.ReloadData ();
}
Try this:
public partial class MasterViewController : UIViewController
{
public void ReloadTable()
{
TableView.ReloadData ();
}
}
public partial class DetailViewController : UIViewController
{
public override void ViewWillDisappear (bool animated)
{
var masterViewController = NavigationController.ViewControllers.OfType<MasterViewController> ().FirstOrDefault();
if (masterViewController != null) {
masterViewController.ReloadTable ();
}
base.ViewWillDisappear (animated);
}
}
You need to add using System.Linq; to get the OfType method.
Objective-C
It can be done by checking if navigationController contains viewcontroller like this:
Note viewControllerA pushes viewControllerB
Now in viewControllerB use viewWillDisappear method
- (void)viewWillDisappear:(BOOL)animated
{
if (![[self.navigationController viewControllers] containsObject: self]) //any other hierarchy compare if it contains self or not
{
// the view has been removed from the navigation stack or hierarchy, back is probably the cause
// this will be slow with a large stack however.
viewControllerA *objViewControllerA = [[self.navigationController viewControllers] lastObject];
//NSLog(#"%#",objViewControllerA);
if (objViewControllerA)
{
[objViewControllerA viewPoppedBack];
}
}
}
Now add one method in viewControllerA
-(void)viewPoppedBack
{
//reload tableview here
[yourTableViewHere reloadData];
}

MVVM Passing data between two views / view models

I have two Pages:
Page 1
Page 2
and two ViewModels with the same properties:
ViewModel1
Properties:
FirstName1
LastName1
ViewModel2
Properties:
FirstName2
LastName2
Now I want to pass data(properties) between ViewModel1 to ViewModel2, and retrive this data on the Page 2.
How do I achieve this?
You could take a look at MVVMLight's Messenger. Here's is a tutorial that could guide you on your way. Basically, the idea is to use a messenger that's independent from your Views/ViewModels to send messages from/to them. Your Views/ViewModels register and send specific messages that contain properties values you want to pass along.
Your page could be constructed like so:
public class Page2 {
public ViewModel1 VM1;
public Page2() {
VM1 = new ViewModel1(new ViewModel2());
}
}
Your ViewModel1 could look like so, with pass-through properties:
public class ViewModel1 : Person {
private ViewModel2 _vm2;
public ViewModel1(ViewModel2 vm2) {
_vm2 = vm2;
}
public override string FirstName {
get { return _vm2.FirstName; }
}
public override string LastName {
get { return _vm2.LastName; }
}
}
We assume your ViewModel2 has some business logic or something
public class ViewModel2 : Person {
//Etc
}
Both inherit from the same base class:
public abstract class Person {
public abstract string FirstName { get; }
public abstract string LastName { get; }
}
You can either go with a parent ViewModel that both viewmodels inherit from or an Event Aggregator. Here is a simple one using Reactive Extensions.

Grouped Table in Monotouch with MvvmCross

How do you implement a view with a grouped table in MonoTouch using MvvmCross, so you get something like this:
http://www.yetanotherchris.me/storage/downloads/UITableViewController.png
Right now I have this piece of code, but I cannot change the UITableViewStyle to Grouped:
public partial class HomeView : MvxBindingTouchTableViewController<HomeViewModel>
{
public HomeView(MvxShowViewModelRequest request)
: base(request)
{
}
public override void ViewDidLoad()
{
base.ViewDidLoad();
NavigationItem.SetRightBarButtonItem(new UIBarButtonItem("History", UIBarButtonItemStyle.Bordered, (sender, e) => ViewModel.DoGoToHistory()), false);
var source = new MvxActionBasedBindableTableViewSource(
TableView,
UITableViewCellStyle.Value1,
new NSString("HomeView"),
"{'TitleText':{'Path':'Date'},'DetailText':{'Path':'Location'},'SelectedCommand':{'Path':'ViewDetailCommand'}}",
UITableViewCellAccessory.DisclosureIndicator);
this.AddBindings(
new Dictionary<object, string>()
{
{ source, "{'ItemsSource':{'Path':'List'}}" },
{ this, "{'Title':{'Path':'TestTitle'}}"}
});
TableView.Source = source;
TableView.ReloadData();
}
}
Does anyone knows how to do this?
Your picture shows only one section.... assuming you are looking for just one section, but this grouped styling, then all you need to do is to introduce UITableViewStyle.Grouped somehow.
I'm not sure that the current MvxTableViewController exposes this for you - so you might either need to edit the Mvx source to add the appropriate constructors:
protected MvxTouchTableViewController(MvxShowViewModelRequest request, UITableViewStyle style = UITableViewStyle.Plain)
: base(style)
{
ShowRequest = request;
}
and
protected MvxBindingTouchTableViewController(MvxShowViewModelRequest request, UITableViewStyle style = UITableViewStyle.Plain)
: base(request, style)
{
}
Alternatively you could use a basic view controller (in which you add a Table as a subview) instead of a tableview derived view controller.
If you want multiple Groups, then you'll need to do a bit more work - as you'll need to work out how the bound TableViewSource works out the number of sections and the number of items in each section.
public class UserView : MvxTableViewController<UserViewModel>
{
public UserView()
:base(UITableViewStyle.Grouped)
{
}
}
Remember to make the constructor public and parameterless.

Passing on variables from ViewModel to another View (MVVMCross)

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!
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:
https://github.com/slodge/MvvmCrossTwitterSearch
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)

Categories