Monday, March 26, 2012

Creating Custom Asp.net MVC ViewEngine

In the last post I talked about creating a custom ActionResult, the next question is, what if I want to add my a custom view engine?   As an example lets say your company’s intranet uses multiple platforms (php, java, and now asp.net mvc) and to keep the look and feel constant you use xslt to render your header and footer, a simple solution is to add a xslt View Engine to generate the markup.

First you create a view to parse and output for content
   1: public class XsltView : IView
   2: {
   3:     private readonly XslCompiledTransform _template;
   4:  
   5:     public XsltView(ControllerContext controllerContext, string viewPath)
   6:     {
   7:         _template = new XslCompiledTransform();
   8:         _template.Load(controllerContext.HttpContext.Server.MapPath(viewPath));
   9:     }
  10:  
  11:     public void Render(ViewContext viewContext, TextWriter writer)
  12:     {
  13:         var xmlDoc = viewContext.ViewData.Model as XDocument;
  14:         if(xmlDoc==null)
  15:         {
  16:             var xs = new XmlSerializer(viewContext.ViewData.Model.GetType());
  17:             xmlDoc = new XDocument();
  18:             using (var xWriter = xmlDoc.CreateWriter())
  19:             {
  20:                 xs.Serialize(xWriter, viewContext.ViewData.Model);
  21:             }
  22:         }
  23:         _template.Transform(xmlDoc.CreateReader(),null,writer);
  24:     }
  25: }
then you create a view engine by inheriting from VirtualPathProviderViewEngine and override the CreatePartialView and CreateView methods to call your view
   1: public class XsltViewEngine : VirtualPathProviderViewEngine
   2: {
   3:     protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath)
   4:     {
   5:         return new XsltView(controllerContext, partialPath);
   6:     }
   7:  
   8:     protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath)
   9:     {
  10:         return new XsltView(controllerContext, viewPath);
  11:     }
  12: }

to add the view open global.ascx.cs and modify the application start method to add the new view engine
   1: protected void Application_Start()
   2: {
   3:     AreaRegistration.RegisterAllAreas();
   4:     ViewEngines.Engines.Clear();
   5:     ViewEngines.Engines.Add(new WebFormViewEngine());
   6:     ViewEngines.Engines.Add(new RazorViewEngine());
   7:     ViewEngines.Engines.Add(new XsltViewEngine
   8:         {
   9:             ViewLocationFormats = new [] { "~/Views/{1}/{0}.xslt", "~/Views/Shared/{0}.xslt" },
  10:             PartialViewLocationFormats = new[] { "~/Views/{1}/{0}.xslt", "~/Views/Shared/{0}.xslt" }
  11:         });
  12:     RegisterGlobalFilters(GlobalFilters.Filters);
  13:     RegisterRoutes(RouteTable.Routes);
  14: }
now to use it just add you xslt file to your view folder like you would with any other view and return your serializable object to the the View you’re returning
   1: public ActionResult TransformedView()
   2: {
   3:     var model = new BasicDemoModel{Name = "Demo User"};
   4:     return View(model);
   5: }

for the full source (click here)

No comments: