MVP Question

Topics: Prism v1, Prism v2 - Silverlight 2, Prism v2 - Silverlight 3, Prism v2 - WPF 3.5
Sep 17, 2009 at 4:10 PM

When designing a MVP Solution, should the view hold a reference to the model?  or should it hold a reference to the presenter, which exposes the model as a public property?

in other words, which is the better practice?:

 

public partial class ProjectsListView : UserControl, IProjectsListView
    {
        public ProjectsListView()
        {
            InitializeComponent();
        }
        public ProjectsListPresentationModel Model
        {
            get { return this.DataContext as ProjectsListPresentationModel; }
            set { this.DataContext = value; }
        }
    }

 

public partial class MyView : UserControl, IMyView
    {
        public MyView()
        {
            InitializeComponent();
        }

        public MyPresentationModel Model
        {
            get { return this.DataContext as MyPresentationModel; }
            set { this.DataContext = value; }
        }
    }

or
public partial class MyView : UserControl, IMyView
    {
        private IMyViewPresenter presenter;
        public MyView(IMyViewPresenter presenter)
        {
            InitializeComponent();
            this.presenter = presenter;
            this.DataContext = this.presenter.Model
        }

    }

public class MyViewPresenter : IMyViewPresenter
{

   ...
   public IMyPresentationModel Model {get;set;}
   
}

Thanks.

 

 

 

Sep 17, 2009 at 5:37 PM

When combining MVP and MVVM I would suggest a method I gleened from PRISM V2 Drop 7 (topdown composition quickstart).   Your View actually holds neither a reference to the model nor the presenter; it's totally decoupled.   In this case the presenter is responsible for resolving the view - thus you'll find code samples that look as follows:

    regionManager.RegisterViewWithRegion(RegionConstants.StatusBarRegion,
        () => this.container.Resolve<StatusBarPresenter>().View);

The Presenter is being resolved and the resulting View registered with a particular region.

I am working on an infrastructure at http://MultiTargeting.CodePlex.com which is designed to simplify the infrastructure/wireup process - for example the following is the minimum required code to wire up a presenter to it's view and model:

namespace Module.Main.Views.StatusBar
{
    /// <summary>
    /// Status Bar Presenter
    /// </summary>
    public class StatusBarPresenter : PresenterBase<IStatusBarView>
   
{
        /// <summary>
        /// Initializes a new instance of the <see cref="StatusBarPresenter"/> class.
        /// </summary>
        /// <param name="view">The view.</param>
        /// <param name="model">The model.</param>
        /// <param name="container">The container.</param>
        public StatusBarPresenter(
            IStatusBarView view, IDemoPresentationModel model, IUnityContainer container)
            : base(view, model, container)
        {
            // Minimum required code to wire-up view/presenter
        }
    }
}

The above code works for the WPF, Silverlight and even WinForm applications the MultiTargeting project works with (all code is shared with exception of Views).   The following is the PresenterBase<T> baseclass that does the wiring up:

public PresenterBase(IView view, IPresentationModel model, IUnityContainer container)
{
    try
    {
        // For logging purposes
        ModuleName = GetType().FullName;

        // Setter injection is not available until after the constructor
        // so we'll set them manually
        Error = container.Resolve<IError>();
        Logger = container.Resolve<ILoggerFacade>();

        // Set the presenter with a reference to the container
        Container = container;

        // Set the Presenter with a reference to the model
        Model = model;

        if ((TView)view is UserControl)
        {
#if !WinForm
            // WPF/Silverlight have SizeChanged event
            // Provide means to notify User Controls that their size has been set
            ((TView)view).SizeChanged += (object sender, SizeChangedEventArgs e) =>
                {
                    OnViewSizeSet(sender, e);
                };
#endif
        }


        // Set the Presenter with a reference to the view
        View = (TView)view;

        // Subscribe to all Button Click events.  The main view (MappingView)
        // will bubble all button click events (see MappingView code-behind)
        container.Resolve<IEventAggregator>()
            .GetEvent<ClickEvent>().Subscribe(OnButtonClickEventHandler, true);

        // Subscribe to any Data Access Layer event changes
        container.Resolve<IEventAggregator>()
            .GetEvent<DALEvent>().Subscribe(OnDALEventHandler, true);

        // Process controls on view 
        IControlProcessor controlProcessor = container.Resolve<IControlProcessor>();
        controlProcessor.ProcessControlCollection(View);

        // Set Unity container in View - this will allow us
        // to gain access to the container if all we have is
        // a reference to the view
        View.Container = container;

        // For the XAML forms (WPF and Silverlight) this sets the databinding
        // to the Presentation Model / View Model
        View.DataContext = model;

    }
    catch (Exception ex)
    {
        Error.SetError(ex);
    }
}