Tuesday, 28 June 2011

Visual Studio/SQL Server - Script schema and data in Visual Studio 2008 and onwards

http://blogs.msdn.com/b/webdevtools/archive/2007/10/15/sql-database-publishing-wizard-is-now-in-visual-studio-orcas.aspx

Friday, 24 June 2011

C#/XSLT - Adding custom xml namespaces / extension objects

Today I had a bit of a ball ache trying to capture errors that were failing within my XSLT. Whenever I called the document() function with an invalid file path, the document will fail to transform and nothing will be displayed.

Initially I looked at trying to capture the error using when/otherwise by passing the result of the document() function into a test attribute. This simply does not work, even if you convert the result to a boolean. So after wading through crappy advice, I thought as I'm using C# to do my transformations, I will simply add a custom namespace.

The following example explains how to check if a file exists using a custom namespace in C#.


1. Add a custom xmlns reference to your XSL file. The format is as follows: xmlns:<tag name>="<namespace>"

Example:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:file="urn:schemas-file">


This will allow me to prefix functions within my urn:schemas-file namespace with the prefix: file



2. Now the XSL has access to this custom library of functions (which we haven't yet coded yet)... we need to actually call one of our functions within the XSL.

Example:
<!-- Sample variable holding a location to a file -->
<xsl:variable name="filePath" select="../root/xml/testdocument.xml" />
 
<!-- Check to see if the file exists -->
<xsl:choose>
<xsl:when test="file:fileExists($filePath)">
    File exists... process the file here and output some stuff!
</xsl:when>
<xsl:otherwise>
    File does not exist. perform fallback operation!
</xsl:otherwise>
</xsl:choose>




3. Now the XSL knows out our functions and were consuming one of them, we just need to write the namespace itself and apply to to the transformation! Create a class in C# and add some functionality that you wish to make available to your XSL file.

Example:
// <copyright file="XSLFileHelper.cs" company="Ginko Solutions">
// Copyright (c) 2011 All Right Reserved
// </copyright>
// <author>Sean Greasley</author>
// <email>sean.greasley@fusedigital.com</email>
// <summary>Provides file functionality to support XSL processing</summary>
namespace RegulationExplorerWebHTML.Helpers
{
    using System;
    using System.IO;
    using System.Web;
 
    /// <summary>
    /// Provides file functionality to support XSL processing
    /// </summary>
    public class XSLFileHelper
    {
        public bool fileExists(string file)
        {
            // Get path
            string path = HttpContext.Current.Server.MapPath(file);
 
            // Exists<?>
            return File.Exists(path);
        }
    }
}




4. The final step is to apply this class to the transformation process! To do this, will use the AddExtensionObject method of the XsltArgumentList class.

Note: Please see my previous post about how to do an XML/XSL transformation in C# with parameters and settings.

Example:
XsltArgumentList xslArgs = new XsltArgumentList();
xslArgs.AddExtensionObject("urn:schemas-file", new XSLFileHelper());




5. and thats it!

Thursday, 23 June 2011

Serializing LINQ-to-SQL classes example with deep cloning

Serializing Linq-To-Sql classes isn't as straight forward as you might imagine. You may have got as far as attempting to serialize a class and found an error stating Linq.EntityRef cannot be serialized. This post explains to to serialize Linq-To-Sql entities using a cloning example.


For this example, I will be taking a deep clone of a Linq-To-Sql object and all of its associated properties. To do this, I will use serialization and utilise the ICloneable interface.

This example assumes you already have a Linq-To-Sql classes file (.dbml) with some tables/class information within the designer...

1. Open the Linq-To-Sql designer (.dbml) and view the main properties. Change the 'Serialzation Mode' to Unidirectional. This decorates our Linq-To-Sql entities with serialization capabilities.

2. Create a partial class for the Linq-To-Sql class you wish to clone. This allows you to extend the functionality of a Linq-To-Sql class as all classes are partial.

3. Add the [Serializable] attribute to the header of the class. This marks the class as serializable.

4. Implement the ICloneable inteferface. We can now override the Clone() method. When this method is called, we need to apply the serialization process to the object we wish to clone.

5. When serializing a Linq-To-Sql class, we cannot use the BinaryFormatter. Linq-To-Sql classes simply do not support this. Instead, we can utilise the NetDataContractSerializer. This will suit out requirements for the serialization of Linq-To-Sql entities.

6. and your done!



Serialzation code snippet
/// <summary>
/// Linq-To-Sql partial class, extends the 'Event' entity/class.
/// Note: Also changed Serialzation mode in designer to Unidirectional
/// </summary>
[Serializable]
public partial class Event : ICloneable
{
    /// <summary>
    /// Overrides the Clone method within the IClonable interface
    /// to performing object cloning
    /// </summary>
    /// <returns>A clone of the incoming object</returns>
    public object Clone()
    {
        return CloningFunctions.CloneObject(this);
    }
}
 
 
/// <summary>
/// Provides functions aiding object cloning
/// </summary>
public class CloningFunctions
{
    /// <summary>
    /// Clones an  object using the NetDataContractSerializer
    /// </summary>
    /// <param name="obj">Incoming object to clone</param>
    /// <returns>A clone of the incoming object</returns>
    public static object CloneObject(object obj)
    {
        using (MemoryStream memStream = new MemoryStream())
        {
            NetDataContractSerializer formatter = new NetDataContractSerializer(new StreamingContext(StreamingContextStates.Clone));
            formatter.Serialize(memStream, obj);
            memStream.Seek(0, SeekOrigin.Begin);
            return formatter.Deserialize(memStream);
        }
    }
}

Wednesday, 22 June 2011

Access computer behind router over the internet

Here's a few notes which I find useful for accessing my system over the internet from an external location.

Is the remote system behind a router?
If so, use the router's IP address. Either go to whatismyip.com, or get the router's IP Address from the router control panel (typically 192.168.1.1). This will be the IP address you will use to access the system.

Enabling remote access
Enable remote access to the system. For windows, its usually at "Right Click My Computer > Properties > Remote/Remote Settings". Here you can also configure who is allowed to access the system.

Systems behind a router
Setup port forwarding to the system in question. Find your local IP address (usually 192.168.x.x) and forward the necessary ports to this system. I.e. for HTTP, use port 80. So when you visit the router's IP address over HTTP (http://externalRouterIP), it will forward the request to your machine. Another useful one is 3389 for remote desktop.
Optional: It may be useful to set a static IP for your system. This ensures that your internal IP address stays the same. If your router software does not support this, you can figure your own in Network Settings under the Control Panel in Windows.

Monday, 20 June 2011

SEO - Search Engine Optimization

Here's a good post on hoe to use an SEO tool to crawl your website for SEO improvements.
The tool installs itself into most web servers and is free to download!

Description
The IIS Search Engine Optimization (SEO) Toolkit helps Web developers, hosting providers, and Web server administrators to improve their Web site’s relevance in search results by recommending how to make the site content more search engine-friendly. The IIS SEO Toolkit includes the Site Analysis module, the Robots Exclusion module, and the Sitemaps and Site Indexes module, which let you perform detailed analysis and offer recommendations and editing tools for managing your Robots and Sitemaps files.

Article and Download
http://weblogs.asp.net/scottgu/archive/2009/06/03/iis-search-engine-optimization-toolkit.aspx

Wednesday, 15 June 2011

C# - Simple TripleDES Encryption with or without hashing

This class below enables simple encryption and decryption of text strings. It uses the TripleDES algorithms and you can chose weather to use MD5 hash against the key.
Simply change the value of the encryptionKey to whatever you want the key to be.


C# Source code
// <copyright file="EncryptionHelper.cs" company="GinkoSolutions.com">
// Copyright (c) 2011 All Right Reserved
// </copyright>
// <author>Sean Greasley</author>
// <email>sean@ginkosolutions.com</email>
// <summary>Enables simple TripleDES encryption with or without hashing</summary>
using System;
using System.Collections.Generic;
using System.Text;
using System.Web;
using System.Security.Cryptography;
 
/// <summary>
/// Enables simple TripleDES encryption with or without hashing
/// </summary>
public class EncryptionHelper
{
    /// <summary>
    /// Encryption key. Used to encrypt and decrypt.
    /// </summary>
    private static readonly string encryptionKey = "YOURSECRETKEY";
 
    public EncryptionHelper() {}
 
    /// <summary>
    /// Encrypt text string
    /// </summary>
    /// <param name="toEncrypt">The string of data to encrypt</param>
    /// <param name="useHashing">Weather hashing is used or not</param>
    /// <returns>An encrypted string</returns>
    public static string Encrypt(string toEncrypt, bool useHashing)
    {
        byte[] keyArray;
        byte[] toEncryptArray = UTF8Encoding.UTF8.GetBytes(toEncrypt);
 
        // If hashing use get hashcode regards to your key
        if (useHashing)
        {
            MD5CryptoServiceProvider hashmd5 = new MD5CryptoServiceProvider();
            keyArray = hashmd5.ComputeHash(UTF8Encoding.UTF8.GetBytes(encryptionKey));
            hashmd5.Clear();
        }
        else
            keyArray = UTF8Encoding.UTF8.GetBytes(encryptionKey);
 
        // Set the secret key for the tripleDES algorithm
        TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider();
        tdes.Key = keyArray;
        tdes.Mode = CipherMode.ECB;
        tdes.Padding = PaddingMode.PKCS7;
 
        // Transform the specified region of bytes array to resultArray
        ICryptoTransform cTransform = tdes.CreateEncryptor();
        byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
        tdes.Clear();
 
        // Return the encrypted data into unreadable string format
        return Convert.ToBase64String(resultArray, 0, resultArray.Length);
    }
 
    /// <summary>
    /// Decrypts a text string
    /// </summary>
    /// <param name="cipherString">The encrypted string</param>
    /// <param name="useHashing">Weather hashing is used or not</param>
    /// <returns>Decrypted text string</returns>
    public static string Decrypt(string cipherString, bool useHashing)
    {
        byte[] keyArray;
        byte[] toEncryptArray = Convert.FromBase64String(cipherString.Replace(' ', '+'));
 
        if (useHashing)
        {
            // If hashing was used get the hash code with regards to your key
            MD5CryptoServiceProvider hashmd5 = new MD5CryptoServiceProvider();
            keyArray = hashmd5.ComputeHash(UTF8Encoding.UTF8.GetBytes(encryptionKey));
            hashmd5.Clear();
        }
        else
        {
            // If hashing was not implemented get the byte code of the key
            keyArray = UTF8Encoding.UTF8.GetBytes(encryptionKey);
        }
 
        // Set the secret key for the tripleDES algorithm
        TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider();
        tdes.Key = keyArray;
        tdes.Mode = CipherMode.ECB;
        tdes.Padding = PaddingMode.PKCS7;
 
        ICryptoTransform cTransform = tdes.CreateDecryptor();
        byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
        tdes.Clear();
 
        // Return the Clear decrypted TEXT
        return UTF8Encoding.UTF8.GetString(resultArray);
    }
}

Facebook Connect - Post to wall feed

After using Facebook Connect for a while, you can soon find out how shoddy the documentation is. There seem to be about 5000 half-written API's, most out of date and even the Facebook API documentation is barely holding itself together.

Anyway, here's a small JS snippet on how to post to a user's wall. The user must first allow the application pushing rights, or an error will occur.

I have already written a great blog entry on getting started with Facebook Connect, check it out here!


Pre-requisites
- Check out the above link if you have no idea how to get started!
- Allow pushing rights (publish_stream)
- Ensure your already dealing with an authentication session (I have added the code to ensure you already have a valid session in the code below)


Javascript Code
FB.getLoginStatus(function (response) {
    if (response.session) {
 
        var params = {};
        params['message'] = 'This is the main message';
        params['name'] = 'This is the name of the link';
        params['description'] = 'This is the link description';
        params['link'] = 'http://www.URLToNavigateTo.com';
        params['picture'] = 'http://www.FULLpathtoimage.com/image.png'; // 90x90 px
        params['caption'] = 'Small caption appearing under the link';
 
 
        FB.api('/me/feed', 'post', params, function (response) {
            if (!response || response.error) {
                // Error occured posting to the fb wall
            }
        });
 
    } else {
        // User isn't authenticated with facebook
    }
});



This will produce the following result...

Tuesday, 14 June 2011

ASP.NET C# 4.0 - Facebook Connect Example

Facebook connect example

This is a full example written in ASP.NET 4.0 and Javascript. It uses the Facebook Connect client-side library to authenticate and register uses into a database. This minimalist example explains how to easily integrate Facebook Connect into existing or new web applications.

Technologies used:
- ASP.NET/C#
- Javascript/JQuery
- AJAX
- SQL Server Compact
- Entity Framework
- LINQ

Setup Instructions
1. Add the Developer application to your facebook account and create a new facebook application.

Make sure you run the server on port "8080"... Facebook won't argue with this.
(Note: If you use any other port, other than the standard web traffic ports [80/8080],
then Facebook will throw a blank proxy dialog at you, with no kind of helpful error messages!
The URL will contain something like "xd_proxy"...so make sure it's set at 8080!

The settings you can use are as follows...
[Web Site] > [Site URL] > http://localhost:8080/
[Web Site] > [Site Domain] > localhost
[Facebook Integration] > [Canvas URL] > http://localhost:8080/
[Facebook Integration] > [Tab URL] > http://localhost:8080/
[Advanced] > [Sandbox Mode] > Enable [Note: Must only use developer accounts to test with! or you will get errors]


2. Enter application details into Web.config.

3. Enjoy!


Download
Download Source Files Here

Friday, 10 June 2011

CSS: Aligning form fields on a page

In the following example, I have used ASP.NET controls as I have lifted this from a project I have been working on.
If you are not familiar with ASP.NET, when the browser renders these server controls, the controls are rendered down to simple HTML tags:

label (asp:Label)
input (asp:TextBox)
span (asp:RequiredFieldValidator))



HTML/ASP.NET Code
<fieldset class="regForm">
<div class="field">
    <asp:Label ID="lblFirstName" AssociatedControlID="txtFirstName" runat="server" Text="Firstname" />
    <asp:TextBox ID="txtFirstName" runat="server" />
    <asp:RequiredFieldValidator ID="rfvFirstName" runat="server" ControlToValidate="txtFirstName" SetFocusOnError="True" ValidationGroup="vgRegister" ErrorMessage="*" />
</div>
<div class="field">
    <asp:Label ID="lblSurname" AssociatedControlID="txtSurname" runat="server" Text="Surname" />
    <asp:TextBox ID="txtSurname" runat="server" />
    <asp:RequiredFieldValidator ID="rfvSurname" runat="server" ControlToValidate="txtSurname" SetFocusOnError="True" ValidationGroup="vgRegister" ErrorMessage="*" />
</div>
<div class="field">
    <asp:Label ID="lblEmail" AssociatedControlID="txtEmailAddress" runat="server" Text="Email" />
    <asp:TextBox ID="txtEmailAddress" runat="server" />
    <asp:RequiredFieldValidator ID="rfvEmail" runat="server" ControlToValidate="txtEmailAddress" SetFocusOnError="True" ValidationGroup="vgRegister" ErrorMessage="*" />
</div>
</fieldset>



CSS Code
fieldset.regForm div {
    clear: both;
    padding-top:0.2em;
}
fieldset.regForm label {
    display:inline;
    padding-top:0.2em;
    text-align:right;
    margin-right:0.5em;
    float:left;
    width:25%;
}

ASP.NET: Quick and simple client-side validation

If you have created some server side controls, some ASP.NET validator controls to go with them, but... you would like to validate on the client, then call the function in the code snippet below from javascript.
Just remember to specify a ValidationGroup on your form fields so the JS function knows which controls to validate


Code Snippet
// By leaving the validation group parameter blank, it will
// validate the whole page, not just a target group.
if (Page_ClientValidate("ValidationGroupNameHere")) {
   // JS Code here...
}

Javascript: format() string extension for easy formatting!

Here's a simple javascript string extension for easy string formatting


Code Snippet
// Example:
//
//    var testString = "Hey {0}, this is a pretty {1} string formatter. I hope you have {2} with it!".format("guys", "cool", "fun");
//    alert(testString);
//
// String.format extension for easy formatting
String.prototype.format = function () {
    var s = this,
        i = arguments.length;
 
    while (i--) {
        s = s.replace(new RegExp('\\{' + i + '\\}', 'gm'), arguments[i]);
    }
    return s;
};

JQuery: Dynamically Add/Remove jquery.ui button from dialog

When using the dialog widget within the jquery.ui library, it may become necessary to add or remove buttons dynamically.
Invoking the extension methods below will allow you to achieve the desired result.


Code Snippet
Code Snippet
  1. // Example:
  2. //            $('#IDDialogDiv').dialog('addbutton', 'Name of button');
  3. //            $('#IDDialogDiv').dialog('removebutton', 'Name of button');
  4. //
  5. //
  6. // Allows simple button addition to a ui dialog
  7. $.extend($.ui.dialog.prototype, {
  8.     'addbutton': function (buttonName, func) {
  9.         var buttons = this.element.dialog('option', 'buttons');
  10.         buttons[buttonName] = func;
  11.         this.element.dialog('option', 'buttons', buttons);
  12.     }
  13. });
  14.  
  15. // Allows simple button removal from a ui dialog
  16. $.extend($.ui.dialog.prototype, {
  17.     'removebutton': function (buttonName) {
  18.         var buttons = this.element.dialog('option', 'buttons');
  19.         delete buttons[buttonName];
  20.         this.element.dialog('option', 'buttons', buttons);
  21.     }
  22. });
End of Code Snippet

Javascript: Pass parameter into setTimeout

Sometimes passing a parameter into a function, specified within a setTimeout() function isn't so straight forward. Different browser garbage collectors operate differently, and your function sometimes won't be executed at all.

Here is a method that works in all browsers. You need to specify the function within an anonymous function, and set the parameter to null.

Code Sample
var foo = "Hello World";
setTimeout(function () { helloWorld(foo); foo = null }, 3000);
 
function helloWorld(foo)
{
    alert(foo);
}

Wednesday, 8 June 2011

CKEditor - WYSIWYG editor for web pages

CKEditor is a text editor to be used inside web pages. It's a WYSIWYG editor, which means that the text being edited on it looks as similar as possible to the results users have when publishing it. It brings to the web common editing features found on desktop editing applications like Microsoft Word and OpenOffice.

Because CKEditor is licensed under flexible Open Source and commercial licenses, you'll be able to integrate and use it inside any kind of application. This is the ideal editor for developers, created to provide easy and powerful solutions to their users.


Download Here

LinqToXML - Safe element and attribute checking [XElement Extension Method]

When using LINQ with XML, you may run into issues where nodes do not exist, values are empty, attribute are either missing or empty also.

By creating two extension methods for the XElement class, we can provide safe checking for these elements and attributes. Consider the following code snippet below...


Invoking the extension method with LINQ
XDocument contentXML = XDocument.Load(xmlPath);
var res = from x in contentXML.Descendants("rootNode")
orderby x.Element("title").ElementValueNull()
select new TestObject(x.AttributeValueOrDefault("id"), x.Element("title").ElementValueNull());



XElement Extension class
// <copyright file="XElementExtensionMethods.cs" company="GinkoSolutions">
// Copyright (c) 2011 All Right Reserved
// </copyright>
// <author>Sean Greasley</author>
// <email>sean@ginkosolutions.com</email>
// <summary>Extension methods for the XElement class</summary>
namespace XElementTest
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;
    using System.Xml;
    using System.Xml.Linq;
 
    /// <summary>
    /// Extension methods for the XElement class
    /// </summary>
    public static class XElementExtensionMethods
    {
        /// <summary>
        /// Allows a safe way to retrieve attribute values from an element
        /// </summary>
        /// <param name="element">A reference to the element object</param>
        /// <param name="attributeName">The name of the attribute</param>
        /// <returns>The attribute content or null</returns>
        public static string AttributeValueOrDefault(this XElement element, string attributeName)
        {
            XAttribute attr = null;
 
            if (element != null)
                attr = element.Attribute(attributeName);
 
            return attr == null ? null : attr.Value;
        }
 
        /// <summary>
        /// Allows a safe way to retrieve element data
        /// </summary>
        /// <param name="element">A reference to the element object</param>
        /// <returns>Element content or an empty string</returns>
        public static string ElementValueNull(this XElement element)
        {
            if (element != null)
                return element.Value;
 
            return string.Empty;
        }
    }
}

Tuesday, 7 June 2011

C# MVC 3 - Render XSLT using a HTMLHelper Extension

This is an example on how to transform XML documents using XSLT, within C# MVC using a HTMLHelper extension and Razor View Syntax.


Method of operation
1. View is requested
2. Controller is invoked
3. Controller finds the location of the XML and XSL files for the current request. Parameters are also defined here.
4. The view is created and returned by the controller
5. The view invokes the HTMLExtension method (Razor View Syntax) to render the XML (RenderXSLT method).
6. The extension transforms the document and returns the result.
7. The transformation is displayed within the view.


Controller Method
        public ActionResult Index()
        {
            XsltViewModel model = new XsltViewModel()
            {
                XmlPath = Server.MapPath("~/PathToXMLFileHere.xslt")
                XsltPath = Server.MapPath("~/PathToXSLTFileHere.xslt")
            };
 
            return View(model);
        }



Code within the view (calls out extension method using the model info)
@model RenderXSLTExample.XsltViewModel
 
<div class="xmlResult">
    
    @Html.RenderXslt(Model.XsltPath, Model.XmlPath)
        
</section>



XsltViewModel object (holds reference to XML, XSLT and params)
// <copyright file="XsltViewModel.cs" company="GinkoSolutions">
// Copyright (c) 2011 All Right Reserved
// </copyright>
// <author>Sean Greasley</author>
// <email>sean@ginkosolutions.com</email>
// <summary>Manages XSLT and XML information model objects</summary>
namespace RenderXSLTExample
{
    using System.Collections.Generic;
 
    /// <summary>
    /// Manages XSLT and XML information model objects
    /// </summary>
    public class XsltViewModel
    {
        /// <summary>
        /// Holds a reference to the XSLT path
        /// </summary>
        public string XsltPath { get; set; }
 
        /// <summary>
        /// Holds a reference to the XML path
        /// </summary>
        public string XmlPath { get; set; }
 
        /// <summary>
        /// Optional parameters to pass into the XSL file.
        /// </summary>
        public List<KeyValuePair<string, string>> Params { get; set; }
    }
}



HTMLHelper Extension class
// <copyright file="HtmlHelperExtensions.cs" company="GinkoSolutions">
// Copyright (c) 2011 All Right Reserved
// </copyright>
// <author>Sean Greasley</author>
// <email>sean@ginkosolutions.com</email>
// <summary>A HTMLExtension method to render XML using XSL</summary>
namespace RenderXSLTExample
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Reflection;
    using System.Text;
    using System.Web;
    using System.Web.Mvc;
    using System.Xml;
    using System.Xml.Resolvers;
    using System.Xml.Xsl;
    using System.Xml.XPath;
 
    /// <summary>
    /// A HTMLExtension method to render XML using XSL
    /// </summary>
    public static class HtmlHelperExtensions
    {
        /// <summary>
        /// Accepts a reference to the XML and XSL files and applies a transformation with optional
        /// parameters.
        /// </summary>
        /// <param name="helper">A reference to the HtmlHelper object</param>
        /// <param name="xsltPath">The path to the XSL file</param>
        /// <param name="xmlPath">The path to the XML file</param>
        /// <param name="parameters">Optional: A list  of arguments to pass into the XSL file</param>
        /// <returns>MvcHtmlString representing the transform</returns>
        public static MvcHtmlString RenderXslt(this HtmlHelper helper, string xslPath, string xmlPath, List<KeyValuePair<string, string>> parameters = null)
        {
            string xsltResult = string.Empty;
 
            try
            {
                // XML Settings
                XmlReaderSettings xmlSettings = new XmlReaderSettings();
                xmlSettings.XmlResolver = null;
                xmlSettings.IgnoreComments = true;
                xmlSettings.DtdProcessing = DtdProcessing.Ignore;
                xmlSettings.ValidationType = ValidationType.None;
 
                // Attaches an action to the valiation event handler. This will write out error messages in the Output pane.
                #if DEBUG
                xmlSettings.ValidationEventHandler += (sender, e) =>
                {
                    Debug.WriteLine(string.Format("{0}({1},{2}): {3} - {4}", e.Exception.SourceUri, e.Exception.LineNumber, e.Exception.LinePosition, e.Severity, e.Message));
                };
                #endif
 
                // XSLT Settings
                XmlReaderSettings xsltSettings = new XmlReaderSettings();
                xsltSettings.XmlResolver = null;
                xsltSettings.DtdProcessing = DtdProcessing.Ignore;
                xsltSettings.ValidationType = ValidationType.None;
 
                // Attaches an action to the valiation event handler. This will write out error messages in the Output pane.
                #if DEBUG
                xsltSettings.ValidationEventHandler += (sender, e) =>
                {
                    Debug.WriteLine(string.Format("{0}({1},{2}): {3} - {4}", e.Exception.SourceUri, e.Exception.LineNumber, e.Exception.LinePosition, e.Severity, e.Message));
                };
                #endif
 
                // Init params
                XsltArgumentList xslArgs = new XsltArgumentList();
                if (parameters != null)
                {
                    foreach (KeyValuePair<string, string> param in parameters)
                        xslArgs.AddParam(param.Key, string.Empty, param.Value);
                }
 
                // Load XML
                using (XmlReader reader = XmlReader.Create(xmlPath, settings))
                {
                    // Load XSL
                    XsltSettings xslSettings = new XsltSettings(true, true); // Need to enable the document() fucntion
 
                    using (XmlReader xslSource = XmlReader.Create(xslPath, xsltSettings))
                    {
                        XslCompiledTransform xsltDoc = new XslCompiledTransform();
                        xsltDoc.Load(xslSource, xslSettings, new XmlUrlResolver());
 
                        // Transform
                        using (var sw = new UTF8StringWriter())
                        {
                            XmlWriterSettings settings = new XmlWriterSettings();
                            settings.Encoding = Encoding.UTF8;
                            settings.OmitXmlDeclaration = true;
 
                            using (var xw = XmlWriter.Create(sw, settings))
                            {
                                xsltDoc.Transform(reader, xslArgs, sw);
                            }
 
                            xsltResult = sw.ToString();
                        }
                    }
                }
            }
            catch { } // custom error handling here
 
            // Return result
            return MvcHtmlString.Create(xsltResult);
        }
    }
}

C#.NET 4 - Transforming XML and XSL documents with settings and parameters

This following example takes a path to an XML file, a path to an XSL file and optional parameters to pass to the XSL file. The XSL file will decorate any xsl:param tags with these values.

The example also includes settings objects to show how settings can be applied to the tranformation and the loading of the XML.


Example C# Code
string xsltResult = string.Empty;
 
try
{
    // XML Settings
    XmlReaderSettings xmlSettings = new XmlReaderSettings();
    xmlSettings.XmlResolver = null;
    xmlSettings.IgnoreComments = true;
    xmlSettings.DtdProcessing = DtdProcessing.Ignore;
    xmlSettings.ValidationType = ValidationType.None;
 
    // Attaches an action to the valiation event handler. This will write out error messages in the Output pane.
    #if DEBUG
    xmlSettings.ValidationEventHandler += (sender, e) =>
    {
        Debug.WriteLine(string.Format("{0}({1},{2}): {3} - {4}", e.Exception.SourceUri, e.Exception.LineNumber, e.Exception.LinePosition, e.Severity, e.Message));
    };
    #endif
 
    // XSLT Settings
    XmlReaderSettings xsltSettings = new XmlReaderSettings();
    xsltSettings.XmlResolver = null;
    xsltSettings.DtdProcessing = DtdProcessing.Ignore;
    xsltSettings.ValidationType = ValidationType.None;
 
    // Attaches an action to the valiation event handler. This will write out error messages in the Output pane.
    #if DEBUG
    xsltSettings.ValidationEventHandler += (sender, e) =>
    {
        Debug.WriteLine(string.Format("{0}({1},{2}): {3} - {4}", e.Exception.SourceUri, e.Exception.LineNumber, e.Exception.LinePosition, e.Severity, e.Message));
    };
    #endif
 
    // Init params
    XsltArgumentList xslArgs = new XsltArgumentList();
    if (parameters != null)
    {
        foreach (KeyValuePair<string, string> param in parameters)
            xslArgs.AddParam(param.Key, string.Empty, param.Value);
    }
 
    // Load XML
    using (XmlReader reader = XmlReader.Create(xmlPath, settings))
    {
        // Load XSL
        XsltSettings xslSettings = new XsltSettings(true, true); // Need to enable the document() fucntion
 
        using(XmlReader xslSource = XmlReader.Create(xslPath, xsltSettings))
        {
            XslCompiledTransform xsltDoc = new XslCompiledTransform();
            xsltDoc.Load(xslSource, xslSettings, new XmlUrlResolver());
 
            // Transform
            using (var sw = new UTF8StringWriter())
            {
                        XmlWriterSettings settings = new XmlWriterSettings();
                        settings.Encoding = Encoding.UTF8;
                        settings.OmitXmlDeclaration = true;
 
                using(var xw = XmlWriter.Create(sw,settings))
                {
                    xsltDoc.Transform(reader, xslArgs, sw);
                }
 
                xsltResult = sw.ToString();
            }
        }
    }
}
catch {} // custom error handling here

XSLT - Adding an attribute to an element based on XML data

Adding an attribute to an element requires us to utilise the xsl:attribute element.

Consider the following example:
<img>
  <xsl:attribute name="src">
    <xsl:value-of select="imageURL" />
  </xsl:attribute>
  <xsl:attribute name="alt">
    <xsl:value-of select="altText" />
  </xsl:attribute>
</img>


This will add an src and an alt attribute to an img element; based on the values within the imageURL and altText elements in the XML in this example.


Note: You must make sure that the attributes are added within the element itself. I.e. If the img tag is closed before the attributes are added in the above example, then this would be invalid.

XSLT - Displaying HTML content within XML tags

Rather then using the value-of tag, utilise the copy-of tag instead.


XML File
<?xml version="1.0"?>
<?xml-stylesheet type="text/xml" href="stylesheet.xsl"?>
<test><p><b>This is bold text in a paragraph</b></p></test>



XSL File
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="/">
    <xsl:copy-of select="test" />
    <xsl:value-of select="test" />
  </xsl:template>
</xsl:stylesheet>



Output

This is bold text in a paragraph


This is bold text in a paragraph

XSLT - Dynamic Grouping Using the Muenchian Method

This blog post describes a full example on how to group XML using the Muenchian Method, using: keys, generate-id() and external parameters [xsl:param] passed into the XSL stylesheet.

My original requirement was to pass in parameters to the XSL file using C# and transform the XML on-the-fly. Therefore, I used xsl:param tags to hold the input values and generate a portion of XML based on this. I had a few problems matching templates and keys based on parameters, as this is not valid.
I therefore, generated by keys based on all the data and filtered the data I required based on the parameters.


The following code describes a full example using: Parameters, Keys and grouping. The aim is to dynamically group events by year and sort them in a descending order.


Grouping.xml
<?xml version="1.0"?>
<?xml-stylesheet type="text/xml" href="stylesheet.xsl"?>
<theme>
    <styles>        
        <style id="style1">
        <title>Test Style 1</title>
            <elements>
                <element id="element1">
                    <title>Test Element 1</title>    
                    <events>
                        <event id="event11" date="2008-10-13">
                            <title>Test Event 1</title>
                        </event>                        
                        <event id="event12" date="2009-03-18">
                            <title>Test Event 2</title>
                        </event>
                        <event id="event13" date="2009-02-26">
                            <title>Test Event 3</title>
                        </event>
                        <event id="event14" date="2011-04-12">
                            <title>Test Event 4</title>
                        </event>
                        <event id="event15" date="2010-01-01">
                            <title>Test Event 5</title>
                        </event>
                        <event id="event16" date="2010-07-06">
                            <title>Test Event 6</title>
                        </event>
                    </events>                                            
                </element>
                <element id="element2">
                    <title>Test Element 2</title>    
                    <events>
                        <event id="event21" date="2001-10-13">
                            <title>Test Event 1</title>
                        </event>                        
                        <event id="event22" date="2001-03-18">
                            <title>Test Event 2</title>
                        </event>
                        <event id="event23" date="2007-02-26">
                            <title>Test Event 3</title>
                        </event>
                        <event id="event24" date="2008-04-12">
                            <title>Test Event 4</title>
                        </event>
                        <event id="event25" date="2010-01-01">
                            <title>Test Event 5</title>
                        </event>
                        <event id="event26" date="2010-07-06">
                            <title>Test Event 6</title>
                        </event>
                    </events>                                            
                </element>
            </elements>                
        </style>
        <style id="style2">
        <title>Test Style 2</title>
            <elements>
                <element id="element3">
                    <title>Test Element 3</title>    
                    <events>
                        <event id="event31" date="2003-10-13">
                            <title>Test Event 1</title>
                        </event>                        
                        <event id="event32" date="2007-03-18">
                            <title>Test Event 2</title>
                        </event>
                        <event id="event33" date="2007-02-26">
                            <title>Test Event 3</title>
                        </event>
                        <event id="event34" date="2010-04-12">
                            <title>Test Event 4</title>
                        </event>
                        <event id="event35" date="2010-01-01">
                            <title>Test Event 5</title>
                        </event>
                        <event id="event36" date="2010-07-06">
                            <title>Test Event 6</title>
                        </event>
                    </events>                                            
                </element>                
            </elements>                
        </style>
    </styles>
</theme>





Stylesheet.xsl
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ms="urn:schemas-microsoft-com:xslt">
 
  <!-- Parameters -->
  <xsl:param name="style">style1</xsl:param>
  <xsl:param name="element">element1</xsl:param>
 
  <!-- Create a key based on the YYYY portion of the start_date attribute -->
  <xsl:key
    name="keyYear"
    match="events/event"
    use="substring(@date, 1, 4)"
  />
  <xsl:key
    name="keyEventID"
    match="events/event"
    use="@id"
  />
 
 
  <!-- Match default template -->
  <xsl:template match="/">
    <xsl:apply-templates select="/theme/styles/style[@id=$style]/elements/element[@id=$element]/events" />
  </xsl:template>
 
 
  <!-- matches on events, so only outputs once [per stream] -->
  <xsl:template match="events">
 
    <table>
      <xsl:for-each select="//event[generate-id()=generate-id(key('keyYear', substring(@date, 1, 4))[1])]">
        <xsl:sort select="substring(@date, 1, 4)" order="descending" />
 
        <!-- Save the Year to a variable -->
        <xsl:variable name="varCurrentYear"><xsl:value-of select="substring(@date, 1, 4)" /></xsl:variable>
        
        <!-- Select all the events belonging to the year -->
        <xsl:variable name="lstEventPerYear" select="/theme/styles/style[@id=$style]/elements/element[@id=$element]/events/event[substring(@date, 1, 4)=$varCurrentYear]" />
        
        <!-- Store child count -->
        <xsl:variable name="varChildCount"><xsl:value-of select="count($lstEventPerYear[generate-id(.) = generate-id(key('keyEventID', @id)[1])])" /></xsl:variable>
 
        <!-- Output year only if there are sub elements -->
        <xsl:if test="$varChildCount > 0">
            <tr>
                <td>
                    <h3><xsl:value-of select="$varCurrentYear" /></h3>
                </td>
            </tr>
          
            <xsl:for-each select="$lstEventPerYear[generate-id(.) = generate-id(key('keyEventID', @id)[1])]">
            <xsl:sort select="title" />     
            <tr>
                <td>
                    <h4>
                        <xsl:value-of select="title" />
                          <xsl:text disable-output-escaping="yes">&amp;nbsp;</xsl:text>
                        [<xsl:value-of select="ms:format-date(@date, 'dd MMM yyyy')"/>]
                    </h4>
                </td>
            </tr>             
            </xsl:for-each>
        </xsl:if>
      </xsl:for-each>
    </table>
  </xsl:template>
    
</xsl:stylesheet>