I can't really wrap my head around the following problem:
All I have in the application is a textboxfor the user input, a button for performing a background calculation on that input and a textblock. Imagine I have to use MVVM, so I have my view, viewmodel and model classes.
I bind the controls (textbox, button and textblock) from the view to the viewmodel on corresponding properties and commands. However, I'm not sure where the viewmodel functionality should end. For instance, would the following be a way to structure the application?
Model:
public class Model
{
public string Input { get; set; }
public string Output { get; set; }
public void FancyMethod ()
{
// Use input to calculate output
}
}
ViewModel:
public class ViewModel
{
public string Input {get; set;}
public string Output {get; set;}
public ICommand command {get; set;}
public Model model {get; set;}
public ViewModel()
{
model = new Model();
}
// When the button is pressed, model.input = Input and then execute model.FancyMethod()
}
If you want to keep a clean layer model, you should not include public Model model {get; set;} in your ViewModel.
So, for example, if you have a command, targeting some business model, your structure should be something like this:
//you don't have this one... but well, maybe other cases have
public class SomeService : ISomeService
{
//member of ISomeService
public void SomeFancyMethod(Model model)
{
//do stuff..
}
}
public class Model //might be database, or domain model.
{
public string Input { get; set; }
public string Output { get; set; }
}
As for your viewmodel, it will become something like this:
public class ViewModel
{
private ISomeService _someService;
//note: someService is passed through a IoC service like ninject, unity, autofac etc.
public ViewModel(ISomeService someService)
{
_someService = someService;
//initialize the command:
command = new RelayCommand(() =>
{
_someService .SomeFancyMethod(new Model()
{
//properties could be mapped with an automapper.
});
});
}
public ICommand command {get; private set;}
public string Input {get; set;}
public string Output {get; set;}
}
Note: there are some additional techniques involved:
using an inversion of control container, and pass the service
through the constructor.
abstracting the service by means of an
interface (ISomeService)
possibly some automapper to isolate your mapping from and towards Models/ViewModels
"So why make this so 'complicated'? You are just making a copy.", a commonly heard argument against this pattern:
Well:
it isn't complicated
doing this will separate your layers. This mean that changes in your datalayer doesn't break your View. In the long run, you'll benefit, as change will come and you'll need to maintain the code.
I guess the FancyMethod() contains your business logic and produces a value that you want to display in the view. In this case, FancyMethod() belongs to your model as it contains some business logic that is the same regardless of whether it's being executed in the context of a client application or some other component.
So your model would look something like this, i.e. it accepts an input and produces an output but it doesn't expose any properties that a view may bind to:
public class Model
{
public string FancyMethod(string input)
{
// Use input to calculate output
}
}
You could then inject your view model with the model and call the FancyMethod when the user executes the command by clicking on the Button in the view:
public class ViewModel
{
private readonly Model _model;
public ViewModel(Model model)
{
_model = model;
command = new RelayCommand(Execute, CanExecute);
}
public string Input { get; set; }
public string Output { get; set; }
public ICommand command { get; private set; }
private bool CanExecute(object _)
{
return !string.IsNullOrEmpty(Input);
}
private void Execute(object _)
{
Output = _model.FancyMethod(Input);
}
}
Obviously the view model class should also implement the INotifyPropertyChanged interface and raise change notifications to the view.
In short the business logic belongs to the model and the application logic, for example what happens when a user clicks a Button, belongs to the view model.
I think its not necessary to outsorce the Input and Output properties in another class. The reason for this is that the properties reflect the input and output of the view. So they have to be in the viewmodel.
You can outsorce the SomeFancyMethod in a service class to separate the logic from the viewmodel anlogous to mvc.
Related
I'm fairly new to Xamarin and stumbled across MVVM and really like it as an architectural pattern. However, I found that most HowTo's and tutorials out there only address the VVM (i.e. View-ViewModel) side of things, probably for simplicity sake!?
I would like to know how the communication between a ModelView and its associated models takes place using the INotifyPropertyChanged paradigm and other things.
If I understand correctly, I personally would put stuff like data handling, data storage (collections), db connections and stuff like that into a model. At least this is how I would've been doing it in the good old MVC days. Following questions arouse in my mind:
Where do I create the model(s) and how do I assign them to ViewModels?
How do I properly connect Model and ViewModel such that property updates are propagated and can be handled correctly?
Would you set the model as a member of the ViewModel?
In my current example, I would like to implement a SensorModel which provides several sensory data which layers above can subscribe to. I would like to send updates whenever new sensor data is available to the layers above; i.e. a ViewModel, for instance.
I'd basically had something like this in mind:
class Sensor
{
int _id { get; set; }
string _name { get; set; }
}
class SensorModel
{
private List<Sensor> _sensors { get; set; }
public void addSensor(Sensor s) ...
public void removeSensor(Sensor s) ...
}
Does anybody have links to actual/complete MVVM examples, including the connection between Model and ViewModel?
Any help appreciated.
Use Lastest stable Xamarin Forms
MODELS
In the Project, create a Models folder
To store data, i usually use SQLite or a temp store:
class DataStore
{
public static List<SensorModel> SensorStore { get; set; }
}
Create the SensorModel model
class SensorModel
{
internal int Id { get; set; }
internal string Sensor { get; set; }
}
VIEWMODELS
In the Project, create a ViewModels folder
Create a SensorVM viewmodel
class SensorVM : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public System.Windows.Input.ICommand StartCommand { get; set; }
public string SensorName { get; set; }
public SensorVM()
{
DataStore.SensorStore = new List<SensorModel>();
StartCommand = new Xamarin.Forms.Command(StartSubmit);
}
private void StartSubmit(object paramter)
{
var sensor = new SensorModel()
{
Id = 1,
Sensor = SensorName
};
AddSensor(sensor);
}
public void AddSensor(SensorModel sensor)
{
//do something
DataStore.SensorStore.Add(sensor);
}
}
VIEWS
In the Project, create a Views folder
Create a Sensor.xaml view
<ContentPage.Content>
<StackLayout Spacing="10" Orientation="Vertical">
<Entry Text="{Binding SensorName}" />
<Button Command="{Binding StartCommand}" Text="Start" />
</StackLayout>
</ContentPage.Content>
In the code behind:
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class Sensor : ContentPage
{
SensorVM vm;
public Sensor()
{
InitializeComponent();
BindingContext = vm = new SensorVM();
}
}
Hope that helps.
I would like to know how the communication between a ModelView and its
associated models takes place using the INotifyPropertyChanged
paradigm and other things.
I think the best way to create a communication in MVVM is Messaging Center.
https://learn.microsoft.com/pt-br/xamarin/xamarin-forms/app-fundamentals/messaging-center
It's not coupled from device (sensor) code to view models ...
Your messages, in this model, active events that could acess your viewmodels as well as other structures.
A sample of this
In your view use :
public void MessegingCenterInit()
{
#region Bluetooth
MessagingCenter.Subscribe<string, string>("App", "Status_name", (sender, arg) =>
{
App.PVM.Name = $"{arg}";//using INotifyPropertyChanged and view model
viewmodelMethod();//using only a viewmodel
});
#endregion
}
in your model use:
public string Name
{
get { return name; }
set
{
name = value;
App.PVM.Add_patient.AddCanExecuteChanged();//PVM is a viewmodel
//The view model need to have INotifyPropertyChanged as a interface
}
}
In specific code you have (into a generic method or event):
string new_name = John;
MessagingCenter.Send<string,string>("App","Status_name",new_name);
There are several ways to do it, its a simple one, you can try use objects as sender with less information.
Regards
Xamarin itself gives a really good example with their default Master-Detail Solution.
Just create a new Xamarin.Forms App and select the Master-Detail Layout.
It includes several Views, ViewModels (with the BaseVIewModel) and some MockUp Data Classes.
For a start just have a look around there :)
In almost all cases there is no communication between the Model and ViewModel, and very rarely there is communication between the Model and View. If you need to communicate between Model and ViewModel it is extremely likely that you are doing something wrong.
To explain, your model usually describes some entity, like that you have the class Cat:
public class Cat
{
public string Color {get; set;}
}
It is generally used in ViewModel either as the field or as a Collection like:
public class CatsViewModel
{
public List<Cat> Cats {get; set;}
}
The cat shouldn't be able to update by itself, if it is updated it is done either by bindings with the view or somewhere from ViewModel.
So you have some architectural problems in your app, I think.
I'm familiar with MVVM and differences between models, viewmodels and views. The only thing that I'm not able to find answer to is how to update models at runtime. Simple example to show you what I mean:
Let's say I have application which can display graphs and store them in a database.
I have models
public class Session {
public Document Doc { get; set; }
}
public class Document {
public string Name { get; set; }
public Point[] GraphPoints { get; set; }
}
I can connect those to their viewmodels by passing them as parameters, so:
public class SessionViewModel{
private readonly Session _session;
public SessionViewModel(Session session)
{
this._session = session;
}
}
public class DocumentViewModel{
private readonly Document_document;
public SessionViewModel(Document document)
{
this._document = document;
}
}
public class ShellViewModel {
public SessionViewModel SessionVm { get; set; } // <-- Bind in view
public DocumentViewModel DocumentVm { get; set; } // <-- Bind in view
private Session _session;
public ShellViewModel()
{
_session = new Session();
session.Doc = new Document();
SessionVm = new SessionViewModel(session);
DocumentVm = new DocumentViewModel(session.Doc);
}
}
Problem appears when in the middle of my application's life cycle I decide to change value of document. For example:
public void OnNewDocumentLoaded(Document newDoc)
{
_session.Doc = newDoc;
}
_session.Doc was changed but every DocumentViewModel has its own instance of document which is passed in a constructor, so even though I changed model, my viewmodel stays the same.
Also I don't want to use INotifyPropertyChanged inside my model, because models should not know about framework and from my understanding this is a bad approach. Also I keep my models in PCL project so I'm not even able to implement INotifyPropertyChanged in my models.
From my understanding of a MVVM approach, models should not have a viewmodel associated with them. Instead, your views should have a viewmodel associated to them. Inside your viewmodel you can have objects from models in your application. Inside your viewmodel is where you should implement INotifyPropertyChanged. Those methods control the objects you have changed and then binding can occur between your view and viewmodel.
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.
In my solution I have two projects.
Project 1 (Core)
Mapping SQL to DTO using Dapper
Project 2 (WebUI - ASP.NET MVC 4)
Here I use a ViewModel per View.
Examples of a Controller
[HttpGet]
public ActionResult Edit(int id)
{
// Get my ProductDto in Core
var product = Using<ProductService>().Single(id);
var vm = new ProductFormModel(product);
return View(vm);
}
Examples of a ViewModel
public class ProductFormModel : BaseViewModel, ICreateProductCommand
{
public int ProductId { get; set; }
public int ProductGroupId { get; set; }
public string ArtNo { get; set; }
public bool IsDefault { get; set; }
public string Description { get; set; }
public string Specification { get; set; }
public string Unit { get; set; }
public string Account { get; set; }
public decimal NetPrice { get; set; }
public ProductFormModel(int productGroupId)
{
this.ProductGroupId = productGroupId;
}
public ProductFormModel(ProductDto dto)
{
this.ProductId = dto.ProductId;
this.ProductGroupId = dto.ProductGroupId;
this.ArtNo = dto.ArtNo;
this.IsDefault = dto.IsDefault;
this.Description = dto.Description;
this.Specification = dto.Specification;
this.Unit = dto.Unit;
this.Account = dto.Account;
this.NetPrice = dto.NetPrice;
}
public ProductFormModel()
{
}
}
Explanation:
I'll get my DTOs in my controller using a service class in the project (Core).
Then i create my ViewModel and pass the DTO to the constructor in ViewModel.
I can also use this view to add a new Product because my ViewModel can take a empty constructor.
Does anyone have experience of this. I wonder if I am in this way will have problems in the future as the project gets bigger?
I know this has nothing to do with Dapper. But I would still like a good way to explain my solution.
I think you will be fine using your current approach. More importantly, start out like this and refactor if you start to encounter problems related to your object mapping code (instead of thinking too much about it beforehand).
Another way to organize mapping logic that I use sometimes is to employ extension methods. That way, the mapping code is kept separate from the view model itself. Something like:
public static class ProductMappingExtensions
{
public static ProductFormModel ToViewModel(this ProductDto dto)
{
// Mapping code goes here
}
}
// Usage:
var viewModel = dto.ToViewModel();
Yet another approach would be to use a mapping framework like AutoMapper - this is a good fit in particular if your mapping logic is simple (lots of 1:1 mappings between properties).
But again, start simple and refactor when you need to.
I realize that this is a little bit late answer, but maybe it will help someone in the future.
This way of doing mapping between objects breaks the 'S' of the SOLID principles, because the responsibility of the ViewModel is to prepare data in its properties to be ready to use by the view and nothing else, therefore, mapping objects should not be on it's responsibilities.
Another drawback of this way is that it also breaks the 'Loose Coupling' OO principle as you ViewModel is strongly coupled with your DTO.
I think, even when we are in the very first step of the project, there are some importants OO principles that we should never break, so using mapper classes, either auto (AutoMapper, ValueInjecter ...) or manual, is definitely better.
I have a View that has the ability to both create and edit an item that I have separated into partial views:
MainEditView.cshtml
_CreateChildDialog.cshtml
_EditChildDialog.cshtml
I have separate ViewModels for both the Create and Child items:
public class CreateChildViewModel
{
public string ItemText { get; set; }
}
public class EditChildViewModel
{
public string ItemText { get; set; }
}
Since the partial views for the Edit and Create dialog boxes will both be rendered on the same page, I will have a conflict for form id's and names...since they are both called ItemText.
Is it possible to customize the binding of these elements without writing a custom model binder?
I would like to do something like:
public class EditChildViewModel
{
[BindFrom("EditItemText")]
public string ItemText { get; set; }
}
Or does it just make more sense to rename the ViewModel properties to:
public class EditChildViewModel
{
public string EditItemText { get; set; }
}
public class CreateChildViewModel
{
public string CreateItemText { get; set; }
}
EDIT
Based on converstation with Darin I want to make this a little more clear.
My Parent has an Edit action.
When you edit the Parent, you would never create a new child or edit a child when you are calling the ParentController.Edit action.
I have a separate controller for the Child object that has a Create and Edit method:
public class ChildController
{
public ActionResult Edit() {}
public ActionResult Create() {}
}
I am using jQuery calls to asynchronously post to this controller when you edit or create a child. Basically I use a jquery dialog to create/edit a child that will get saved immediately when I click Ok on the dialog. This would happen even before clicking save for the Edit action of the parent.
I would use editor templates. Normally you would pack those two view models into a main view model which will be used by the main view:
public class MyViewModel
{
public CreateChildViewModel Create { get; set; }
public EditChildViewModel Edit { get; set; }
}
and then:
#model MyViewModel
#Html.EditorFor(x => x.Create)
#Html.EditorFor(x => x.Edit)
and I would replace the two partials by their corresponding editor templates (~/Views/Shared/EditorTemplates/CreateChildViewModel.cshtml and ~/Views/Shared/EditorTemplates/EditChildViewModel.cshtml). The editor templates will take of generating proper names and ids of the corresponding input elements.
Personally I tend to prefer editor/display templates instead of partials as they handle better naming of input elements.