Thursday, October 28, 2010

Model View Presenter Pattern with Sharepoint Service Locator

After playing around with the Sharepoint 2010 Guidance Package, I decided to implement a MVP pattern within a WebPart using the ServiceLocator component. Let’s start by creating a new Visual WebPart with the according project template in Visual Studio 2010.

This template is a real improvement and generates all components necessary for a Visual WebPart, especially an ASP.NET user control (ASCX), which can be designed using the visual editor in Visual Studio. The following image shows the project structure after executing the template:

The project contains nearly all elements we need for MVP. The WebPart class (VisualWebPart1) will become our model. Actually it’s more kind of a controller and initializes the view and also provides access to data. Of course, the UserControl will be the view. So all we need additionally is the Presenter and some interfaces. Let’s create the interfaces first. We create one for the WebPart class. In the example the interface provides just one method called GetNames, which returns a List of strings just for testing:
public interface IVisualWebPart1
{
    System.Collections.Generic.IList<string> GetNames();
}

We also need an interface for the View, so that the Presenter can call the appropriate methods on it without knowing the concrete implementation. In the example the View has a Fill method, which accepts a list of strings:
public interface IVisualWebPart1UserControl
{
    void Fill(System.Collections.Generic.IList<string> names);
}

With that interfaces we can now define our Presenter. I will create a generic base class for the Presenter, which can be reused in other projects:
public class Presenter<IView, IModel>
{
    public Presenter()
    {
        IServiceLocator serviceLocator = SharePointServiceLocator.GetCurrent();
        Model = serviceLocator.GetInstance<IModel>();
    }

    public Presenter(SPSite site)
    {
        if (site != null)
        {
            IServiceLocator serviceLocator =
SharePointServiceLocator.GetCurrent(site);
            Model = serviceLocator.GetInstance<IModel>();
        }
    }

    protected IModel Model { get; set; }

    public IView View { get; set; }

    public virtual void OnViewInitialized() { }

    public virtual void OnViewLoaded() { }
}

The Presenter is the connection between the Model and the View. It expects to get the Model by using the Sharepoint Service Locator. The View property will be set by the View itself upon its creation. The derived Presenter in the example (VisualWebPart1Presenter) uses IVisualWebPart1 as Model and IVisualWebPart1UserControl as View. It overrides OnViewInitialized and calls the Fill method of the View with the list provided by the Model. So all we need to do know is to create the Presenter in the View and call its OnViewInitialized method. The View gets a property of type VisualWebPart1Presenter:
VisualWebPartMVPPresenter _presenter;
VisualWebPartMVPPresenter Presenter
{
    get
    {
         if (_presenter == null)
         {
            _presenter = new VisualWebPartMVPPresenter(SPContext.Current.Site);
            _presenter.View = this;
        }
        return _presenter;
    }
}

We’re nearly done. The last thing to do is to register the Model with Sharepoint Service Locator. As said in my previous post, I will use a feature receiver to do that. A right click on Feature1.Feature opens the context menu and there you find an option to add an event receiver. Visual Studio creates a class derived from SPFeatureReceiver and also adds the important method overrides which you only need to uncomment. Within the FeatureInstalled method add the following code:
using (SPSite site = new SPSite("Add your site URL"))
{
    IServiceLocator serviceLocator = SharePointServiceLocator.GetCurrent();
    IServiceLocatorConfig typeMappings =
serviceLocator.GetInstance<IServiceLocatorConfig>();
    typeMappings.RegisterTypeMapping<VisualWebPart.IVisualWebPart1,
VisualWebPart.VisualWebPart1>();
}

Now we can give it a try. Deploy the WebPart and add it to the WebPart gallery within Sharepoint. When you place it on a site you should be able to see the names list from the model class inside a list box.

The sample project can be downloaded here.

10 comments:

  1. Very nice representation of MVP pattern.

    However the download link seems to be broken.

    Florin

    ReplyDelete
  2. Fixed the download link. Thanks for the hint.

    ReplyDelete
  3. I would recommend that you comment all methods and explain the code better, so far the only example I have found with MVP and visual webparts is yours.

    The other examples on the web are with normal webparts, and for other kind of applications.

    I had to read the SP guidance and try to match what you did to what they did so that I could understand how this works.

    This is very important to me because I am planning to use it in all future sp 2010 projects.

    ReplyDelete
    Replies
    1. Dude your comment sounds much like a complaint and I think you should be thankful instead.

      BTW, thanks Daniel for this great post.

      Delete
  4. I downloaded your sample project:
    The file "VisualWebPartMVPUserControl.ascx" inherits "VisualWebPartMVP.VisualWebPartMVP.VisualWebPartMVPUserControl", but it can't be found according to the error message when debugging. If I delete the inherit-part, the Visual WebPart shows nothing but it should show the names list. I performed trial & error in order to find the right file to inherit but I keep getting the same error message.

    This shouldn't be a complaint but a request for help. Thank you for your answer and your work so far.

    ReplyDelete
    Replies
    1. This is likely to be a problem caused by the development environment. This remembers me of other projects having the same issue. You may try to add the 'src' attribute to the ascx file. If this won't help, I know no other way than to recreate the whole view with the Visual Studio template or even creating a complete new project and copying the code over there.

      Delete
  5. I don't see which part of the project is meant by "VisualWebPartMVP.VisualWebPartMVP.VisualWebPartMVPUserControl". From which file should "VisualWebPartMVPUserControl.ascx" (which means the View) inherit? Adding the src attribute unfortunately didn't work. Which version of Visual Studio did you use in order to create this solution? I already recreated the view but this also didn't work.

    Thank you again!

    ReplyDelete
    Replies
    1. The ascx file needs to inherit an ASP.NET page or user control. In the sample project it inherits VisualWebPartMVPUserContorl.ascx.cs. The same file that is specified by the CodeBehind attribute. I had a look into the source and maybe the namespace is not correct. There is one VisualWebPartMVP too much.

      BTW: The solution was created with Vosual Studio 2010 Premium.

      Delete
  6. Unfortunately, it still doesn't work. I still get a HttpParseException stating that "VisualWebPartMVP.VisualWebPartMVPUserControl" could not be found. I again tried many possibilities for the inherits-reference but non of them worked.

    Thank you again very much for your help!

    ReplyDelete
  7. This means, I can deploy the solution but not view the Visual WebPart. Can you view the Visual WebPart?

    Thanks for your reply, your work and your help!

    ReplyDelete