After you build a web part and you use it in Sharepoint, you see that there are three types of possible editors for the part's properties: textboxes for strings and numbers, combos for enums and checkboxes for booleans. This post shows how you can create your own property editors.
A normal text editor has a lookup button next to it, which appears when the text editor receives focus. If clicked, the button shows a popup with a larger text editor in it. That is actually a page that is hosted in a dialog, interacting with the textbox via javascript (the
zoombldr.aspx page located in the
_layouts folder).
Using the
HtmlDesigner property attribute, this page can be replaced with one of your own. The downside to this is that the web part must be a Sharepoint web part or use only a static url. There are people on the web that advice on using the
System.Web.UI.WebControls.WebParts.WebPart class, not the one in the Sharepoint library, as the latter is kept only for compatibility reasons, however, it becomes obvious that the HtmlDesigner attribute cannot be used with system web parts. (an error of the form
InvalidCastException: Unable to cast object of type 'SharepointTests.HtmlDesignerWebPart' to type 'Microsoft.SharePoint.WebPartPages.WebPart'. is thrown).
Also, there is the issue of the lookup style button. That is the only customization you are allowed: at the closing of the custom editor, the resulting value will be stored in the same plain one line textbox.
There are some steps that must be taken in order for a custom property editor to work:
- the property must have an XML namespace. This can be declared on the class, using the XmlRoot attribute, or as a Namespace parameter to the XmlElement property attribute
- the property must be decorated with the HtmlDesigner attribute. It must feature either the URL for the page used as a custom editor, or the BrowserBuilderType.Dynamic value
- considering that BrowserBuilderType.Dynamic was used, the GetCustomBuilder method must be overriden in order to generate the url
- the page itself will receive the editor textbox as a parameter in the value of window.dialogArguments, while the selected value should be stored via javascript in window.returnValue
Let's see an example:
using System.ComponentModel;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Serialization;
using Microsoft.SharePoint.WebControls;
using Microsoft.SharePoint.WebPartPages;
using System.Runtime.InteropServices;
//using WebPart=System.Web.UI.WebControls.WebParts.WebPart;
using WebPart=Microsoft.SharePoint.WebPartPages.WebPart;
namespace SharepointTests
{
[Guid("28b28c5d-41ca-2603-a937-d6980b5a9aff")]
[XmlRoot("https://siderite.dev")]
public class HtmlDesignerWebPart : WebPart,IDesignTimeHtmlProvider
{
// Create a custom category in the tool pane.
[Category("Custom Properties")]
// Assign the default value.
[DefaultValue(null)]
// Make property available in both Personalization
// and Customization mode.
[WebPartStorage(Storage.Personal)]
// The caption that appears in the tool pane.
[FriendlyNameAttribute("My Custom String")]
// The tool tip that appears when pausing the mouse pointer over
// the friendly name in the tool pane.
[Description("Type a string value.")]
// Display the property in the tool pane.
[Browsable(true)]
[XmlElement(ElementName = "MyString")]
[HtmlDesignerAttribute(BrowserBuilderType.Dynamic,
DialogFeatures = @"center:yes;"+
"dialogHeight=40;"+
"dialogWidth=51;"+
"status=yes;"+
"resizable=yes;"+
"unadorned=no;")]
[WebDisplayName("My String")]
[WebDescription("It's mine, all mine!")]
[WebBrowsable(true)]
[Personalizable(PersonalizationScope.Shared)]
public string MyString
{
get;
set;
}
protected override string GetCustomBuilder(string propertyName)
{
if (propertyName=="MyString")
{
return ReplaceTokens("_WPR_/WebPartHtmlDesigner.aspx");
}
return base.GetCustomBuilder(propertyName);
}
protected override void Render(System.Web.UI.HtmlTextWriter writer)
{
writer.Write(MyString);
}
public string GetDesignTimeHtml()
{
return MyString;
}
}
}
This web part is the simplest you can imagine, with a single text property that it renders. Now, notice the
XmlRoot attribute decorating the class, the
HtmlDesignerAttribute attribute decorating the property MyString, with its attribute
DialogFeatures, and the
GetCustomBuilder override returning a string containing a strange
_WPR_ after being processed by
ReplaceTokens. Follow the links to see why is that.
Now for the editor itself. I created a web site with a page and then copied it in the
wpresources/[webpartnamespace] folder in the Sharepoint site. Take care about the
web.config file in the
wpresources folder which forbids the serving of aspx files. You need to remove or comment out the line
<add verb="*" path="*.aspx" type="System.Web.HttpForbiddenHandler, System.Web, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" /> in it. Of course, the folder (in this case
SharepointTests, as the namespace of the web part) must be an ASP.Net site, so register it as such.
Of course, this
wpresources method is taken from a site that gave me the idea for this post and it is there to demonstrate the use of the ReplaceTokens method. That being said, the override for
GetCustomBuilder could use a configuration file as a source for the url or you could even use a hardcoded string in the
HtmlDesignerAttribute constructor like
/SharepointTests/WebPartHtmlDesigner.aspx and then you just add the site
SharepointTests to the root of the sharepoint site. No web.config edits, no bothering with resources folder and so on.
The page does the following: it displays the properties of the received parameter, then it returns the current date in a string and sets the background of the editor textbox to red.
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title></title>
<script type="text/javascript">
function OnDesignerLoad() {
if (!window.dialogArguments) return;
var s = '';
for (var k in window.dialogArguments) {
s += '<br/>' + k + ' = ' + window.dialogArguments[k];
}
var lbl = document.getElementById('lblInfo');
lbl.innerHTML = s;
}
function OnDesignerUnload() {
if (!window.dialogArguments) return;
window.returnValue = (new Date()).toString();
window.dialogArguments.style.backgroundColor = 'red';
}
</script>
</head>
<body onload="OnDesignerLoad()" onunload="OnDesignerUnload()">
<form id="form1" runat="server">
<div>
<asp:Label runat=server Text="Design this!" ID="lblInfo" />
</div>
</form>
</body>
</html>
In order to avoid a lot of grief, add
Response.Expires = -1; to the Page_Load of the page, as browsers cache a lot of stuff and you have no "Refresh" option in the editor dialog.
I would have liked to have a javascript event that would replace the editor with something completely different when the editing begins, like a color picker or a calendar. I am not sure if this can be done, not until someone presses the lookup button, anyway. If I find a way I will update the entry.
That should do it. Here is
the link from which I draw my inspiration, it has pictures :), so you might like it better.
Comments
Be the first to post a comment