Complex SharePoint Web Part Properties
Complex SharePoint Web Part Properties
Web part properties not always consist of strings and integers. Sometimes your properties are a little bit more complicated. And sometimes your property is a list of complicated properties. Such as a list of Virtual Earth pushpins for your Virtual Earth Maps control. This post is the starting post for my series "How to create a Virtual Earth Maps Web Part for SharePoint" in which I'll demonstrate how you can make modifying complex properties simple.
First things first
I'll start by laying the groundwork for our Virtual Earth Maps web part by creating a Virtual Earth Pushpin class and a Virtual Earth class. The latter will inherit from Microsoft.SharePoint.WebPartPages.WebPart. I know that we should not inherit from that class anymore but later on in this series I'll show you a very good reason why we still use this class.
These two classes form the basis for everything else. From an object point of view it’s easy to understand that our Virtual Earth class must have some sort of property that contains the list of Virtual Earth Pushpins. From a designers point of view it's kind of difficult to implement a good user interface to enter these pushpins. But as said. First things first. Here's the code for a Virtual Earth Pushpin:
//-----------------------------------------------------------------------
// <copyright file="VirtualEarthPushpin.cs" company="motion10">
// Copyright (c) motion10. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
using System;
namespace Motion10.SharePoint2007.WebControls.WebParts { public class VirtualEarthPushpin { private static readonly VirtualEarthPushpin _motion10 = new VirtualEarthPushpin() { Title = "motion10",
InfoBoxHtml = "<div style=\'text-align:center\'><p><strong>Virtual Earth Feature</strong><br/>designed by <a href=\'http://www.motion10.com\' target=\'_blank\'>motion10</a></p></div>',
Latitude = 51.913779F,
Longitude = 4.53926F
};
private float _latitude;
private float _longitude;
/// <summary>
/// Initializes a new instance of the <see cref="VirtualEarthPushpin"/> class.
/// </summary>
public VirtualEarthPushpin() { }
/// <summary>
/// Gets the motion10 location pushpin.
/// </summary>
/// <value>The motion10 location pushpin.</value>
public static VirtualEarthPushpin Motion10 { get { return _motion10;
}
}
/// <summary>
/// Gets or sets the title.
/// </summary>
/// <value>The title.</value>
public string Title { get; set; }
/// <summary>
/// Gets or sets the description.
/// </summary>
/// <value>The description.</value>
public string InfoBoxHtml { get; set; }
/// <summary>
/// Gets or sets the latitude.
/// </summary>
/// <value>The latitude.</value>
/// <remarks>Latidude can't be less than -90 or more than 90.</remarks>
/// <exception cref="T:System.ArgumentOutOfRangeException">If the value is less than -180 or exceeds 180.</exception>
public float Latitude { get { return _latitude;
}
set { if (value < -90F || value > 90F) { throw new ArgumentOutOfRangeException("value", "Latidude can't be less than -90 or more than 90."); }
_latitude = value;
}
}
/// <summary>
/// Gets or sets the longitude.
/// </summary>
/// <value>The longitude.</value>
/// <remarks>Longitude can't be less than -180 or more than 180.</remarks>
/// <exception cref="T:System.ArgumentOutOfRangeException">If the value is less than -180 or exceeds 180.</exception>
public float Longitude { get { return _longitude;
}
set { if (value < -180F || value > 180F) { throw new ArgumentOutOfRangeException("value", "Longitude can't be less than -180 or more than 180."); }
_longitude = value;
}
}
/// <summary>
/// Gets or sets the image.
/// </summary>
/// <value>The image.</value>
public string Image { get; set; } }
}
As you can see this class is really really straightforward. It implements some properties, some validation and, to promote my employer, a static property with the title, info box html, latitude and longitude of motion10. Nothing spectacular right? So let's concentrate on our topic of today. How to add a complex property to a web part and how to enable the end user to insert values?
Human readable and writeable object notation
There are many ways to enable modifications of complex properties, but they're all kind of pain in the *&$# to implement. So I was looking for some sort of object notation that is human readable and writeable and validateable. And the one thing that popped into my mind was JSON. JSON stands for JavaScript Object Notation, is very lightweight, human readable and writeable and validateable.
It's easy validateable because of the JavaScriptSerializer class in the .Net framework. With the help of an extension method it's even simpler to implement use JSON. My hero Scott Guthrie wrote an excelent post on how to create such an extension method.
Implementation
To implement all this we first need a class with a private member variable of generic type List<VirtualEarthPushpin>. This list will hold all of our pushpins for us. SharePoint will Serialize this member to and from the database. It looks like this:
/// <summary>
/// The motion10 VirtualEarth web part displays a Virtual Earth control.
/// </summary>
[Guid("DEC5B663-4E6B-4e56-99F1-C941C5AE81DD")][XmlRoot(Namespace = "Motion10.SharePoint2007.WebControls.WebParts")]
public class VirtualEarth : Microsoft.SharePoint.WebPartPages.WebPart, IListConsumer, IRowConsumer, IDesignTimeHtmlProvider { private static readonly string designTimeHtml = "<div style='border: solid 1px black;'><h4>motion10 VirtualEarth web part</h4><p>The VirtualEarth web part cannot render a Virtual Earth maps control in design view due the fact it needs to render and run some javascript.</p><p>You can edit its properties however without any problem. Any changes you make will be reflected in runtime.</p></div>';
private List<VirtualEarthPushpin> _pushpins = new List<VirtualEarthPushpin>(){ VirtualEarthPushpin.Motion10,
new VirtualEarthPushpin(){ Title="Microsoft Netherlands", Latitude=52.304541F, Longitude= 4.750958F} };
As you can see I've added to two pushpins and I did that for a very good reason. Not just to promote my employer but to promote Microsoft as well. No, just kidding, I did that to show my end user an example of the JSON he can use in the Pushpins property of type string. Yep, I'm serious. We are going to use a property of type string to define a complex property! Our Pushpins property looks like this:
/// <summary>
/// Gets or sets the pushpins.
/// </summary>
/// <value>The pushpins.</value>
[Browsable(true)]
[Category("Map Settings")][WebPartStorage(Storage.Shared)]
[FriendlyName("Pushpins")][Description("The pushpins to place on the map in Json Format")]public string Pushpins { get { return _pushpins.ToJson();
}
set { if (string.IsNullOrEmpty(value)) { value = "[]";
}
try { _pushpins = new JavaScriptSerializer().Deserialize<List<VirtualEarthPushpin>>(value);
}
catch (Exception) { throw new WebPartPageUserException("could not deserialize the given string into an array of pushpins."); }
}
}
The code is not that complicated at all. We use the "List<VirtualEarthPushpin> _pushpins" variable as a backing field. In the 'getter' we simply return _pushpins.ToJson(); which in turn uses the extension method described by mister Guthrie. The setter does a little bit more.
First it checks if the value is null or empty and if so turns it into "[]" which stands for an empty array in JSON. Then we are going to try to deserialize the given value to a List. If this does not succeed and throws an error, we are catching that exception and throw a WebPartPageUserException.
This is a call to all web part developers around the world. Start validation your property input and stop using an error label to show property validation errors! The WebPartPageUserException will stop SharePoint from serializing the invalid value to your database and will display a decent validation error to your end users. It's made for this. An errorlabel does have it's place, but not for property validation!
Now that's off my chest, I can show you some screenshots on how this will look in your browser:

I do know this doesn't look that simple at all but when you break it down it's just this:
[
{ "Title":"motion10", "InfoBoxHtml":"<div>Some html</div>",
"Latitude":51.91378,
"Longitude":4.53926,
"Image":null },
{ "Title":"Microsoft Netherlands", "InfoBoxHtml":null,
"Latitude":52.3045425,
"Longitude":4.750958,
"Image":null }
]
And that's something people can learn. If they do make a mistake they'll see an errormessage and the value will not be saved to the database:

Conclusion
Today I demonstrated how you can use JSON for complex properties in a SharePoint Web Part. I know it's not the most beautiful way to edit complex properties. You can create custom editors by creating a class that inherits from the abstract EditorPart class, but I think this is a good 'in the middle' solution where you both give the end user an interface to add his or her own pushpins and you don't go out of your way in creating and designing a complex user interface. In the end we'll probabely add pushpins to our map with a connection to our contacts list anyway, but that's something we'll discuss in the next episode were we'll finish our Virtual Earth class. Stay tuned and please feel free to drop me your comments.
Cheers,
Wes