ASP.NET 2.0 Web Parts - Creating an Editor Zone Dialog
Categories
Some modern portal sites such as http://my.msn.com/ allow the editing of web parts to be carried out in a dialog window. This article shows the mechanics that are required to allow this to be done.
In my book I showed how to create a Catalog Zone dialog which allowed the web parts catalog zone to be displayed in a separate dialog window above the main page. On the forums for the book, a user recently asked whether it would be possible to create an Editor Zone dialog - a question that I've also seen on the ASP.NET forums a few times as well.

While creating a catalog zone dialog is a little tricky there's really only one really difficult task - to pass the Type name of the selected web part from the dialog window back to the parent window that opened it. From there you simply force a postback (__doPostback) and use the IPostbackEventHandler interface to intercept it. In the handing code you create an instance of the Type that was passed back and add it to a zone; simple!
Creating an editor zone dialog is much trickier because we need to refer to a single instance of a web part across multiple pages. The reason that this is so difficult is that the WebPartManager knows what web parts exist of a page and which web parts belong to which page and it does not allow a web part that belongs on one page to be added to another page - you can't even programatically change the page that a web part is connected to. Luckily the portal framework has a mechanism that allows for web parts to be moved around via a form of serialization known as Importing and Exporting. Through this mechanism, a web part can be exported from one page into an XML format and then, imported onto a second page using the ImportWebPart method of the WebPartManager. The following diagrams highlight the steps that I took to get a dialog editor zone solution up and running.

To launch the dialog I added a special edit verb to each web part which called a client-side javascript function that launched the dialog zone. To do that I had to create a special base class which added the verb and have all of my web parts inherit from that base class. The code for adding the special verb looked like this:
public override WebPartVerbCollection Verbs {
get {
Collection<WebPartVerb> verbs = new Collection<WebPartVerb>();
HttpContext ctx = HttpContext.Current;
if (ctx.Request.IsAuthenticated) {
ClientScriptManager cs = this.Page.ClientScript;
string postbackReference = cs.GetPostBackEventReference(this, "whatever");
string js = string.Format("DisplayDialog(\"DialogEditor.aspx?path={0}&wpid={1}&postbackReference={2}\");",
this.Page.Request.Path,
this.ID,
postbackReference
);
WebPartVerb editVerb = new WebPartVerb("MyEditVerb", js);
editVerb.Text = "Edit WebPart"
verbs.Add(editVerb);
}
return new WebPartVerbCollection(verbs);
}
}
Here you can see that the new edit verb will call a client-side function named DisplayDialog when it is clicked and will pass through the following information:
- The path to the dialog editor
- The id of the web part to edit
- a postback reference that can be used to invoke a postback
The postback reference is special in that we can now implement the IPostbackEventHandler interface on the web part to receive the event notification when the postbackReference is invoked.

Now that the editor has been launched we need to silently invoke the calling page to get the current web part instance that we'll be editing. Silently invoking the main page is done using the same technique that I discussed in the post about fixing broken web parts.
When the dialog is loaded we first check to see whether a web part ID is passed through and if so, we remove any existing web parts and add the new one that is being edited. We also set the page to Edit mode:
protected override void OnLoad(EventArgs e) {
base.OnLoad(e);
if (!IsPostBack) {
if (!string.IsNullOrEmpty(this.WebPartID)) {
RemoveAllWebParts();
AddWebPart();
WebPartManager1.DisplayMode = WebPartManager.EditDisplayMode;
WebPartManager1.BeginWebPartEditing(WebPartZone1.WebParts[0]);
}
}
}
The code for the RemoveAllWebParts and AddWebPart methods look like so:
void RemoveAllWebParts() {
int countOfParts = WebPartManager1.WebParts.Count;
for (int i = 0; i < countOfParts; i++) {
this.WebPartManager1.DeleteWebPart(WebPartManager1.WebParts[i]);
}
}
private void AddWebPart() {
string webPartXML = WebPartHelper.ExportWebPart(this.WebPartID, this.PathToEdit, this.Context);
WebPart wp = WebPartHelper.ImportWebPart(webPartXML, this);
wp.ID = this.WebPartID;
WebPartManager1.AddWebPart(wp, WebPartZone1, 0);
WebPartManager1.SetDirty();
}
The REALLY IMPORTANT thing to note here is the call to SetDirty on the WebPartManager. If you check the documentation for the WebPartManager you will not see such a method because mine is a custom WebPartManager class. The SetDirty method simply calls through to the protected SetPersonalizationDirty method of the WebPartManager:
public class CustomWebPartManager : WebPartManager {
public CustomWebPartManager() { }
public void SetDirty() {
this.SetPersonalizationDirty();
}
}
I added the call to SetDirty because generally Personalization changes will only be automatically saved on POST requests and not on GET's. Because we got to the dialog editor via a GET request we need to manually mark the personalization data as dirty for our changes to get saved. Failure to do this will mean that our web part is not properly added to the page and will therefore not be available when we attempt to re-access our changes from the main page later on.
To close the dialog editor I've just stuck a close button on the page which invokes the following javascript:
protected void btnClose_Click(object sender, EventArgs e) {
ClientScriptManager cs = this.ClientScript;
string script = "<script>CloseEditorDialog(\"" + PostbackReference + "\") ;</script>"
cs.RegisterStartupScript(this.GetType(), "whatever", script);
}
As you can see, closing the dialog will call a CloseEditorDialog function passing in the original PostbackReference string that was created from the verb on the calling web part. The CloseEditorDialog function will close the editor dialog and invoke the postback by eval'ing the PostBackReference string:
function CloseEditorDialog( postbackReference ) {
if( window.opener ) {
window.opener.DoEditorPostBack( postbackReference ) ;
}
window.close() ;
}
function DoEditorPostBack( postbackReference ) {
eval(postbackReference) ;
}

When the postback is invoked our RaisePostBackEvent method will get called and we will grab the web part from the dialog window and return it as XML:
public void RaisePostBackEvent(string eventArgument) {
if (eventArgument == "whatever") {
string xml = WebPartHelper.ExportWebPart(this.EditorPath, this.Context);
if (!string.IsNullOrEmpty(xml)) {
WebPartHelper.CopyWebPartValues(xml, this);
}
}
}
The XML is retrieved from the dialog window using the custom ExportWebPart method which, again, silently invokes the editor dialog to get the instance that was just edited:
public static string ExportWebPart(string path, HttpContext context) {
StringBuilder sb = new StringBuilder();
Page page = (Page)BuildManager.CreateInstanceFromVirtualPath(path, typeof(Page));
page.Load += delegate {
WebPartManager wm = WebPartManager.GetCurrentWebPartManager(page);
if (wm.WebParts.Count > 0) {
WebPart part = wm.WebParts[0];
if (part.ExportMode != WebPartExportMode.None) {
using (StringWriter sw = new StringWriter(sb))
using (XmlTextWriter xw = new XmlTextWriter(sw)) {
wm.ExportWebPart(part, xw);
}
}
}
};
ExecutePage(page, path, context);
return sb.ToString();
}
private static void ExecutePage(Page page, string path, HttpContext context) {
string originalPath = context.Request.Path;
context.RewritePath(path);
try {
context.Server.Execute(page, TextWriter.Null, false);
} catch { }
context.RewritePath(originalPath);
}

For now I have a very dumb method in my WebPartHelper class to copy the values from the XML that came back from the editor dialog and onto the instance. The method is basically just a huge switch statement which knows how to grab a string value and convert it to several underlying instance types. I'm sure that this could be done much better with more reflection code and some type converters but I won't take it any further for now. The method looks like this:
/// <summary>
/// Copies the Browsable property values from one web part to another
/// </summary>
/// <param name="webPartXML">The web part values to copy from</param>
/// <param name="copyTo">The web part to copy the values to</param>
public static void CopyWebPartValues( string webPartXML, WebPart copyTo ) {
XmlDocument doc = new XmlDocument();
doc.LoadXml(webPartXML);
XmlNamespaceManager mgr = new XmlNamespaceManager(doc.NameTable);
mgr.AddNamespace("wp", "http://schemas.microsoft.com/WebPart/v3");
foreach (XmlNode node in doc.SelectNodes(@"//wp:data/wp:properties/wp:property", mgr)) {
string propertyName = node.Attributes["name"].Value;
string propertyValue = (node.FirstChild == null) ? "" : node.FirstChild.Value;
PropertyInfo prop = copyTo.GetType().GetProperty(propertyName);
switch (prop.PropertyType.Name) {
case "Boolean":
prop.SetValue(copyTo, Convert.ToBoolean(propertyValue), null);
break;
case "Integer":
int b = 0;
if( int.TryParse(propertyValue, out b )) {
prop.SetValue(copyTo, Convert.ToInt32(propertyValue), null);
}
break;
case "DateTime":
DateTime d = DateTime.MaxValue;
if( DateTime.TryParse(propertyValue, out d ) ) {
prop.SetValue(copyTo, d, null);
}
break;
case "Unit":
Unit u = new Unit(propertyValue);
prop.SetValue(copyTo, u, null);
break;
case "Color":
prop.SetValue(copyTo, Color.FromName(propertyValue), null);
break;
case "String":
prop.SetValue(copyTo, propertyValue, null);
break;
default:
// TODO: Handle these
// System.Web.UI.WebControls.WebParts.WebPartExportMode ExportMode
// System.Web.UI.WebControls.WebParts.PartChromeType ChromeType
// System.Web.UI.WebControls.ContentDirection Direction
// System.Web.UI.WebControls.WebParts.PartChromeState ChromeState
// System.Web.UI.WebControls.WebParts.WebPartHelpMode HelpMode
Debug.WriteLine(prop);
break;
}
}
}
#
come and see
posted by
molly
on
8/19/2009 3:06:36 PM
:
plastic card manufacturer
Perhaps it goes without saying that plastic card manufacturer accessed using a smart card should be as easy to use as possible for all groups of users, but plastic card manufacturer is important that every effort is made to achieve this goal. [URL=
http://www.7daysprinting.com]plastic card manufacturer[/URL].When designing for plastic card manufacturer, it is useful to consider how a user interacts with all plastic card manufacturer to ensure that all aspects have been considered.
<a href="
http://www.7daysprinting.com">plastic card manufacturer</a>.The plastic card manufacturer provides a description for IT procurement. User interaction covers the plastic card manufacturer 's life cycle, from applying for and activating plastic card manufacturer, to ending any interaction with the plastic card manufacturer.
Clear plastic card
A Clear plastic card is used in or at a terminal. Before a user can use a Clear plastic card, the terminals in which the Clear plastic card can be used must be located.[URL=
http://www.7days-plasticcards.co.uk]Clear plastic card[/URL]A user’s first interaction with a Clear plastic card service is the process to receive a Clear plastic card.
<a href="
http://www.7days-plasticcards.co.uk">Clear plastic card</a>.This is called Clear plastic card issuance and covers activities such as how a user applies to access the service, how a suitable Clear plastic card is distributed and how it is activited for use. Naturally, a central consideration for a Clear plastic card service is the Clear plastic card itself. It is important, therefore, that the design of available Clear plastic card types cater for the whole user population.
plastic id card
If plastic id card service for whatever reason, plastic id card may be the most important aspect to a particular user group.
[URL=
http://www.7days-printing.com">
http://www.7days-printing.com]plastic id card[/URL]. Users need to be aware of what plastic id card is available. plastic id card readers are sophisticated devices with built-in intelligence.
<a href="
http://www.7days-printing.com">
http://www.7days-printing.com ">plastic id card</a>.plastic id card readers are found in notebooks. Combining plastic id card with biometrics can make plastic id card even more convenient and secure. Have a look at some state-of-the-art plastic id card readers. And we also sales engineering services to plastic id card.
smart card security
We are part of a vast network of smart card security solution partners with expertise in areas of smart card security manufacturing, and smart card security personalization. [URL=
http://www.smartcard-supplier.co.uk]smart card security[/URL].Almost two-thirds of adults who pruchase smart card security spent more than the original limit on the card at the store. <a href="
http://www.smartcard-supplier.co.uk"> smart card security</a>.Smart card security is ideal for loyalty cards. Unlike magnetic stripe cards, smart card security works offline. Imagine a computer so small it fits inside a smart card security the same size as any credit card you carry in your wallet. Im agine the smart card security.You can start your own affordable loyalty program with smart card security. Smart card security developers have many choices of smart card security systiem design.
Environmentally friendly
We can supply from stock a complete range of Environmentally friendly for use with card printer. [URL=
http://www.7daysprinting.co.uk">
http://www.7daysprinting.co.uk]Environmentally friendly[/URL].The recently updated Environmentally friendly fooers all the features required to create card layouts with ease of simplified operation. <a href="
http://www.7daysprinting.co.uk">
http://www.7daysprinting.co.uk">[URL=
http://www.7daysprinting.co.uk">
http://www.7daysprinting.co.uk]Environmentally friendly </a>.Four editions of Environmentally friendly are available from the entry level Environmentally friendly which allows for connection to external databases and the encoding of contactless Environmentally friendly. Whatever your requirement, Environmentally friendly has the solution. A Environmentally friendly personalisation bureau service is offered, where pre-printed or Environmentally friendly can be thermally printed, embossed encoded to your specific requirements. Environmentally friendly can also be procuced by digital or litho print methods. Other brands of Environmentally friendly can also be supplied.