Copy & Paste WebParts (ASP.NET 2.0 portal framework)
Some developers in the ASP.NET 2.0 Forums have asked questions on how to copy a WebPart (including it's configuration) from one page to another.
The standard solution for this is to use the 'Export/Import' functionality that ASP.NET 2.0 offers for WebParts. Note: for a WebPart to be export-enabled, its 'ExportMode' property should have a value other than 'None' (which is the default) and enableExport="true" should be set in the webParts section of the web.config. The export and import of a (configured) WebPart is done in six steps:
- Switch to EditDisplayMode
- Select 'Export' from the verbs menu of the WebPart you want to copy
- Download the WebPart file to your local PC
- Navigate to the destination page, switch to CatalogDisplayMode
- Select the 'Imported Web Part Catalog', and upload the WebPart file
- Add the uploaded WebPart to a zone
Not very user-friendly, is it? Most users wouldn't even know what to do with the downloaded Webpart file.
However, the ASP.NET 2.0 portal framework is very extensible, so I've build a custom solution where the user can just 'copy' the WebPart to a serverside 'clipboard' (i.e. the Session object), and 'paste' it from a custom Catalog (which I named the 'Copied Web Part Catalog'). So this is done without downloading and uploading WebParts files!
Here's a screenshot of the copy/paste functionality:
So you want to know how this is done? I'll show you the code in 3 parts:
Part 1: Add a custom 'Copy' verb to your WebPart code.
/// <summary>
///
Override the Verbs collection to add the 'copy
webpart' verb
/// </summary>
public
override
WebPartVerbCollection Verbs
{
get
{
if (_verbs == null)
{
WebPartVerb myVerb = new
WebPartVerb("copy", OnWebPartCopy);
myVerb.Description = "Copy
this WebPart";
myVerb.Text = "Copy";
myVerb.ImageUrl =
"~/images/CopyVerb.gif";
_verbs = new WebPartVerbCollection(new
WebPartVerb[] { myVerb });
}
return
_verbs;
}
}
/// <summary>
///
Called when the user selects 'Copy WebPart' from the
verbs menu.
///
The selected WebPart is Exported to a xml string, and
added to
///
a list in the Session. This list is used by the
CopiedCatalogPart
///
to add the copied WebParts to a new page or zone
/// </summary>
public
void OnWebPartCopy(object
sender,
WebPartEventArgs e)
{
//get reference to WebPart to copy
WebPart
webPart = e.WebPart;
//temporarily set ExportMode to 'All' to enable
WebPart exporting
//this is a kind of hack,
but assumed legitimate because the exported WebPart
//stays on the server (in Session)
WebPartExportMode
origExportMode = ExportMode;
webPart.ExportMode =
WebPartExportMode.All;
//export WebPart
to string
StringWriter
stringWriter = new
StringWriter();
XmlTextWriter
xmlwriter = new
XmlTextWriter(stringWriter);
WebPartManager.ExportWebPart(webPart, xmlwriter);
string
copiedWebPart = stringWriter.ToString();
//reset original ExportMode of WebPart
webPart.ExportMode = origExportMode;
//reset original ExportMode in exported WebPart
string
XmlDocument doc =
new
XmlDocument();
doc.LoadXml(copiedWebPart);
XmlNode
exportModeNode = doc.SelectSingleNode("//property[@name='ExportMode']");
if
(exportModeNode !=
null)
{
exportModeNode.InnerText =
this.ExportMode.ToString();
copiedWebPart =
doc.OuterXml;
}
//get or create list of copied WebParts
List<KeyValuePair<string,
string>> copiedWebParts;
if
(Context.Session["COPIED_WEBPART_KEY"] != null)
{
copiedWebParts = (List<KeyValuePair<string, string>>)
Context.Session["COPIED_WEBPART_KEY"];
}
else
{
copiedWebParts =
new
List<KeyValuePair<string,
string>>();
}
//insert new copied WebPart as first on
list
copiedWebParts.Insert(0,
new
KeyValuePair<string, string>(
string.Format("{0} [copied {1}]", this.Title, DateTime.Now.ToLongTimeString()),
copiedWebPart));
//add list to Session
Context.Session["COPIED_WEBPART_KEY"] = copiedWebParts;
}
Part 2: The CopiedWebPartCatalogPart
using
System;
using
System.Collections.Generic;
using
System.Web.UI.WebControls.WebParts;
namespace
myClassLib.CatalogParts
{
/// <summary>
///
Catalog for reading WebParts copied to the users
Session (copy/paste functionality)
/// </summary>
public
class
CopiedWebPartCatalogPart :
CatalogPart
{
/// <summary>
///
Overrides the Title to display "Copied Web Part
Catalog" by default
/// </summary>
public
override
string
Title
{
get
{
string
title = base.Title;
return
string.IsNullOrEmpty(title)
?
"Copied Web Part Catalog"
: title;
}
set
{
base.Title = value;
}
}
//dictionary to hold the availabe copied
webparts
private
Dictionary<WebPartDescription, string> _webparts
=
new
Dictionary<WebPartDescription, string>();
/// <summary>
///
Returns the WebPartDescriptions for the catalog
part
/// </summary>
public
override
WebPartDescriptionCollection
GetAvailableWebPartDescriptions()
{
_webparts.Clear();
if
(Context.Session["COPIED_WEBPART_SESSION_KEY"] != null)
{
List<KeyValuePair<string, string>> copiedWebParts =
(List<KeyValuePair<string,
string>>)Context.Session["COPIED_WEBPART_SESSION_KEY"];
foreach
(KeyValuePair<string, string> copiedWebPart
in
copiedWebParts)
{
WebPartDescription
desc = new
WebPartDescription(
copiedWebParts.IndexOf(copiedWebPart).ToString(),
copiedWebPart.Key,
null,
null);
_webparts[desc] =
copiedWebPart.Value;
}
}
return
new
WebPartDescriptionCollection(_webparts.Keys);
}
/// <summary>
///
Returns a new instance of the WebPart specified by the
description
/// </summary>
public
override
WebPart GetWebPart(WebPartDescription
description)
{
//get a xmltextreader to the exported
WebPart
System.Xml.XmlTextReader
reader =
new
System.Xml.XmlTextReader(new System.IO.StringReader(_webparts[description]));
string
errorMessage; //will contain errorMessage on import errors
//return an instance of the requested
WebPart
return
WebPartManager.ImportWebPart(reader,
out
errorMessage);
}
}
}
Part 3: Add the CopiedWebPartCatalogPart in the CatalogZone of your aspx page
<%@
Register
TagPrefix="myCatalogParts"
Assembly="myClassLib"
Namespace="myClassLib.CatalogParts"
%>
<asp:CatalogZone ID="CatalogZone" runat="server" >
<ZoneTemplate>
<asp:PageCatalogPart
ID="PagePart"
runat="server"
/>
<asp:ImportCatalogPart
Visible="false"
ID="ImportPart"
runat="Server"
/>
<myCatalogParts:CopiedWebPartCatalogPart
ID="PastePart"
runat="server"
/>
</ZoneTemplate>
</asp:CatalogZone>