Chapter 9 is all about looking at various ways to manage a web portal an my favourite sample from that chapter is a feature that the ASP.NET team showed off at PDC in LA last year. In that session Mike Harder, who is a Software Design Engineer on the ASP.NET team for the web parts feature, showed us how to create a page that displays all of the web parts for another page and then allows individual web parts to be deleted from the personalization data for that page. To do this, we dynamically execute the target page "silently" and then handle the PreInit event off of it and then grab the web parts from the WebPartManager instance on the page.
To dynamically execute another page and attach an event handler we use the following snippet of code:
Page page = (Page)BuildManager.CreateInstanceFromVirtualPath(path, typeof(Page));
WebPartCollection webParts = null;
page.PreLoad += delegate {
webParts = WebPartManager.
GetCurrentWebPartManager(page).WebParts;
};
ExecutePage(page, path, context);
Here we instantiate a page dynamically by using CreateInstanceFromVirtualPath method of the BuildManager class. This method takes the path of the ASP page that we wish to create an instance of and also a type argument which indicates what the base type for the page class is.
At this point you might be wondering why you would want to create a separate page to delete web parts. Consider what would happen if you added a web part to your page and that web part had some logic error which caused it to throw an exception from within its RenderContents method. The answer to this is that the web part would get added to the personalization data for the page and then that page would not be able to be displayed again - because of the exception that is thrown from the web part. This means that you cannot access the page to remove that web part and fix the problem. Now consider what would happen if that happened on the home page of a public internet site that you have just deployed!
Now I'm sure that you can see why it's important to provide a separate page so that we can recover from just such a case.
In our example, the helper method for executing the page looks is shown in the following snippet:
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);
}
You can see here that a null TextWriter is passed to the Execute method so that the page is not rendered anywhere. Once the page is executed, our anonymous method in the first snippet will get invoked and the web parts will be popped-off into our local webParts variable. This variable can then be bound to a GridView and displayed to the user. When the user chooses which part they would like to remove we run through a similar set of steps to when we loaded the web parts initially only our anonymous method uses the DeleteWebPart method on the WebPartManager to remove that web part like so:
page.PreLoad += delegate {
WebPartManager webPartManager =
WebPartManager.GetCurrentWebPartManager(page);
WebPart webPart = webPartManager.WebParts[ID];
webPartManager.DeleteWebPart(webPart);
};
In the book I go into more depth and there's also a working example of how to hook this up as the standard handling mechanism for errors that occur in a portal application so that users are given an immediate self-service option for fixing their broken page.
Thanks Mike, now I know that I won't get a call from the boss at midnight asking me to fix his broken portal page!