Question

Edit : Accepted Chris Holmes response, but always ready to refactor if someone come up with a better way! Thanks!

Doing some winforms with MVP what is the best way to pass an entity to another view.

Let say I have a CustomerSearchView/Presenter, on doubleClick I want to show the CustomerEditView/Presenter. I don't want my view to know about the model, so I can't create a ctor that take an ICustomer in parameters.

my reflex would be,

CustomerSearchView create a new CustomerEditView, which create it's own presenter. Then my CustomerSearchView would do something like :

var customerEditView = new CustomerEditView();
customerEditView.Presenter.Customer = this.Presenter.SelectedCustomer;

Other possible approach would be a CustomerDTO class, and make a CustomerEditView that accept one of those CustomerDTO, but I think it's a lot of work something simple.

Sorry for basic question but all example I can find never reach that point, and it's a brownfield project, and the approach used so far is giving me headache...

Was it helpful?

Solution

I don't know exactly how you are showing your views, so it's a bit difficult to give you specific advice here. This is how I've done this sort of thing before:

What we did was have the CustomerSearchViewPresenter fire an event like OpenCustomer(customerId). (That is assuming that your search view only has a few pieces of Customer data and the customerId would be one of them. If your search view has entire Customer objects listed then you could call OpenCustomer(customer). But I wouldn't build a search view and allow it to populate with entire objects... We keep our search views lightweight in terms of data.)

Somewhere else in the application is an event handler that listens for the OpenCustomer() event and performs the task of creating a new CustomerEditView w/ Presenter (and I'm going to defer to my IoC container do this stuff for me, so I don't have to use the "new" keyword anywhere). Once the view is created we can pass along the id (or customer object) to the new CustomerEditView and then show it.

This class that is responsible for listing the OpenCustomer() event and performs the creation of the CustomerEditView is typically some sort of Controller class in our app.

To further simplify this situation, I've done this another way: I create both the CustomerSearchView (& presenter) and CustomerEditView (& presenter) when the application or module starts up. When the CustomerSearchView needs to open a Customer for editing, the CustomerEditView becomes the responder to the OpenCustomer event and loads the data into itself, and knows how to show itself in whatever container it is supposed to do.

So there's multiple ways to do this.

OTHER TIPS

How about:

//In CustomerSearchPresenter
var presenter = new CustomerEditPresenter();
var customerEditView = new CustomerEditView(presenter);
presenter.SetCustomer(customer);

//In CustomerEditPresenter
public void SetCustomer(customer)
{
    View.Name = customer.Name;
    View.Id = customer.Id;
    ...
}

In think your customer search view should just delegate to its presenter you need to have an action execute.

There are a couple of crucial insights to get a natural flow in any MVP code:

  1. It's the presenter that drives the view, not the other way around.
  2. Because of 1. the view need not know about the presenter's existence. Less dependencies usually means easier maintenance.

In C#, I find events being a great asset when decoupling presenters from views. More details in a previous answer: Model-View-Presenter in WinForms

  1. I would look at MS Prism 4, and their nice Navigation interface. Also look at Silverlight and WCF Navigation. They are well done and handle things like prompting the user for confirmation from "dirty" forms, with cancellation.

  2. I would look at the PageFunction() documentation in WCF as well, for how to "call" a page from another, and get back info.

Here's how it works (javascript, sorry):

User double-clicks customer on customer list:

CustomerList.onDblClick(customerId){

  app.fireEvent('customerEditRequest', id)

}

...

app.onCustomerEditRequest(id){
  this.mainRegion.requestNavigate('customers/edit', id);
}

If navigation to edit view was successful...

CustomerEditView.onNavigatedTo(context){
  this.model.load(context.parameters.id));
}

CustomerEditView.onSaveButtonClick(){
  this.model.save();
  app.fireEvent('customerEdited', id);
}

...

app.onCustomerEdited(id){
  app.mainRegion.requestNavigate('customerlist', id);
}

There are a few different ways you could do it:

  1. send a callback function to the edit form, from the customer list. edit form will call it, and you do what you want when it's called.

  2. have the edit form raise on "customerEdited" event that you listen to and react to (no app-wide bus)

  3. use an application-wide Event Bus to manage the events centrally, shown.

I used to have my views communicate with their presenters, but have moved away from that. It doesn't conform to the original definition of a pattern (not a reason in itself for deviating just a contributing factor to exact those benefits). Views ideally should be kept as dumb and with as few dependencies as possible. View should communicate w/ Presenter (any "observers") via delegates/events/some "fire-and-forget" mechanism. As a matter of fact, I've introduced a controller into MVP specifically to intercept View events and either re-fire to presenter (rarely) to communite w/ Presenter, or to communicate with a system or Presenter-specific event bus - enabling me to change user action alerting mechanisms w/out touching the view. Have to be careful with an event bus though; pretty soon you start throwing all events in there, app gets chatty/bogged down in handling events, and events aren't the fastest things in .Net. Sunchronization is an added concern, esp if ur app need to have a more "conversational" interaction with your user.

Should bear in mind that although Presenter is usu view/process-specific, views (and view-models) can be reused; having the View in a containment/delegation relationship with the Presenter strongly couples View/limits its reuse. This could be reduced by some DI, but I find DI containers to be unnecessary complexity in most cases (since I have to know how to create objects anyway and how often do you change out an object for another semantically similar one after creating/testing it?). Concrete dependency goes nowhere except another layer/adds more obscurity/makes things more difficult to debug/trace. Been on a "simplicity" kick lately though, and mostly prefer to do my on Factory/object creations/ORM mappings for most apps, since there's usu a "1-to-1" btw db tables/entities and n need for the added complexity of a generic 3rd-party ORM tool that by taht generic context/needing to serve different apps has to make things harder than they need to be, even if u understand how they work (not the point).

Moreover, it's still quite feasible for View to observe Model in MVP (as in MVC), so I wouldn't be so quick to rule this out. I don't prefer to do this myself, but it' doesn't "break" the pattern. Matter of fact, I developed something similar to MVP about a decade ago because I didnt like the "circular loop" btw the MVC components (View knowing about Model); I preferred to have the cleaner separation btw View and Model that all these patterns (including MVC) professed, as well as a desire to keep View as dumb as possible (observing Model woujld mean View would need more intelligence to process Model changes). What I ended up doing was something like MVVM and strategy patter, where I used "substructures" of the model to pass in to the View, serving as "change notifiers". This kept everything view purpose-specific and flexible/reusable (tough combo).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top