Updated 20090716.
Since I wrote my article about accessing properties in a strongly typed way EPiServer CMS5 R2 SP2 has been released wich offers way greater possibilities in this area. It is now possible to work with our own subtypes of PageData that can represent our EPiServer pagetypes, and eventually access their properties just like we´re doing with for example CurrentPage.PageName on PageData.
Create a PageData subtype to represent a EPiServer pagetype
Let´s say that in EPiServer I have a pagetype named "StartPage" with two properties, Heading and MainBody. I would then create a PageData subtype that looks like this.
Example of a PageData subtype (StartPageData)
1: public class StartPageData : PageData
2: {
3: public string Heading
4: {
5: get { return this["Heading"] as string; }
6: set { this["Heading"] = value; }
7: }
8:
9: public string MainBody
10: {
11: get { return this["MainBody"] as string; }
12: set { this["MainBody"] = value; }
13: }
14: }
Yes, there´s some “magic string” usage here wich I´m not a very big fan of but as it´s encapsulated I feel it´s no biggie. But if you wan´t to completely avoid using “magic strings” try these PageData extensionmethods for getting and setting property values instead.
1: public static TProperty GetProperty<TPageData, TProperty>(this TPageData pageData, Expression<Func<TPageData, TProperty>> expression) where TPageData : PageData
2: {
3: var methodExp = (MemberExpression)expression.Body;
4: return (TPageData)pageData[methodExp.Member.Name];
5: }
6:
7: public static void SetProperty<TPageData>(this TPageData pageData, Expression<Func<TPageData, object>> expression, object value) where TPageData : PageData
8: {
9: var methodExp = (MemberExpression)expression.Body;
10: pageData[methodExp.Member.Name] = value;
11: }
Example on using the GetProperty and SetProperty extensionmethods
1: public class StartPageData : PageData
2: {
3: public string Heading
4: {
5: get { return this.GetProperty(p => p.Heading); }
6: set { this.SetProperty(p => p.Heading, value); }
7: }
8:
9: public string MainBody
10: {
11: get { return this.GetProperty(p => p.MainBody); }
12: set { this.SetProperty(p => p.MainBody, value); }
13: }
14: }
Regardless of the chosen approach above the “StartPageData” type should now be ready for use.
Example on using the PageData subtype
1: var startPage = DataFactory.Instance.GetPage<StartPageData>(PageReference.StartPage);
2: LiteralMainBody.Text = startPage.MainBody;
Have CurrentPage as your desired type
Now that we have a way of creating our own PageData subtypes the next natural step would be to have CurrentPage as the desired type that correctly represents the current EPiServer pagetype. In this case CurrentPage should be of type StartPageData.
So we need to create generic versions of TemplatePage, SimplePage and UserControlBase. I provide example for TemplatePage, the others should look the same.
Code for TemplatePage<TPageData>
1: public class TemplatePage<TPageData> : TemplatePage where TPageData : PageData, new()
2: {
3: public new TPageData CurrentPage
4: {
5: get
6: {
7: return DataFactory.Instance.GetPage<TPageData>(base.CurrentPage.PageLink, new LanguageSelector(base.CurrentPage.LanguageBranch));
8: }
9: }
10: }
Example on using the generic TemplatePage class with the new CurrentPage property.
1: public partial class StartPage : TemplatePage<StartPageData>
2: {
3: protected override void OnLoad(System.EventArgs e)
4: {
5: LiteralMainBody.Text = CurrentPage.MainBody;
6: }
7: }
PageType building
This article is not about pagetype building but for those of you who had that in mind while reading might have thought of the property types in the StartPageData example. That none of them used the PropertyData types but rather the final output type, for example MainBody has string instead of PropertyXhtml. This is obviously done because of greater usability of the PageData subtypes, no casting, ToString() or the like needed for getting the values.
For PageType building however the correct PropertyData type need to be provided aswell so the builder knows if MainBody should be a PropertyString or a PropertyXhtml. One approach of solving this is to decorate the properties with attributes, as Joel Abrahamsson also shows in his Strongly typed property access and page type inheritance – again article.
Example of a PageData subtype with attributes.
1: public class StartPageData : PageData
2: {
3: [PropertyDataType((typeof(PropertyString)))]
4: public string Heading
5: {
6: get { return this["Heading"] as string; }
7: set { this["Heading"] = value; }
8: }
9:
10: [PropertyDataType((typeof(PropertyXhtmlString)))]
11: public string MainBody
12: {
13: get { return this["MainBody"] as string; }
14: set { this["MainBody"] = value; }
15: }
16: }
Code for PropertyDataType attribute
1: public class PropertyDataTypeAttribute : Attribute
2: {
3: public Type PropertyDataType { get; set; }
4:
5: public PropertyDataTypeAttribute(Type propertyDataType)
6: {
7: PropertyDataType = propertyDataType;
8: }
9: }
Conclusion
I´m sure there are other approaches on working with these new features in SP2 but that´s what I´ve come up with, for now :) I hope you find it useful.
All feedback is much appreciated!