Welcome to MSDN Blogs Sign in | Join | Help

ASP.NET MVC XSLT viewengine

Der er en masse forskellige viewengines, som man kan vælge at bruge, i stedet for standard webformviewengine’en. I MVCContrib ligger der bl.a. NVelocity, Brail, NHaml, XSLT. Jeg syntes XSLT lød semifornuftigt, så den har jeg kigget lidt på, men har ikke kunne finde noget dokumentation/eksempler. Så hvad gør man, man forsøger at skrive en selv. Det viser sig at være ret nemt, eftersom parseren allerede er skrevet for os.

Dsuden giver det ret god mening i min optik med en XSLTViewengine, eftersom man ofte har brug for at formattere og præsentere data, som modtages i XML.

Lad os gå lige til essensen, controlleren.  

public class XsltViewEngine : VirtualPathProviderViewEngine
{
    public XsltViewEngine(string xsltHome)
    {
        //sæt stierne, så controlleren ved hvor viewet kan findes
        ViewLocationFormats = new[] { 
            xsltHome + "{1}/{0}.xslt", 
            xsltHome + "Shared/{0}.xslt" };
        PartialViewLocationFormats = ViewLocationFormats;
    }
    //den helt overordnede metode til at "rendere" et view
    protected override IView CreateView(ControllerContext controllerContext, 
        string viewPath, string masterPath)
    {
        return new XsltTransformView() { ViewPath = viewPath };
    }
    protected override IView CreatePartialView(ControllerContext controllerContext, 
        string partialPath)
    {
        throw new NotImplementedException();
    }
    //lav tranformationen  
    public class XsltTransformView : IView
    {
        public string ViewPath { get; set; }
        public void Render(ViewContext viewContext, System.IO.TextWriter writer)
        {
            XsltArgumentList xslArg = new XsltArgumentList();
            //tilføj alt ViewData til XsltArgumentList, så de kan bruges fra XSLT.
            foreach (var item in viewContext.ViewData)
            {
                xslArg.AddParam(item.Key, "", item.Value);
            }
            //lav XSLT transformationen
            var doc = XDocument.Parse(viewContext.ViewData.Model.ToString());
            var path = viewContext.HttpContext.Server.MapPath(ViewPath);
            var xslt = XDocument.Load(path);
            var transformer = new XslCompiledTransform(true);
            transformer.Load(xslt.CreateReader());
            transformer.Transform(doc.CreateReader(), xslArg, writer);
        }
    }
}

For at kunne bruge min viewengine skal den registreres i Global.asax:

protected void Application_Start()
{
    ViewEngines.Engines.Clear();
    ViewEngines.Engines.Add(new WebFormViewEngine());
    ViewEngines.Engines.Add(new XsltViewEngine("~/Views/"));
    RegisterRoutes(RouteTable.Routes);
}

Det er faktisk det. Så er jeg klar til at bruge min viewengine. Se følgende controller:

public class DemoController : Controller
{
    public ActionResult Index()
    {
        ViewData.Model = XDocument.Load(Server.MapPath("Web.sitemap"));
        ViewData["header"]= "XSLT viewengine";
        return View();
    }
}

Jeg kan nu i min View folder, lave en underfolder der hedder Demo, der matcher min DemoController, og i denne ligger jeg en Index.xslt.

image

Fordi det view der nu skal renderes, matcher det mønster jeg instantierede i constructoren på min controller ("..{1}/{0}.xslt"), så vil min viewengine blive valgt til renderingen.

Mit view (Index.xslt) ud som følgende:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
>
  <xsl:output method="html" omit-xml-declaration="yes"  indent="yes" encoding="utf-8" />
  <xsl:param name="header"></xsl:param>
  
  <xsl:template name="renderSiteMapEntry">
    <xsl:variable name="depth" select="count(ancestor::node())-2"></xsl:variable>
    <xsl:variable name ="url" select="@url"/>
    <tr>
      <td style="text-indent: {$depth*10}px">
        <a href="{$url}">
          <xsl:value-of select="@title"/>
        </a>
      </td>
    </tr> 
  </xsl:template>

  <xsl:template match="/">
    <xsl:if test="$header !=''">
      <xsl:value-of select="$header"/>
    </xsl:if>
    <xsl:for-each  select="//siteMapNode">
      <table>
      <xsl:call-template  name="renderSiteMapEntry"  >
      </xsl:call-template>
      </table>
    </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>

Bemærk at jeg bruger xsl:param, til at føre ViewData over med. I dette tilfælde bruger jeg parameteren header.

En anden pointe er, at du selvfølgelig kan bruge denne XSLT viewengine, fra andre views. Se nu f.eks. dette “webform” view

<asp:Content ID="indexContent" ContentPlaceHolderID="MainContent" runat="server">
    <%= Html.ActionLink("SiteMap", "Index", "Demo") %>
    <% Html.RenderAction<DemoController>( a => a.Index()  ); %>
</asp:Content>

Med Html.RenderAction kan man kalde actions på andre controllere. Ovenstående resulterer i, at min sitemap bliver indsat inline. Html.RenderAction kommer med ASP.NET MVC Futures.

Published Wednesday, August 12, 2009 9:07 AM by henrikwh
Filed under:

Comments

No Comments

New Comments to this post are disabled
 
Page view tracker