Sometimes requirement from customer is very peculiar. For instance, once customer wanted a web part whose layout (positioning of controls within the web part, styling, design specs etc. ) can be changed by some authorized user without changing the web part code. Even he wants that the authorized user can add/remove controls from/to web part without code change. Now this is where the XML/XSLT comes into picture.
In this case we can either use the OOTB Data Form web part or we can create our own custom web part. I will take you through the custom web part implementation approach (just to demonstrate how exactly the XML/XSL transformation takes place).
Here for our custom web part, we'll put the ASP.NET controls in the XSLT (code will be shown below) and they will read their properties (ID, Text, CSS Class name) and default values from a XML file. These XML/XSL files can be stored in either a list/library or their values can be stored within a list item of a list/library. For our custom web part, we were storing the XSL file within the Style Library and the XML file as a list item of a list.
- Master XML File
The Master XML file will look like:
<?xml
version="1.0" ?>
<CONTROLS
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<CONTROL
Name="WebPartControl1">
<PROPERTIES>
<PROPERTY
Name="Hidden"
ID="Hidden"
type="Boolean"
controltype="CheckBox">
<VALUE>False</VALUE>
</PROPERTY>
<PROPERTY
Name="List URL"
ID="NavigationListName"
type="string"
controltype="TextBox">
<VALUE>lists/CustomList</VALUE>
</PROPERTY>
<PROPERTY
Name="XSLT File Path"
ID="XSLTFilePath"
type="string"
controltype="TextBox">
<VALUE>Style Library/customXSL/ListMenusTransformXSLT.xsl</VALUE>
</PROPERTY>
<PROPERTY
Name="CSS Class Name"
ID="CSS"
type="string"
controltype="TextBox">
<VALUE>leftNav</VALUE>
</PROPERTY>
<PROPERTY
Name="Orientation"
ID="Orientation"
type="string"
controltype="DropDownList">
<OPTIONS>
<OPTION>Horizental</OPTION>
<OPTION>Vertical</OPTION>
</OPTIONS>
<VALUE>Vertical</VALUE>
</PROPERTY>
</PROPERTIES>
</CONTROL>
</CONTROLS> |
The XML above has defined the controls required in the web part "WebPartControl1". Each <PROPERTY></PROPERTY> node represents a control. It contains Check Boxes, Textboxes & Dropdown lists etc. we can add any type of control. The value of these controls will be stored in the <VALUE></VALUE> node.
2. Master XSLT File
The corresponding XSLT file will look like:
<?xml
version="1.0"
encoding="utf-8"?>
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:asp="remove">
<!-- Parmeters passed through Code -->
<xsl:param
name="controlID"></xsl:param>
<xsl:param
name="mainCSSClass"></xsl:param>
<xsl:template
match="CONTROLS">
<div
class="{$mainCSSClass}">
<xsl:if
test="string-length($controlID)!=0">
<asp:Panel
ID="PROPERTIES"
runat="server">
<table>
<!-- ControlID the the value of Control Node's Name attribute value -->
<xsl:for-each
select="CONTROLS/CONTROL[@Name=$controlID]">
<div
ID="propertiesDiv"
class="propertiesDiv">
<table>
<!-- Iterate through all the property nodes -->
<xsl:for-each
select="PROPERTIES/PROPERTY">
<tr>
<td>
<xsl:value-of
select="@Name"/>
</td>
<td>
<!-- Read the value of the Property in a variable -->
<xsl:variable
name="selectedValue">
<xsl:value-of
select="VALUE"/>
</xsl:variable>
<xsl:choose>
<xsl:when
test="@controltype='DropDownList'">
<!-- Render Drop down list -->
<asp:DropDownList
ID="{@ID}"
runat="server">
<xsl:for-each
select="OPTIONS/OPTION">
<xsl:choose>
<xsl:when
test="$selectedValue=current()">
<asp:ListItem
selected="selected">
<xsl:value-of
select="current()"/>
</asp:ListItem>
</xsl:when>
<xsl:otherwise>
<asp:ListItem >
<xsl:value-of
select="current()"/>
</asp:ListItem>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</asp:DropDownList>
</xsl:when>
<xsl:when
test="@controltype='CheckBox'">
<!-- Render Check box control -->
<asp:CheckBox
ID="{@ID}"
runat="server">
<xsl:attribute
name="Checked">
<xsl:value-of
select="$selectedValue"/>
</xsl:attribute>
</asp:CheckBox>
</xsl:when>
<xsl:otherwise>
<!-- Render Text box control -->
<asp:TextBox
ID="{@ID}"
runat="server" >
<xsl:attribute
name="Text">
<xsl:value-of
select="$selectedValue"/>
</xsl:attribute>
</asp:TextBox>
</xsl:otherwise>
</xsl:choose>
</td>
</tr>
</xsl:for-each>
</table>
</div>
</xsl:for-each>
</table>
</asp:Panel>
<div
ID="buttonDiv"
class="buttonDiv">
<table>
<tr>
<td>
<asp:Button
ID="btnSave"
runat="server"
Text="Save" />
</td>
<td>
<asp:Button
ID="btnCancel"
runat="server"
Text="Cancel" />
</td>
</tr>
</table>
</div>
</xsl:if>
</div>
</xsl:template>
</xsl:stylesheet> |
As you can see, here we are iterating through all the <PROPERTY></PROPERTY> nodes of the web part control whose ID is passed as a parameter into the XSLT. If the "ControlType" attribute of the control (in the XML, its PROPERTY) is Dropdown list, then we are adding a ASP.NET drop down control. The list items of this dropdown list are stored inside <OPTIONS><OPTION></OPTION></OPTIONS> node. Also the selected value is stored within the <VALUE></VALUE> node. We've assigned all the required properties of the ASP.NET control in XSLT. Similarly you can set the properties of Check box and Text box ASP.NET controls also. Also you can add other ASP.NET controls in the same way.
3. Web part Code
In the web part you need to read the XML data from the file or a list item. Then use the XSlTransform class to transform XML data into HTML using the XSL file. I will not go deep into this; however I'll write few lines of code for your reference which returns the HTML string data from the transformation of XML using the XSLT.
///
<summary>
/// Render xml/xsl into html using path of xsl and content of xml
///
</summary>
///
<param name="XMLString"> string formate of XML</param>
///
<returns></returns>
public
virtual
string RenderIntoHTML(string XMLString)
{
string XHTML = "";
string sXslUrl = "";
try
{
if (XMLString == null) { throw
new System.ArgumentException("XSL/XML file is missing"); }
if (xslPath == null) { throw
new System.ArgumentException("XSL file is missing"); }
sXslUrl = GetXslLocation + xslPath;
using (StringWriter swRenderedContents = new
StringWriter())
{
if (xmlDocument == null) { xmlDocument = new
XmlDocument(); }//creating objects
if (xslTransform == null) { xslTransform = new
XslCompiledTransform(); }//creating objects
if (xslArguList == null) { xslArguList = new
XsltArgumentList(); }//creating objects
xmlDocument.LoadXml(XMLString);
byte[] byteArray = System.Text.Encoding.UTF8.GetBytes(xslPath.Replace("\\\"", "\""));
MemoryStream streamd = new
MemoryStream(byteArray);
XmlTextReader ob = new
XmlTextReader(streamd);
xslTransform.Load(ob);
//xslTransform.Load(sXslUrl);
xslTransform.Transform(xmlDocument, xslArguList, swRenderedContents);
XHTML = swRenderedContents.ToString().Replace("<?xml version=\"1.0\" encoding=\"utf-16\"?>", "").Replace("xmlns:asp=\"remove\"", "").Replace("<br>", "<br/>").Replace("xmlns:SharePointWebControls=\"Microsoft.SharePoint.WebControls\"", "").Replace("xmlns:PublishingNavigation=\"Microsoft.SharePoint.Publishing.Navigation\"", "");
streamd.Close();
ob.Close();
}
}
catch (FileNotFoundException ex)
{
throw ex;
}
catch (Exception ex)
{
throw ex;
}
finally
{
xslTransform = null;
xslArguList = null;
xmlDocument = null;
}
return XHTML;
} |
Now the most important section is parsing the HTML string into the ASP.NET controls. Therefore in the CreateChildControls method the web part, just after calling the above method we will write following code.
Control ctrl = null;
string strRenderHtml = string.Empty;
strRenderHtml = RenderIntoHTML(strXML);
ctrl = Page.ParseControl(strRenderHtml);
this.Controls.Add(ctrl);
We are using the ParseControl() method of the page to parse the HTML string into ASP.NET controls.
That's it. Done! Your ASP.Net controls defined in the XSLT are rendered in your web part without any custom rendering logic written in the web part code. Thus, if tomorrow customer wants to add new dropdown list/text box etc. in your web part, you just need to add corresponding nodes in the XML file. Also if you need to change the display of your web part (controls positioning change etc.), you just need to make these changes in the XSLT file without the web part code change.