How to display complex data in MVP Passive View - c#

I've been looking in the MVP pattern for a while, and managed to create some simple MVP-compliant applications.
I am now trying to apply the pattern to a more complex application, and I have some doubts on the best way of doing that.
My application has a single WinForm, with two buttons for loading two different kinds of data. My view interface looks like the following:
interface IView_MainForm
{
// Load input
//
event EventHandler<InputLoadEventArgs> LoadInput_01;
event EventHandler<InputLoadEventArgs> LoadInput_02;
bool Input01_Loaded { get; set; }
bool Input02_Loaded { get; set; }
}
The IView is referenced in my presenter via constructor injection:
public Presenter_MainForm(IView_MainForm view)
{
this.View = view;
this.View.LoadInput_01 += new EventHandler<InputLoadEventArgs>(OnLoadInput_01);
this.View.LoadInput_02 += new EventHandler<InputLoadEventArgs>(OnLoadInput_02);
}
So far, so good. When the user clicks any of the two buttons for loading data, a LoadInput_## event is raised, the Presenter is handling it, checks the input for errors and structures it according to my data model.
My next step would be displaying the processed data back in the View.
I'm striving to keep my View as passive and "dumb" as possible, assuming it knows nothing of the Presenter (it doesn't subscribe to its events, the Presenter sends data to the View by calling IView methods instead), let alone of the Model.
How am I supposed to populate a control like a TreeView, if the View has no idea of what the data model looks like?
Also, am I getting the whole MVP thing right, or is there anything I have missed?

There is nothing wrong with having complex type properties in your View. Let's say you have some ComplexType.
class ComplexType
{
public string ParentNode {get;set;}
public List<string> ChildNodes {get;set;}
// some other properties
}
Let's also assume ComplexType is data model for your TreeView. It is perfectly fine with MVP pattern to have properties on your View that will have ComplexType. So having something like this is perfectly fine
interface IView_MainForm
{
// Load input
//
event EventHandler<InputLoadEventArgs> LoadInput_01;
event EventHandler<InputLoadEventArgs> LoadInput_02;
bool Input01_Loaded { get; set; }
bool Input02_Loaded { get; set; }
ComplexType Input01Data {get;set;} // you might actually have some code in get/set setters
ComplexType Input02Data {get;set;} // you might actually have some code in get/set setters
public void SetInput01Data(ComplexType input01Data)
{
Input01Data = input01Data;
// some other stuff
}
}
And since your Model is for View that has 2 inputs, your Model could look something like this
public interface IModel
{
public ComplexType Input01Data {get;set;}
public ComplexType Input02Data {get;set;}
}
Now in your Presenter you would just handle event fired from View, populate Model and set properties on View
class Presenter
{
private IModel _myModel...
private IRepository _repository;
public Presenter(IView_MainForm view, IRepository repository)
{
_repository = repository;
this.View = view;
this.View.LoadInput_01 += new EventHandler<InputLoadEventArgs>(OnLoadInput_01);
this.View.LoadInput_02 += new EventHandler<InputLoadEventArgs>(OnLoadInput_02);
}
public void OnLoadInput_01(object sender, InputLoadEventArgs e)
{
// get data based on passed arguments (e.SomeProperty)
// construct IModel
myModel = _repository.GetData(e.SomeProperty);
// pass data to IView_MainForm
View.SetInput01Data(myModel.Input01Data);
}
}
And regarding your concern
I'm striving to keep my View as passive and "dumb" as possible,
assuming it knows nothing of the Presenter (it doesn't subscribe to
its events, the Presenter sends data to the View by calling IView
methods instead), let alone of the Model.
Your View still doesn't know anything about Presenter nor Model. It just fires events, get data from Presenter and binds its controls. And you have testability in place (please note this Unit Test is pseudo code, since I don't know how you retrieve data, what input you required in button click event etc...) .
[Test]
public void ShouldLoadInput01DataOnButtonClick()
{
// Arrange
IModel data = // create dummy data
Mock<IView_MainForm> clientsViewMock = new Mock<IView_MainForm>();
Mock<IRepository> clientsRepositoryMock = new Mock<IRepository>();
clientsRepositoryMock.Setup(repository => repository.GetData(something)).Returns(data.Input01Data);
var presenter = new Presenter(clientsViewMock.Object, clientsRepositoryMock .Object);
// Act
clientsViewMock.Raise(view => view.LoadInput01 += null, new InputLoadEventArgs());
// Assert
clientsViewMock.Verify(view => view.SetInput01Data(data.Input01Data), "Input01 data expected be set on button click.");
}

Related

Two ViewModels are using one Model which holds data

I've searched for almost 3 hours and found alot about PRISM and VM data sharing, but I can't get my brain to understand it.
I have the two ViewModels MainViewModel and ListViewModel. The MainViewModel has a dockpanel where I want to have a common menu to add, remove items and close the application for example. There is also a usercontrol in the XAML which is showing the ListView. The ListView contains a few ObservableCollection<ProjectModel>.
From the MainVM I want to add more Projects - so to a collection. To access the ListModel, which contains the data, I pass it to the constructor of both ViewModels:
public MainModuleViewModel(ListModel listModel)
{
ListModel = listModel;
InitializeCommands();
}
public ListModuleViewModel(ListModel listModel)
{
ListModel = listModel;
InitializeCommands();
}
But it seems that during initializaiton to different objects of ListModel are created.
So my question is: What do I have to do, to work just on the one object of ListModel, which I'm passing to the constructors?
You have two options, as always:
register the ListModel as singleton (ContainerControlledLifetimeManager in Unity)
or register a service as singleton that gives the data to the view model
I'd go with the second one as it's far more flexible - you can use a different ListModel instance elsewhere with different content, you can make the service to give each view model its own instance (though with the same content) or you can make it give each view model the same instance and so on...
public interface IDatasource
{
ListModel Data { get; }
}
internal class StephensService : IDatasource
{
ListModel Data { get; } = new ListModel(); // or however you plan to procure the data
}
// ... bootstrapper / module initialization ...
Container.RegisterType<IDatasource, StephensService>( new ContainerControllerLifetimeManager() );
// ...
internal class ListModuleViewModel
{
public ListModuleViewModel( IDatasource datasource )
{
var heresMyData = datasource.Data;
}
}
Depending on your needs, the service can implement INotifyPropertyChanged or the ListModel can be an ObservableCollection...

How to synchronize Model and View Model bidirectional in MVVM?

It's a simple question and I searched the Internet for hours without success...
I have a model and a view model with one property. To make this property viewable in the view, I use a view-model-object which should automatically be generated from the model-object and vice versa. Of course, the following code will throw an StackOverflowException, because the updating of the model-object in the model causes an update of the view-model-object in the view-model and this causes an update of the model-object in the model and so on...
class ModelObject
{
...
}
class ViewModelObject
{
...
}
class Model : INotifyPropertyChanged
{
private ModelObject modelObject = new ModelObject();
...
public ModelObject ModelObject
{
get
{
return this.modelObject;
}
set
{
this.modelObject = value;
this.NotifyPropertyChanged("ModelObject");
}
}
}
class ViewModel : INotifyPropertyChanged
{
private ViewModelObject viewModelObject = new ViewModelObject();
private Model model = new Model();
...
public ViewModel()
{
this.model.PropertyChanged += new PropertyChangedEventHandler(this.propertyChangedEvent);
}
public ViewModelObject ViewModelObject
{
get
{
return this.viewModelObject;
}
set
{
this.viewModelObject = value;
this.model.ModelObject = new ModelObject(...);
this.NotifyPropertyChanged("ViewModelObject");
}
}
private void propertyChangedEvent(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName.Equals("ModelObject"))
{
this.ViewModelObject = new ViewModelObject(...);
}
}
}
What is the common way to synchronize these two objects?
There is no silver bullet. As model is a representation of the database and viewmodel is more closer to the interface, there is always some business logic needed to convert the model to view model and vice versa.
I usually have two methods in my view model class - SyncModel(ViewModel viewModel) and SyncViewModel(Model model)
One more suggestion -
Model should not implement INotifyPropertyChanged. The view model should implement this as its bound to the user interface. Why does the model ever need to change? It represents whats in the db. You can refresh it but why do you need change notifications for the model?
Edit: MVVM: Binding to Model while keeping Model in sync with a server version
Hard reference. Each class having a reference to another, listens to property change event and updates itself accordingly.
Observer Pattern - Have an observer class, each class register itself with an observer, observer listens for any changes and updates all its subscribers.
There's also an event aggregator which might be useful.
If you want a deferred update, an isDirty property would be required. You know your application better, google for more articles and choose wisely.

WPF MVVM PropertyChanged notifications in the ViewModel triggered by Model Events

I am having a problem understanding how to propagate a property changed event in a Model class up through the ViewModel and into the view. I am trying to conform to the MVVM pattern so please keep that in mind.
I have a Model that I am trying to expose by the ViewModel. My Model class queries an Api call to get the server status and exposes that status in public properties. Ex:
public class ServerStatusRequest : ApiRequest
{
//Exposable properties by request
public ServerStatusHelperClass Status { get; set; }
Where ServerStatusHelperClass is just a wrapper for the combined results in the query:
public class ServerStatusHelperClass
{
public bool ServerStatus { get; set; }
public int OnlinePlayers { get; set; }
The cool thing about my ApiRequest base class is that it checks the cache time of a particular Api call and updates the Results by using a System.Timers.Timer. So, for example, the ServerStatus Api call is cached for 3 minutes on the Api, so every 3 minutes my ServerStatusApiRequest object will have fresh data for it. I expose a UpdatedResults event in all ApiRequest classes to notify when new data comes in.
Now I want my ViewModel to have an instance of ServerStatusApiRequest and bind to its ServerStatusHelperClass Status property and stay up to date with the changes every time the information is updated, but my view (for binding) can't know about my model, and thus, doesn't know about my UpdatedResults event in my ApiRequest class. How can I reflect that out to the View through my ViewModel? Am I doing something completely weird here?
Here is what I have that is semi-working but I feel is a very hacky solution:
In my ViewModel:
public const string EveServerStatusPropertyName = "EveServerStatus";
private ServerStatusRequest _eveServerStatus = new ServerStatusRequest();
public ServerStatusRequest EveServerStatus
{
get
{
return _eveServerStatus;
}
set
{
//if (_eveServerStatus == value)
//{
// return;
//}
//RaisePropertyChanging(EveServerStatusPropertyName);
_eveServerStatus = value;
RaisePropertyChanged(EveServerStatusPropertyName);
}
}
public void UpdateEveServerStatus(object sender, EventArgs e)
{
EveServerStatus = (ServerStatusRequest)sender;
}
And in the ViewModels constructor I subscribe to the Model's event:
EveServerStatus.UpdatedResults += new UpdatedResultsEventHandler(UpdateEveServerStatus);
As you can see, this seems extremely redundant. And I also ran into a problem where I had to comment out the check in the setter for EveServerStatus because at that point the _eveServerStatus was already updated to value just without it knowing and I wanted to fire the event anyway.
I fell like I'm missing a key concept here to link this all together much more easily.
Thanks for any input.
I have come across a much better way to implement the behavior I was looking for. Here is the code in my ViewModel:
private ServerStatusRequest _eveServerStatus = new ServerStatusRequest();
public ServerStatusRequest EveServerStatus
{
get
{
return _eveServerStatus;
}
}
No setter as my ViewModel nor my View should be changing this data. And Inside my ServerStatusRequest class I have a property exposing the ServerStatusHelperClass object as shown in the Question. I have changed the ServerStatusHelperClass and made it implement INotifyPropertyChanged as so:
public class ServerStatusHelperClass : ObservableObject
{
private bool _serverStatus;
public bool ServerStatus
{
get
{
return _serverStatus;
}
set
{
_serverStatus = value;
RaisePropertyChanged("ServerStatus");
}
}
...
ObservableObject is just a simple class that implements INotifyPropertyChanged for me from mvvmlight.
By doing this my View is automatically updated when my ApiRequest class modifies it's ServerStatusHelperClass object.
Input on this solution is welcome.

MVP and rich user interface

I want to implement MVP pattern for my application.
MVP Passive View actually. So I came to a problem, it's easy one,
but can't decide which path should I take, so
I want to ask you guru's, how to properly work with MVP and display rich UI.
I mean, let's assume we need to display some data, and customer wants it to be TreeView.
There is requirement, that if user select different treenode, then the application updates itself with
new data or something like that.
At this point, i'm not sure how to implement View.
(All view logic goes to presenter)
I don't think that it is a good idea, to expose WinForms class
ISomeForm : IView {
//Presenter will take control of this TreeView.
TreeView Host {
get;
}
}
or exposing my data models
ISomeForm : IView {
//View knows how to display this data
List<MyDataNodes> Items {
get;
set;
}
}
or using other View interfaces.
ISomeForm : IView {
//Presenter knows what Views presenter should display.
List<IDataView> Items {
get;
set;
}
}
Any suggestions?
I would go with the View Interfaces.
In WPF MVVM, the more view separation I have, the easier it is to manage the UI/Logic interaction along the way.
I had to solve this problem using a MVC pattern. You could expose the TreeView as you suggested in your first example. Then the presenter could subscribe some events of the TreeView. But if you go this way your presenter will probably have to subscribe a lot of events of differents controls on your form. I have chosen to have a single event on the form that sends messages to the controller (in my case). The messages are represented as a class and can have any information you need. This is how my message looks:
public class MvcMessage
{
public object Source { get; private set; }
public MessageType MessageType { get; private set; }
public Type EntityType { get; private set; }
public IList InvolvedItems { get; set; }
public int NumAffected { get; set; }
public EventArgs SourceEventArgs { get; internal set; }
/// <summary>
/// Name of property who changed its value. Applies to models implementing INotifyPropertyChanged.
/// </summary>
public string PropertyName { get; set; }
public MvcMessage(object source, MessageType messageType, Type entityType)
{
this.Source = source;
this.MessageType = messageType;
this.EntityType = entityType;
}
public void Reroute(Type newEntityType)
{
MvcMessage reroutedMessage = (MvcMessage)MemberwiseClone();
reroutedMessage.EntityType = newEntityType;
Controller.NotifyAll(reroutedMessage);
}
}
... where MessageType is a enum containing a lot of common commands and requests.
My IView interface then defines the event like this:
public delegate void ViewEventHandler(MvcMessage message);
public interface IView : IViewPage, IWin32Window
{
event ViewEventHandler ViewEvent;
...
}
You should go more along the lines of the two latter examples; the view shouldn't expose WinForm-ish details to the presenter. See this answer for details on handling exactly your problem with TreeView updating - especially item 5.

In MVP, should a form get the object it's going to display in the constructor?

It's my first attempt at MVP, so please be patient :)
In order to present a certain entity, I'm creating a view and a presenter.
I need to pass this entity to the view and the presenter somehow.
Is this the right way of doing it, in terms of passing around the DTO, or is there a smell?
class CustomerView
{
public CustomerView(IPersenterProvider presenterProvider,
CustomerDto customer)
{
_presenter = presenterProvider.ResolvePresenter<CustomerPresenter>(this);
_presenter.Customer = customer;
}
...
}
class CustomerPresenter
{
public CustomerDto Customer
{
set
{
// Show customer details in the View
}
}
...
}
I'm patient, really. You have a smell or two or three, indeed. The biggest one being the set-only property Customer on CustomerPresenter. The second one is that the view knows about the presenter - it really doesn't need to. The third one is that the view knows about the DTO on the same level as it knows about the presenter. It shouldn't - the presenter should tell the view what data to show.
This is one of my humble takes on MVP and your specific issues.
First, anything that a user can interact with, or just be shown, is a view. The laws, behavior and characteristics of such a view is described by an interface. That interface can be implemented using a WinForms UI, a console UI, a web UI or even no UI at all (usually when testing a presenter) - the concrete implementation just doesn't matter as long as it obeys the laws of its view interface.
Second, a view is always controlled by a presenter. The laws, behavior and characteristics of such a presenter is also described by an interface. That interface has no interest in the concrete view implementation as long as it obeys the laws of its view interface.
Third, since a presenter controls its view, to minimize dependencies there's really no gain in having the view knowing anything at all about its presenter. There's an agreed contract between the presenter and the view and that's stated by the view interface.
The implications of Third are:
The presenter doesn't have any methods that the view can call, but the view has events that the presenter can subscribe to.
The presenter knows its view. I prefer to accomplish this with constructor injection on the concrete presenter.
The view has no idea what presenter is controlling it; it'll just never be provided any presenter.
For your issue, the above could look like this in somewhat simplified code:
interface ICustomerView
{
event EventHandler EditCustomerDetails;
void Show(Customer customer);
}
class CustomerView : ICustomerView
{
Customer customer;
readonly Form form;
readonly Button editCustomerDetailsButton;
public event EventHandler EditCustomerDetails;
public CustomerView()
{
// UI initialization.
this.editCustomerDetailsButton.Click += delegate
{
var Handler = this.EditCustomerDetails;
if (Handler != null)
{
Handler(this, EventArgs.Empty);
}
};
}
public void Show(Customer customer)
{
if (this.form.Visible)
{
// Update UI with new data.
}
else
{
// Initialize UI with data and then show it.
this.form.ShowDialog();
}
}
}
interface ICustomerPresenter
{
void ShowView(ICustomer customer);
}
class CustomerPresenter : ICustomerPresenter
{
readonly ICustomerView view;
readonly IEditCustomerPresenter editPresenter;
ICustomer customer;
public ConfigurationPresenter(ICustomerView view, IEditCustomerPresenter editPresenter)
{
this.view = view;
this.view.EditCustomerDetails += delegate
{
this.editPresenter.ShowView(this.customer); // Edit
this.view.Show(this.customer); // Update
};
this.editPresenter = editPresenter;
}
public void ShowView(ICustomer customer)
{
this.customer = customer;
this.view.Show(customer); // Assuming modal
this.customer = null;
}
}
That's a rather simplistic view (sic!) on this matter, but it'll maybe provide some hints.

Categories

Resources