Click here to Skip to main content
15,895,812 members
Articles / Web Development / ASP.NET

ASP.NET DaST to wrestle with MVC and WebForms

Rate me:
Please Sign up or sign in to vote.
5.00/5 (2 votes)
26 Mar 2013CPOL82 min read 23.2K   152   11  
DaST is a new architectural pattern for building highly dynamic Web 2.0 applications. A web page is rendered as a set of randomly nested rectangles where each rectangle is controlled individually and every combination of rectangles can be partially updated via AJAX.
// ******************************************************************************
// ******************************************************************************
// ASP.NET DaST Rendering Engine
// Copyright (C) 2011 Roman Gubarenko
// Project Home: http://aspnetdast.sourceforge.net/
// ******************************************************************************
// ******************************************************************************
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//
// ******************************************************************************
// Primary Contact: rgubarenko@gmail.com
// Learn More: http://aspnetdast.sourceforge.net/
// Project Repository: http://sourceforge.net/projects/aspnetdast/
// ******************************************************************************


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace AspNetDaST.Web.Private
{
  internal class ScopeTreeRenderer
  {
    private ScopeTreeModel _scopeTree;
    private Stream _output;

    internal ScopeTreeRenderer(ScopeTreeModel scopeTree)
    {
      _scopeTree = scopeTree;
      _output = null;
    }


    public void RenderSubtree(PathExact renderPath, Stream outputStream)
    {
      _output = outputStream;

      var startNode = _scopeTree.GetNode(renderPath);
      RenderSubtreeRec(renderPath, startNode, true);
    }

    private void RenderSubtreeRec(PathExact renderPath, TreeNodeScope scopeNode, bool bNeedContainer)
    {   
      // if current node is a controller, then rebuild the controller model
      if (scopeNode.IsControllerRoot)
      {
        _scopeTree.RebuildControllerModel(scopeNode.Controller, renderPath);
      }
   
      // get scope data for the current rendering path
      var scopeData = _scopeTree.GetScopeDataBag(renderPath);

      // set flag to indicate that current scope instance is 
      // processed by the renderer and needs to be serialized
      scopeData.Validated = true;

      
      // 
      // Scope data binding
      //

      // do databinding only if current scope is visible
      if (scopeData.RenderType == ScopeRenderType.Normal)
      {
        _scopeTree.InvokeDataBindingHandler(scopeNode, renderPath);
      }

      
      //
      // Render scope contents
      //

      // visible scope outputs all its content
      if (scopeData.RenderType == ScopeRenderType.Normal)
      {
        if (!bNeedContainer) RenderContainerStartTag(scopeNode, scopeData);

        // TODO: revisit how FromNodeIdx and StopNodeIdx values are set
        // render if it's not an empty scope
        if (scopeNode.Nodes.Count > 0) 
        {
          // TODO: make sure that there are only HTML nodes until limiter
          // output raw text before scope-from limiter
          for (int i = 0; i < scopeNode.FromNodeIdx; i++)
          {
            var childHtmlNode = (TreeNodeHtml)scopeNode.Nodes[i];
            RenderRawText(childHtmlNode.HtmlText.ToString());
          }

          // rendered content between scope-from and scope-stop on each axis 
          foreach (var axis in scopeData.DataBinding.AxisBindings)
          {
            for (int nodeIdx = scopeNode.FromNodeIdx; nodeIdx <= scopeNode.StopNodeIdx; nodeIdx++)
            {
              // on child scope call the rendering function recursively
              if (scopeNode.Nodes[nodeIdx] is TreeNodeScope)
              {
                var childScopeNode = (TreeNodeScope)scopeNode.Nodes[nodeIdx];
                var childPath = renderPath.Append(axis.AxisNum, childScopeNode.ScopeID);
                RenderSubtreeRec(childPath, childScopeNode, false);
              }
              // otherwise, child node is an html template fragment that has to be rendered
              else
              {
                var childHtmlNode = (TreeNodeHtml)scopeNode.Nodes[nodeIdx];
                RenderTemplateFragment(childHtmlNode.HtmlText.ToString(), axis.Renderers, scopeData);
              }
            }
          }

          // output raw text after scope-stop limiter
          for (int i = scopeNode.StopNodeIdx + 1; i < scopeNode.Nodes.Count; i++)
          {
            var childHtmlNode = (TreeNodeHtml)scopeNode.Nodes[i];
            RenderRawText(childHtmlNode.HtmlText.ToString());
          }
        }

        if (!bNeedContainer) RenderContainerEndTag(scopeNode);
      }
      // empty scope output is only a container
      else if (scopeData.RenderType == ScopeRenderType.Empty)
      {
        // render scope container only
        if (!bNeedContainer)
        {
          RenderContainerStartTag(scopeNode, scopeData);
          RenderContainerEndTag(scopeNode);
        }
      }
      // hidden scope does not output anything
      else if (scopeData.RenderType == ScopeRenderType.None)
      {
        // dont output anything 
      }
    }


    private void RenderRawText(string text)
    {
      byte[] bytes = Encoding.ASCII.GetBytes(text);
      _output.Write(bytes, 0, bytes.Length);
    }

    private void RenderTemplateFragment(string template, List<HtmlContentRenderer> rendererSet, ScopeDataBag scopeData)
    {
      // create new list of renderers and add some predefined replacements to it  
      var renderers = new List<HtmlContentRenderer>();
      if (rendererSet != null) renderers.AddRange(rendererSet);
     
      // add some system replacements
      renderers.Add(new HtmlContentRenderer_SingleReplacement("{scope:ClientID}", scopeData.ScopeClientID));

      // apply sequence of renderers
      foreach (var renderer in renderers)
      {
        template = renderer.RenderContent(template);
      }

      RenderRawText(template);
    }

    private void RenderContainerStartTag(TreeNodeScope scopeNode, ScopeDataBag scopeData)
    {
      // take only attributes without 'dast:' prefix
      var attrs = from attrName in scopeNode.TagAttributes.Keys.Cast<string>()
                  where !attrName.StartsWith("dast:", StringComparison.OrdinalIgnoreCase)
                  select new { Name = attrName, Value = scopeNode.TagAttributes[attrName] };

      StringBuilder sb = new StringBuilder();
      foreach (var attr in attrs) sb.AppendFormat(" {0}=\"{1}\"", attr.Name, attr.Value);

      // construct final tag
      string strTag = string.Format("<{0} id=\"{1}\"{2}>",
        scopeNode.TagOriginalName.ToLower(),
        scopeData.ScopeClientID,
        sb.ToString());

      RenderRawText(strTag);
    }

    private void RenderContainerEndTag(TreeNodeScope scopeNode)
    {
      string strTag = string.Format("</{0}>", scopeNode.TagOriginalName.ToLower());

      RenderRawText(strTag);
    }
  }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Canada Canada
Software Architect with over 15 years in IT field. Started with deep math and C++ Computer Vision software. Currently in .NET and PHP web development. Creator of DaST pattern, open-source frameworks, and plugins. Interested in cutting Edge IT, open-source, Web 2.0, .NET, MVC, C++, Java, jQuery, Mobile tech, and extreme sports.

Comments and Discussions