Page Type Tool - Strongly Typed Properties

by: Fredrik Tjärnberg

There has been quite some buzz in the “EPiSphere” around the property framework lately. It turns out that a lot of smart people has spent time on finding the way to strongly typed properties in EPiServer…. and so have I.

Back in March I posted the first version of a visual studio extension for building up your page type structure from within the Visual Studio environment (aka the Page Type Tool). Now, when SP2 is soon to be released (with a RC available for testing) it’s time for a “refresh” of the Page Type Tool (PTT) that takes the code generation one step further. SP2 has a couple of small but useful API changes in PageData and DataFactory that makes it possible to work with your own PageData classes. The PTT leverages these changes to generate and work with strongly typed PageData classes.

The first version of PTT exposed the EPiServer properties as CLR properties in the page template implementation. This is very useful in some cases but does not give you much when writing code outside the page context or when working with other pages than the “current page”.

The new Page Type Tool by a short example.

Let’s assume we add the following .pagetypes file named “/Page Types/Demo.pagetypes” to an EPiServer project

<?xml version="1.0" encoding="utf-8" ?>
<PageTypes xmlns="http://schema.episerver.com/PageTypeSchema.xsd">
<PageType Name="Base" IsAbstract="true">
<Properties>
<Property Name="Heading" Type="String" />
<Property Name="MainBody" Type="XhtmlString" />
<Property Name="MainIntro" Type="String" />
</Properties>
<PageTypes>
<PageType Name="Standard" />
<PageType Name="News" />
<PageType Name="Event" />
<PageType Name="ListingPage" IsAbstract="true">
<Properties>
<Property Name="ListingRoot" Type="PageReference" />
<Property Name="ListingCount" Type="Number" />
</Properties>
<PageTypes>
<PageType Name="EventList" />
<PageType Name="NewsList" />
</PageTypes>
</PageType>
</PageTypes>
</PageType>
</PageTypes>

(Users of v1 of this tool will notice that the xml schema has changed slightly (to the better I hope)).

With the file above the tool will add a Standard, News, Event, EventList and NewsList page type to the database. It will also add default page templates for those page types. See solution explorer below.

image

This is pretty much the same thing as you got in V1 but if we examine the the page templates you will find that the generated code is a bit different. Here’s what the code behind of the Standard.aspx page looks like.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace EPiServerProject.Templates
{
public partial class Standard : EPiServer.TypedPageBase<EPiServerProject.PageTypes.Demo.StandardPageData>
{
protected void Page_Load(object sender, EventArgs e)
{

}
}
}

As you can see, the code behind derives from a generic class EPiServer.TypedPageBase which is a new type in EPiServer that derives from PageBase i.e a page template base class. The generic parameter is a PageData deriving class. This is the class where the EPiServer properties will be exposed as strongly typed properties, in this case EPiServerProject.PageTypes.Demo.StandardPageData. StandardPageData is generated by the tool and will be found in Demo.cs. Note that the namespace of the generated PageData class will follow standard naming conventions of a C# project.

To use the strongly typed properties you retreive the typed PageData instance from the CurrentTypedPage property of TypedPageBase. From there you can then dereference all your properties in a variety of ways.

protected void Page_Load(object sender, EventArgs e)
{
if (CurrentTypedPage.MainBodyIsNull)
{
StandardPageData copy = CurrentTypedPage.CreateWritableClone();
copy.MainBody = "<p>Auto content</p>";
DataFactory.Instance.Save(copy, EPiServer.DataAccess.SaveAction.Publish);
}
}

The above example is kept short for clarity. It shows a few of the features of the generated PageData classes. In addition to being able to directly access MainBody, you can access MainBodyIsNull, MainBodyIsNullOrEmpty and MainBodyProperty. The meaning of the first three should be obvious, the last one is a strongly typed link to the underlying PropertyData object. In this case a EPiServer.SpecializedProperties.PropertyXhtmlString instance. In the example we also use an “overload” of CreateWriteableClone that returns a StandardPageData instance (as opposed to the normal PageData). With this instance it’s only a matter of assigning a value to any of the properties and having it saved by DataFactory.

Working with arbitrary pages

What if you write code that deals with other pages than the “current page’”? This is enabled by a few generic overloads of DataFactory.GetPage and DataFactory.GetChildren that will be able return page data instances of your choice. If you construct your page type hierarchy in a clever way where you try to centralize most of the properties to base classes you can enjoy strongly typed child page enumerations.

var children = 
DataFactory.Instance.GetChildren<BasePageData>(PageReference.StartPage);

children is now an IEnumerable<BasePageData> of child pages to the start page. This gives a pretty slick Linq experience where you can use the strongly typed properties inside your lambda expressions when doing all kinds of crazy linq-stuff.

var children = 
DataFactory.Instance.GetChildren<BasePageData>(PageReference.StartPage);

var childrenWithIntro =
from c in children
where !c.MainIntroIsNullOrEmpty
select new { c.LinkURL, c.MainIntro };

Easy to read, easy to understand and a breeze to write.

Download and installation

In order to test this you will need the R2 SP2 RC. This version of the tool will only work with R2 SP2 and later. After installing R2 SP2 (with or without the VS integration) you can go ahead and download the Page Type Tool installer. Before you continue with the installation, make sure that you uninstall V1 of the tool if present. Launch the downloaded installer and you will soon have a Page Type Schema extension in your add new item dialog of VS.

Filename

>> PageTypeTool.Setup.msi 66Kb 2009-05-23


Fill in the form to receive a mail with download information.



Name  
Email  
Company  

 

Feedback is greatly appreciated

Enjoy

Note: I have updated the download to support the final version of R2 SP2.

23 May 2009


Comments

  1. This is really great, thank you for sharing!
  2. Another step in the right direction, especially with the new DataFactory methods. Don't like the "CurrentTypedPage" property name though. Sounds odd to me. Complexity is increasing with each EPiServer version, and if this is supposed to go into the core, we really need to make sure it looks simple, clean and understandable to new developers.
  3. Btw. commenting here is a pain, when you use the download dynamic content on the same page. Validation is not separated for the two input forms.
  4. Thanks great post
  5. Good point(s). Actually, before the code review of the DataFactory change, the signature was

    public new TPageData CurrentPage { get; }

    My point was that no one would ever want just the PageData instance if a strongly typed one existed. But, I agree with the reviewer, to some degree, that hiding the original CurrentPage property was a bit agressive. Please, find me a better name before SP2 goes out the door.

    I will talk to Jacob about the form interference stuff.

    Again, thanks for the input.

  6. Fixed it
  7. Regarding the strongly typed PageData, how can I create a new PageData of my own type from code? The GetDefaultPageData does not take a generic type.
  8. You do it by using the copy constructor of the strongly typed PageData classes.

    var newsPage = new NewsPageData(
          DataFactory.Instance.GetDefaultPageData(parent, pageTypeId));

    This is of course a place where there is room for improvement. The generated classes could provide methods for creating default instances them self or as you say, provide generic overloads for GetDefaultData in DataFactory.

  9. Thanks, that did the trick.
  10. I agree with Steve, the CurrentTypedPage is a bit odd. I actually prefer the "new construct" since why have 2 properties that do the same thingy. Maybe we should remove the TypedPageBase from SP2 and play with different constructs in your already generated cs-files instead to give us some time (since TypedPageBase does not have to be part of the Epi core).
  11. Good feedback, all of it sounds reasonable.

    To sum it up:

    • TypedPageBase will be removed (maybe temporarily) from core, and I will probably rename it to PageBase. The generic argument will distinguish it from EPiServer.PageBase.
    • I revert the signature of CurrentTypedPage to be named CurrentPage again.
    • I would also like to do something about the GetDefaultPageData "thing" since it's a quite common API.

    Anything else before I get started? ;)

  12. Just have to say I agree about the changes you have done. Moving the concept of page types and properties in a new direction is a tricky and complex at best. This (and the other great blog articles from Joel, Daniel and Mikael) is a great start. Looking forward to what people comes up with next.
Post a comment    
User verification Image for user verification  
EPiTrace logger