Given the following MVP setup how would you update the winforms UI? This is my first time trying to implement MVP and I believe I have been following a "Passive View" implementation of MVP.
I really didn't want my model to reference the presenter because I thought that was against the idea of the MVP pattern but then isn't the purpose of the Presenter to update the View? And obviously didn't want my Model updating my view. Let me know if I have made a mistake in my thinking though!
public class HomePresenter
{
Item item;
Model model
SomeTask()
{
model.AnotherTask(item);
}
}
public class Model
{
public void AnotherTask(Item item)
{
/* SOME COMPLEX LOGIC HERE */
if (item.BoolProperty)
// How do I write "Success" to richtextbox in View
else
// How do I write "Failure to richtextbox in View
}
}
Your Presenter should sync your View and your Model. The View only shows the data. The Model know's the business logic and "real" data and the Presenter link the Model data to your View. So you won't access the Richtextbox from your Model. Instead you do this from your Presenter. To stay independent you should use Interfaces. So you have no direct access to View elements in Presenter or Model.
Create an IView Interface and an IModel Interface. Both of them are
known by your Presenter.
Your example could look like this:
public class HomeView : IHomeView
{
public string Text
{
get {return richtextbox.Text;}
set {richtextbox.Text = value;}
}
}
public class HomePresenter
{
IHomeView view;
IModel model;
HomePresenter(IHomeView view, IModel model)
{
view = view;
model = model;
//Update View
view.Text = model.Text;
}
public void UpdateModel
{
model.Text = view.Text; //Set the Model Property to value from Richtextbox
}
}
public class Model : IModel
{
public string Text {get;set} //Property which represent Data from Source like DB or XML etc.
}
You find another example here.
Related
I have an application which holds objects in a list view. I want to enable user to change those objects using modal dialogs when an item is double clicked in the list view.
I am using the mvvm light toolkit. When I double click an item in the list view I know which object and therefor which model type is chosen. I am retrieving the corresponding ViewModel via a ServiceLocator and I am using Execute to "launch" the viewmodels' relay command passing the model as object with the needed data information. However, now, in the ViewModel, I am struggling how to open the corresponding view as a model dialog bound to the ViewModel?
Edit (add some code fragments)
public class ViewModelLocator
{
public ViewModelLocator
{
SimpleIoc.Default.Register<OptionSpecificViewModel>();
}
public OptionSpecificViewModel OptionSpecificView
{
get
{
return ServiceLocator.Current.GetInstance<OptionSpecificViewModel>();
}
}
}
the view locator works fine
public class MyListViewManager
{
public void CallMyDialog(Guid xxx)
{
var objModel = GetMyModelByGuid(xxx);
var vm = CommonServiceLocator.ServiceLocator.Current.GetInstance<ObjectSpecificViewModel>();
vm.EditCommand.Execute(objModel);
}
}
the "ListViewManager" works also
My Problem is, that I am in the correct ViewModel with the correct model (data).
public class OptionSpecificViewModel : ViewModelBase
{
public OptionSpecificViewModel()
{
InitRelayCommands();
RegisterMessages();
}
...
public void OnEditCommand(object model)
{
// I reach here in the correct view model with the correct model
// but how can I open the view here??
}
}
As far as I understood MVVM, the view model does know the model and the view has a "connection" to the ViewModel via binding.
But the ViewModel does not know the view. so how do I start the view?
I'm using MVVM in a Xamarin application, I have an interface to navigate between pages:
public interface INavigate
{
INavigate Next();
INavigate Previous();
string ViewTitle { get; }
}
In the implementing views:
public partial class V2Upload : ContentView, INavigate
{
public string ViewTitle => "Upload photos";
public INavigate Next()
=> new V3AdDetail();
public INavigate Previous()
=> new V1Agreement();
}
and in the view model
I have a property of type INavigate:
public INavigate CurrentAddItemStep
{
get { return _currentAddItemStep; }
set { Set(ref _currentAddItemStep, value); }
}
and the Content property of the parent view is bound to this property:
when next button is clicked I execute this code:
CurrentAddItemStep = CurrentAddItemStep.Next();
ViewTitle = CurrentAddItemStep.ViewTitle;
now a validation method is required before navigating to the next page for all the Content views..
I want to keep the MVVM pattern as clean as possible by not writing business code in the view, for example in the V2Upload view the File1 and File2 properties of the view model shouldn't be null:
private bool ValidateFiles(){
return (File1 ?? File2) != null;
}
but since the navigating is done dynamically in run-time, I can't know which view is the current view.
I'm thinking to use reflection , to know what is the name of the view (but this will break the whole design)
Another option is to provide a function parameter to the Next method, but also how to provide it in the design time from the view model?
This is what I'm doing now:
public INavigate Next()
{
if (((ViewModel.AddItemViewModel)BindingContext).ValidateFiles())
return new V3AdDetail();
else
return this;
}
but again, I'm accessing the view model from the view (and had to change the ValidateFiles method from private to public), which I want to avoid
For my project I need to know which View is using my ViewModel
So i created this ViewModel:
public class HistoriqueViewModel : INotifyPropertyChanged
{
public HistoriqueViewModel(MetroWindow view)
{
this.MetroWindow = view;
this.ExportCommand = new RelayCommand(Export_Ex);
}
private MetroWindow _metroWindow;
public MetroWindow MetroWindow
{
get { return _metroWindow; }
set
{
if (Equals(value, _metroWindow)) return;
_metroWindow = value;
OnPropertyChanged();
}
}
//.........
}
And in the View constructor:
public partial class ViewHisto : MetroWindow
{
public ViewHisto()
{
InitializeComponent();
DataContext=new HistoriqueMV(this) ;
}
}
It Work perfectly for me but I want to know if this Break the MVVM Pattern?
Yes, this breaks MVVM. A properly constructed view model shouldn't care about what the view is.
Nothing in your code really suggests why you are passing that reference (other than exposing the view as a public property, which is an even bigger no-no) but there are several ways around it:
Pass the view as an interface and hold/expose that
Use a mediator to pass whatever messages necessary between the view model/view
Have the view invoke whatever methods it needs on the view model, and have the view model raise events that the view can listen to.
Any of the above approaches will provide far better decoupling than the one you are going with.
One other thing, its "View Model", not "Model View"
I'm trying to refactor a WindowsForm to the MVP Pattern. The app is a tile editor. The form has a custom control where i display the tilemap (TileDisplay). After loading a map from a file i call a method named "AdjustHScrollBar" to readjust the horizontal scrollbar to the tilemap-size. I'm not 100% sure how to split the method according to MVP.
First the original none MVP method:
private void AdjustHScrollBar()
{
if (tileMap.GetWidthInPixels() > tileDisplay.Width)
{
hTileScrollBar.Visible = true;
hTileScrollBar.Minimum = 0;
hTileScrollBar.Maximum = tileMap.GetWidth();
}
else
{
hTileScrollBar.Visible = false;
}
}
This is the method after using MVP in the presenter:
private void AdjustHScrollBar()
{
if (mainFormModel.TileMap.GetWidthInPixels() > mainFormView.GetTileDisplayWidth())
{
mainFormView.EnableHScrollBar(mainFormModel.TileMap.GetWidth());
}
else
{
mainFormView.DisableHScrollBar();
}
}
The view doesn't know the presenter. My question is if the presenter should know the controls of the view. In my implementation it doesn't - that's the reason for the GetTileDisplayWidth, EnableHScrollBar and DisableHScrollBar - methods. Afaik that's the right way, but this seems to become a lot of work if i have to make a method for every property i need from the view. I have basicly the "same" code for the vertical scroll bar for e.g. (so that's 6 methods for readjusting the scroll bars).
The point of the Presenter layer is to be able to communicate with the View layer without knowing the specifics of the view, i.e. anything to do with size, color,what type of view it is or any other properties.
Usually the presenter class will take the view object in it's constructor. Ideally you would have the Adjust method on the view and the presenter would call it directly, even more ideally you would do this through an interface rather than direct view class.
In your code you are accessing all of the view's properties and then trying to manipulate them through the presenter, you don't necessarily have to do that unless you have some sort of dependency on business logic. So you can do the whole operation on the View layer and then call it from the Presenterlayer.
MVP involves a lot of code and that is the trade-off that you have to accept.
I would do something like this in the presenter:
public interface ITileMapView
{
event EventHandler<string> TileMapFileLoaded;
void OnTileMapLoaded(TileMapModel model);
}
public class TileMapPresenter
{
private readonly ITileMapView view;
public TileMapPresenter(ITileMapView view)
{
this.view = view;
view.TileMapFileLoaded += OnTileMapFileLoaded;
}
private void OnTileMapFileLoaded(object sender, string filename)
{
// Parse data from file
// Populate model
// Tell view
view.OnTileMapLoaded(model); //Implement the 'AdjustHScrollBar' logic in the view
}
}
The Presenter knows when and how to get data, and how to interpret the data. You should not let the Presenter engage in any view specific logic.
I've been working on an MVVM application in C# but consistiently run into some problems when working with the collections of ViewModels my View digests. Specifically, they all tend to relate to the issue of the Model being a private member of the ViewModel.
An example of this is creating new ViewModels (as requested by the View). For some preamble (although you might not need these to help me) here are example Model and ViewModel classes:
Private Class Model()
{
public string Name { get; set; }
}
Public Class ViewModel()
{
Private Model _Model;
Public Void ViewModel(Model model)
{
_Model = model;
}
Public String Name
{
get
{
return _Model.Name;
}
set
{
_Model.Name = value;
}
}
}
The entire model is never directly exposed as a public member of the ViewModel. The MainWindowViewModel handles collections of Models (private, the view cant see these) and ViewModels (public for View digestion):
Public Class MainWindowViewModel
{
Private List<Model> _NamesModel;
Private ObservableCollection<ViewModel> _NamesViewModel;
Public Void MainWindowViewModel()
{
//Lets pretend we have a service that returns a list of models
_NamesModel = Service.Request();
foreach(Model model in _NamesModel)
{
ViewModel viewmodel = new ViewModel(model);
_NamesViewModel.Add(viewmodel);
}
}
Public ObservableCollection<ViewModel> NamesViewModel
{
get
{
return _NamesViewModel;
}
}
}
Now thats the preamble but now I have a problem. How do I add a new ViewModel? Do methods within my view create a new ViewModel and populate that? Being a purist, I'm assuming the View should not be allowed to create or populate Models at all. Should my ViewModel contain a constructor that accepts nothing (i.e. no underlying model) and instead creates a blank to populate?
These kinds of issues keep coming up with a "pure" MVVM approach. I've had to create a public method in my ViewModel (bool compare(Model model)) that will compare a model (ready for deletion etc.) to it's internal one. If the models were publicly exposed (breaking purity) then it would be much easier to do stuff like find the ViewModel thats connected to a Model.
I can sympathize with some of those problems. I recently wrote an MVVM application where similar questions came up frequently. One of the tricks is to decide - definitively - which class is going to be responsible for Model instances. Do you want it to be your MainWindowViewModel? Or your NameViewModel? You don't want to share the responsibilities of creating/deleting the model between both of those classes; you'll have quite a logistical nightmare.
Secondly, even a "pure" MVVM approach doesn't dictate that you can't expose the model publicly. You said yourself that doing so would save you a lot of headache: DO IT. MVVM dictates only that the ViewModel has no knowledge/access of the View. There are many "official" MVVM examples that go so far as to implement their Model using the INotifyPropertyChanged interface, and bind directly to properties on the Model.
Personally, I think I would dictate control of the NameModel to the NameViewModel. This means that you should remove the list of NameModels completely from the MainWindowViewModel. If you want to give the NameViewModel an optional constructor which takes a Model, that would be fine too.
I'm a fan of this approach:
public NameViewModel : ViewModelBase
{
public NameModel Model
{
get { /* get stuff */ }
set { /* set stuff */ }
}
// Default constructor creates its own new NameModel
public NameViewModel()
{
this.Model = new NameModel();
}
// Constructor has a specific model dictated to it
public NameViewModel(NameModel model)
{
this.Model = model;
}
//Model wrapper properties
public String Name
{
get { return Model.Name; }
set { Model.Name = value; }
}
}
and...
public class MainWindowViewModel
{
Private ObservableCollection<ViewModel> _NameViewModels;
Public Void MainWindowViewModel()
{
//Lets pretend we have a service that returns a list of models
var nameModels = Service.Request();
foreach(Model model in nameModels)
{
ViewModel viewmodel = new NameViewModel(model);
NameViewModel.Add(viewmodel);
}
}
Public ObservableCollection<ViewModel> NameViewModels
{
get
{
return _NameViewModels;
}
}
}
In this way your MainWindowViewModeldoesn't keep an entirely separate copy of the Models; it only tracks the NameViewModels. Each NameViewModel is responsible for its own underlying model, while still making the option available to have a specific model passed to it during construction.
All the creation-related issues can be resolved with introduction of factory design pattern. The factory will take care of creating view models basing on model that was provided.
public class MainWindowViewModel
{
private List<Model> _NamesModel;
private ObservableCollection<ViewModel> _NamesViewModel;
private IViewModelFactory factory;
public void MainWindowViewModel(IViewModelFactory factory)
{
//Lets pretend we have a service that returns a list of models
_NamesModel = Service.Request();
_NamesViewModel = factory.CreateNamesViewModels(_NamesModel);
}
public ObservableCollection<ViewModel> NamesViewModel
{
get
{
return _NamesViewModel;
}
}
}
What is more, you could even get rid of Service dependency in view model and move it to the factory itself, thus reducing the need to keep model in view model (admittedly though, removal of model might not work in more complex scenarios):
public ObservableCollection<ViewModel> CreateNamesViewModels()
{
var models = Service.Request();
return new ObservableCollection(models.Select(m => new ViewModel(m)));
}
Also, your main window view model can expose commands that utilize factory to create any new instances. This way, no model is leaking to view and also no creation details are exposed (since commands will hide actual implementation).