Tuesday, March 10, 2009

Replace a webpart by another

In My "problem of the day" list, I have been asked whether it was possible or not to replace a webpart by another programatically. And obviously I have been asked to set the property values for the news Webpart.... So, I made a small tool for that.

I have created a Codeplex for this : http://webpartreplacer.codeplex.com The next versions will be uploaded there soon !!!

Basically, How does it work? On a specific application, I try to load the assembly that contains the webparts (source and destination) using reflection :
System.Reflection.Assembly asm = System.Reflection.Assembly.Load(AssemblySrc);
System.Web.UI.WebControls.WebParts.WebPart wpS = (System.Web.UI.WebControls.WebParts.WebPart) asm.CreateInstance(ClassSrc);


Then I look inside the Default page of each web and subweb trying to find the webpart (by checking the type of each webparts on the page) and if I find it, I replace the webpart using the SPLimitedWebPartManager.

I know that I should also parse all the document libraries to find other webpart pages in order to parse them as well... But in my current project, it wasn't a need, and I was kind of lazy!

Below is the iteration how I check whether the webparts on the page are to be replaced.


public void ReplaceWebpart(SPWeb web)
{
try
{

SPLimitedWebPartManager theMan =
web.Files["Default.aspx"].GetLimitedWebPartManager(System.Web.UI.WebControls.WebParts.PersonalizationScope.Shared);
System.Reflection.Assembly asm = System.Reflection.Assembly.Load(AssemblySrc);
System.Web.UI.WebControls.WebParts.WebPart wpS = (System.Web.UI.WebControls.WebParts.WebPart) asm.CreateInstance(ClassSrc);
System.Reflection.Assembly asmD = System.Reflection.Assembly.Load(AssemblyDest);
for (int i = 0; i < theMan.WebParts.Count; i++)
{

System.Web.UI.WebControls.WebParts.WebPart wp = theMan.WebParts[i];
if (wp.GetType().Equals(wpS.GetType()))
{
System.Web.UI.WebControls.WebParts.WebPart wpD = (System.Web.UI.WebControls.WebParts.WebPart)asmD.CreateInstance(ClassDest);
wpD.Title = wp.Title;
theMan.AddWebPart(wpD, theMan.GetZoneID(wp), wp.ZoneIndex);
theMan.WebParts[wpD.ID].
theMan.DeleteWebPart(wp);
Result++;
i--;
}
}
foreach (SPWeb Sweb in web.Webs)
{
ReplaceWebpart(Sweb);
Sweb.Dispose();
}
}
catch (Exception ex)
{
}
finally
{
}
}

This is the part 1 of the replacement. Now let's say that we want to go futher. Why not trying to specify some properties on this new webpart?

OK, I have created a ObjectCollection object containing a list of KeyValuePair (simply by taking the Items attribute of a listBox !

Then before the AddWebPart([...]) code line, I have added the following code :


foreach (Object LstItem in PropCollection)
{
try
{
System.Reflection.PropertyInfo prop = wpD.GetType().GetProperty(((KeyValuePair<String, String>)LstItem).Key);
if (prop != null)
{
String[] propValues = new String[1] { ((KeyValuePair<String, String>)LstItem).Value };
if (prop.PropertyType.Equals(String.Empty.GetType()))
{
prop.SetValue(wpD,((KeyValuePair<String, String>)LstItem).Value,null);
}
else
{
Type[] types = new Type[1] { String.Empty.GetType() };
prop.SetValue(wpD, prop.PropertyType.GetMethod("Parse", types, null).Invoke(prop, propValues), null);
}
}
}
catch (Exception exceptionProp)
{}
}

What happens here? We just try to get The property we try to set contained in our newly instanced WebPart. If found, we try to Use the Parse method of this property (Yes this only work with simple types (as Int32, Bool, String), and then we invoke it! Simple!!

That's it. You just have to make an iteration on each SPSite of you application and here we are!
Once again, be aware that this code only checks the default.aspx page !!
Sorry for that!!

Go to check in the codeplex project.

No comments:

Post a Comment