Some of the most difficult aspects of consuming PortableAreas (PAs) are MasterPages and ContentPlaceHolders. The embedded views of a PA are hard coded to use Site.Master, which may be inconsistent with the host application. It works, but it can be much better. After some discussion on twitter with Eric Hexter and Steve Michelotti, I had an idea for solving the problem. I’ll soon submit a pull request.
Host Application – Wiring Things Up
In your application, you surely have your own MasterPage. That MasterPage has ContentPlaceHolders, probably with IDs that conform to the coding standards of your particular project. The goal is to use a PA, and make it feel like a seamless part of your application. To do that, you need some way to tie everything together.
Hypothetically, you wish to utilize “SomePortableArea” with your application. Inside of your Global.asax.cs, during Application_Start() you can map the PA to your MasterPage and its ContentPlaceHolders like this:
Content.Map<SomePortableAreaMap>()
.Master("~/Views/Shared/MyApplication.master")
.Title("PageTitle")
.Body("BodyContent");
AreaRegistration.RegisterAllAreas();
The Content static class has a Map<>() method that returns an instance for the given map type. The type you provide depends on the PA you’re working with. The imaginary SomePortableArea.dll has a class called SomePortableAreaMap. Thus, the example calls Map<SomePortableAreaMap>() to get the map.
PortableAreaMap has three extension methods:
| Master() | - Tells the PortableArea what MasterPage to use. |
| Title() | - Tells the PortableArea what the Title ContentPlaceHolderID is in the MasterPage. |
| Body() | - Tells the PortableArea what the Body ContentPlaceHolderID is in the MasterPage. |
*In order for the mappings to work, they must be done before the areas are registered.
Here is a look at the markup in “MyApplication.Master”:
Creating PortableAreas – Providing a Map Class
As the author of a PortableArea, now you can feel confident that people won’t send nasty grams about how your PA ruined their conventions! Even more good news: it’s not that difficult for you to implement. Imagine that you’re the author of “SomePortableArea.” You would define a class like this:
public class SomePortableAreaMap : PortableAreaMap
{
public SomePortableAreaMap()
{
DefaultMasterPageLocation = "~/Views/Shared/Site.Master";
DefaultBodyID = "MainContent";
DefaultTitleID = "TitleContent";
}
}
Inherit from PortableAreaMap, and provide some default values in the constructor. These default values come from the MasterPage that you have in your project (mostly for intellisense & View generation reasons):
There is just one part left, and that is to override a new method in SomePortableAreaRegistration:
public class SomePortableAreaRegistration : PortableAreaRegistration
{
// ... other code
public override PortableAreaMap GetMap()
{
return Content.Map<SomePortableAreaMap>();
}
// ... other code
}
This just grabs the Map for your portable area, using the SomePortableArea type defined earlier.
Extensibility
One way that you might be able to extend the portable area is to use another <asp:Content> tag in your views, perhaps ContentPlaceHolderID="JavaScriptContent" to make sure all of your area’s JavaScript gets rendered to the bottom of the page. You could then add a method to SomePortableAreaMap, allowing the host to map it to one of their ContentPlaceHolders:
public SomePortableAreaMap JavaScript(string name)
{
Add("JavaScriptContent", name);
return this;
}
Summary
Host applications are able to configure a PA to use their MasterPages by calling chainable methods.
PortableAreas provide a PortableAreaMap, making the former possible. By providing a map class, the PA can more easily support another ContentPlaceHolder (i.e. for JavaScript). Then, the consuming application can create a special MasterPage, or nest MasterPages to accommodate the PA, all the while retaining their freedom of conventions.
Hopefully this is easy to understand. The steps seem fairly intuitive, and simple to me (but then again, I’m biased… after all, I wrote it!). What do you think?