Using XSLT in your SharePoint web part
I've been doing a lot of content management work with SharePoint lately, which means a lot of the same functionality but always a different look and feel. XSLT is one of the main pillars of SharePoint development, and if you haven't been using it or haven't been using it extensively than you are doing yourself a huge disservice. Every time I create a SharePoint web part or control I always make the presentation use an XSL style sheet and allow that style sheet to be configured through a property. This is very similar to how the Content Query WebPart works, just most people end up using the default styles that come out of the box. This has saved me many times from redeveloping a web part or control just because the HTML or styling isn't just how the customer wants it. It is also a great way to get HTML markup out of your code, which just makes the code extremely hard to read. I'm going to show you how to use XSLT in all of your web parts from now on with a few simple examples. Here it goes....
The first example is when you are using a response from a datasource that returns XML already. The example I'm going to use is a Yahoo RSS feed. I know that for this you could simply use the RSS web part and modify that XSL but this is for demonstration purposes. You might have a XML Document for a data source. The first thing we need to do is get the XML. I did this as an Async method so it might be a little different if you aren't using Async methods.
IAsyncResult BeginGetIndustryNews(Object sender, EventArgs e, AsyncCallback cb, object state)
{
industrynewsReq = (HttpWebRequest)WebRequest.Create(industryNewsUrl);
industrynewsReq.Method = "GET";
industrynewsReq.Proxy = System.Net.GlobalProxySelection.GetEmptyWebProxy();
industryNewsResult = industrynewsReq.BeginGetResponse(cb, industrynewsReq);
return industryNewsResult;
}
void EndGetIndustryNews(IAsyncResult asyncResult)
{
if (industryNewsResult != null)
{
WebResponse response2 = (WebResponse)industrynewsReq.EndGetResponse(asyncResult);
Stream streamResponse2 = response2.GetResponseStream();
industryNewsHtml.Text = GetTransformResults(industryNewsXsl, streamResponse2);
response2.Close();
streamResponse2.Close();
}
}
private string GetTransformResults(string xslLink, Stream responseStream)
{
XmlUrlResolver resolver = new XmlUrlResolver();
resolver.Credentials = CredentialCache.DefaultCredentials;
XmlTextReader stylesheet = new XmlTextReader(SPContext.Current.Site.Url + xslLink);
stylesheet.XmlResolver = resolver;
XslCompiledTransform transform = new XslCompiledTransform();
transform.Load(stylesheet);
StringBuilder sb = new StringBuilder();
TextWriter writer = new StringWriter(sb);
using (XmlReader reader = new XmlTextReader(responseStream))
{
XmlTextWriter results = new XmlTextWriter(writer);
transform.Transform(reader, results);
reader.Close();
}
return sb.ToString();
}
If you haven't figured it out already the important method here is the "GetTransformResults". This is the method that takes the input stream and applies the XSL to it. In this method I create an XmlUrlResolver in order to use the current users credentials when accessing the XSL stylesheet. I then use a XmlTextReader to get the stylesheet. I then pass the XslCompiledTransform the XmlTextReader of the style sheet and call the transform method, writing the results to a StringBuilder. When I return this StringBuilder to the calling method what I am returning is a string of HTML that was created by transforming the XML data with the XSL transform. When I get the result in the EndGetIndustryNews method I set the Text property of the IndustryNewsHTML control, which is simply a literal control on the page. That's it! You now have a reusable control that you can change the look and feel of very easily without changing any code. In case you are wondering industryNewsXsl is a property on this control that can be set to any Xsl stylesheet in the site collection. To keep things consistent I usually place mine in the /style library/xsl style sheets folder.
This is all great but what if you have an object that contains the data that you want to transform? Surprisingly this is even easier. Simply use the built in XmlSerializer to get the XML representation of your object like so.
System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(typeof(Newsletter));
MemoryStream memoryStream = new MemoryStream();
serializer.Serialize(memoryStream, newsletter);
memoryStream.Position = 0;
newsHtml.Text = GetTransformResults(newsXsl, memoryStream);
In this case you might have a little trouble if you don't know exactly what the XML looks like that is coming out of the serializer. In case you want to get a look at the raw XML (which you probably will) you can use the style sheet below which will output in a textarea the bare XML.
<xsl:stylesheet version="1.0" exclude-result-prefixes="x d xsl msxsl cmswrt" xmlns:x="https://www.w3.org/2001/XMLSchema" xmlns:d="https://schemas.microsoft.com/sharepoint/dsp" xmlns:cmswrt="https://schemas.microsoft.com/WebParts/v3/Publishing/runtime" xmlns:xsl="https://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt">
<xsl:template match="/">
<textarea rows="20" cols="100">
<xsl:apply-templates/>
</textarea>
</xsl:template>
<xsl:template match="node()|@*">
<!-- Copy the current node -->
<xsl:copy>
<!-- Including any child nodes it has and any attributes -->
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
That's all there is to this really. Hopefully this will help you in writing all your web parts and controls in SharePoint to take advantage of XSL!
Comments
Anonymous
March 14, 2008
PingBack from http://msdnrss.thecoderblogs.com/2008/03/14/Anonymous
March 14, 2008
PingBack from http://msdnrss.thecoderblogs.com/2008/03/14/Anonymous
March 20, 2008
Really nice technique. I have been playing around with this for a couple of months because creating HTML inside the webpart is really a pain. Pulling out as much info as possible is the best way to go and this solutions helps with that. Thanks!Anonymous
February 10, 2010
Hi Josh I have a typical situation where - I have 5 different Web Parts which share the same XML response. But each Web Part applies different XSLT to the XML response for different results to be displayed. Now I would like to provide XSLT Edit option for each Web Part so that they could change the look and feel of every Web Part. I am also confused how to use the same Xml response between different Web Parts? Do I have to cache that XML response? Could you please help me? Can you please give me an example of how to do XSLT Edit property for a Custom Web Part. Thanks CarolAnonymous
February 12, 2010
Carol523 - Typically what I do in this situation is create a web part property that can be set on that instance of the web part. This way you can use the same web part and code in multiple places with different XSL. Here is the property declaration I typically use. private string xslFileUrl = "/style library/xsl style sheets/CustomListView/default.xsl"; [WebBrowsable(true)] [Personalizable(PersonalizationScope.Shared)] [WebDisplayName("XSL File URL"), Category("Generic List View Settings")] public string XslFileUrl { get { return xslFileUrl; } set { xslFileUrl = value; } }Anonymous
February 14, 2010
The comment has been removedAnonymous
March 02, 2011
I am trying to load an XSLT file in a Sharepoint MOSS 2007 custom webpart, but I am getting a 401 error while trying to access an XSLT file. I tried providing default credentials but it is empty upon inspection during debugging. Any idea what it can be ? XmlUrlResolver resolver = new XmlUrlResolver(); resolver.Credentials = CredentialCache.DefaultNetworkCredentials; XsltSettings settings = new XsltSettings(true, true); XslCompiledTransform oXSLTranform = new XslCompiledTransform(); string siteUrl = SPContext.Current.Site.Url; if (siteUrl.EndsWith("/")) siteUrl = siteUrl.Remove(siteUrl.LastIndexOf("/")); siteUrl += "/Style Library/XSL Style Sheets/THM1News.xslt"; oXSLTranform.Load(siteUrl ,settings, resolver);