I’ve been meaning to blog about this for a while but just haven’t gotten around to it. Have you ever needed to add a web part to a page during Feature activation? Of course you can do this declaratively using CAML but I usually prefer to do this stuff via code. The challenge is that occasionally you will need to activate the Feature outside the context of a web request – such as via STSADM – this becomes critical for certain web parts, such as earlier versions of the KPI List Web Part, which required a valid SPContext
in order to be added to a page (this web part was fixed in the August 2008 Cumulative Update (or thereabouts) so that it no longer requires a valid SPContext
object).
If you’re faced with adding a web part (or any other artifact) which requires a valid SPContext
object outside of a web request than you can create your own context with the following three lines of code:
1HttpRequest httpRequest = new HttpRequest("", web.Url, "");
2HttpContext.Current = new HttpContext(httpRequest, new HttpResponse(new StringWriter()));
3SPControl.SetContextWeb(HttpContext.Current, web);
I actually can’t take the credit for this – I was talking with Dan Attis during the summer about an issue and he pointed out this simple little fix for me. Using this I created a simple helper function that allows me to easily add a web part to a page within my Feature activation event. The code is below – just pass in the SPWeb
object, the URL to a page, and the path to the web part file. I put this in a shared library which I use for all of my Features thus making it super easy to add a web part to a page:
1public static void AddWebPart(SPWeb web, string page, string webPartXmlFile, string zone, int zoneId, bool deleteExistingIfFound)
2{
3 bool cleanupContext = false;
4 try
5 {
6 if (HttpContext.Current == null)
7 {
8 cleanupContext = true;
9 HttpRequest httpRequest = new HttpRequest("", web.Url, "");
10 HttpContext.Current = new HttpContext(httpRequest, new HttpResponse(new StringWriter()));
11 SPControl.SetContextWeb(HttpContext.Current, web);
12 }
13
14 using (SPLimitedWebPartManager manager = web.GetLimitedWebPartManager(page, PersonalizationScope.Shared))
15 {
16 string err;
17
18 XmlTextReader reader = null;
19 System.Web.UI.WebControls.WebParts.WebPart wp;
20 try
21 {
22 string webPartXml = File.ReadAllText(webPartXmlFile);
23 webPartXml = webPartXml.Replace("${siteCollection}", web.Site.Url);
24 webPartXml = webPartXml.Replace("${site}", web.Url);
25 webPartXml = webPartXml.Replace("${webTitle}", HttpUtility.HtmlEncode(web.Title));
26 reader = new XmlTextReader(new StringReader(webPartXml));
27
28 wp = manager.ImportWebPart(reader, out err);
29 if (!string.IsNullOrEmpty(err))
30 throw new Exception(err);
31 }
32 finally
33 {
34 if (reader != null)
35 reader.Close();
36 }
37
38 // Delete existing web part with same title so that we only have the latest version on the page
39 foreach (System.Web.UI.WebControls.WebParts.WebPart wpTemp in manager.WebParts)
40 {
41 if (wpTemp.Title == wp.Title)
42 {
43 if (deleteExistingIfFound)
44 manager.DeleteWebPart(wpTemp);
45 else
46 {
47 wpTemp.Dispose();
48 return;
49 }
50 break;
51 }
52 wpTemp.Dispose();
53 }
54
55 manager.AddWebPart(wp, zone, zoneId);
56 }
57 }
58 finally
59 {
60 if (HttpContext.Current != null && cleanupContext)
61 {
62 HttpContext.Current = null;
63 }
64 }
65}