Extending View Functionality

Best way to extend view functionality is to add fluent extension methods to IDocumentHelper or IDocument and allow the actual views to opt in for your actual view processing. Example of this is how the data-binding is implemented:

/// <summary>
    /// Extension methods for databinding
    /// </summary>
    public static class DataBindingExtensions
    {
 
        public const string EvalAttribute = "eve-eval";
        /// <summary>
        /// Processes the html document's tags with EvalAttribute ("eve-eval"), 
        /// by evaluating the given attribute value on the given Model, and inserting the result into the tag
        /// </summary>
        /// <param name="documentHelper">Document to attach this functionality on</param>
        /// <param name="Model">Model on which the evaluation is done</param>
        /// <returns></returns>
        public static IDocument ProcessEvals(this IDocument documentobject Model)
        {
            document.ProcessNodesWithAttribute(EvalAttribute, new Func<IDocumentNode, string>(a =>
            {
                //var evalPath = a.Attributes[EvalAttribute].Value;
                var evalPath = a.GetAttributeValue(EvalAttribute);
                //eval the new value on the current model
                if (Model != null && !string.IsNullOrWhiteSpace(evalPath))
                {
                    var evalValue = ViewDataEvaluator.Eval(Model, evalPath.Trim());
                    return evalValue == null ? string.Empty : evalValue.Value.ToString();
                }
                return null;
            }
               ));
            return document;
        }
    }

Opt-in processing

You have to call these extension methods. This design choice is intentional as you should only process as much of the view as needed, and opt-in for any additional processing. The EmbeddedView class implements the method BeforeProcessView, which you can override and use to execute any rendering extension you like. This method allows for inheritance, so for example you can create a sub-class of EmbeddedView that uses data-binding by default.

public abstract class DataBindingView<T> : EmbeddedView<T>
    {
        protected override void BeforeProcessView(ViewContext viewContext)
        {
            base.BeforeProcessView(viewContext);
            this.HtmlDocument.Document.ProcessEvals(this.Model);
        }
    }

ViewBag extension

Here's another example this time for viewbags, super similar to the data-binding implementation:

public static IDocumentHelper<T> ProcessViewBag<T>(this IDocumentHelper<T> documentHelper, ViewContext viewContextwhere T : IDocument
       {
           if (viewContext == null)
               throw new ArgumentNullException("viewContext");
           if (viewContext.ViewBag == null)
               throw new ArgumentNullException("viewContext.ViewBag");
           //Sequential processing is required because the evaluation needs the http context that 
           // parallel implementation does not have on all threads
           documentHelper.ProcessNodesWithAttributeSequential(ViewBagAttribute, new Func<IDocumentNode, string>(a =>
           {
               var dynaPath = a.GetAttributeValue(ViewBagAttribute);
               var value = GetProperty(viewContext.ViewBag, dynaPath);
               //ViewDataEvaluator.Eval(viewContext.ViewBag, dynaPath);
               return value?.ToString();
           }
              ));
           return documentHelper;
       }

Localization Extension

Localization is just another extension you can opt in for like databinding previously:

public static class Localization
   {
       public const string LocalAttribute = "eve-local";
       public static IDocumentHelper ProcessLocals(this IDocumentHelper documentHelper, ResourceManager resourceManager, CultureInfo culture)
       {
           documentHelper.ProcessNodesWithAttribute(LocalAttribute, new Func<HtmlNodestring>(a =>
           {
               var resourceKey = a.Attributes[LocalAttribute].Value;
               return resourceManager.GetString(resourceKey,culture);
           }
              ));
           return documentHelper;
       }
   }
First create the appropriate resx files with the necessary content. See source for that.
Then prepare the markups with the localization tag:
<h3 class="h4 mb-2" eve-local="localized1">Sturdy Themes</h3>
                    <p class="text-muted mb-0" eve-local="localized2">Our themes are updated regularly to keep them bug free!</p>

You should set the locale of you page explicitly based on user input before calling the view:
public ActionResult Extensions()
       {
           this.ViewBag.Content = "This content comes from viewbag!!!!";
           Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo("ja-JP");
           return View("eve-Extensions.LandingPage", Models.LandingPageModel.GetSample());
       }

In you page view code you have to opt in for localization:
protected override void BeforeProcessView(ViewContext viewContext)
       {
           base.BeforeProcessView(viewContext);
           this.HtmlDocument.Document
               .ProcessEvals(this.Model);
 
           this.HtmlDocument
               .ProcessViewBag(viewContext)
               .ProcessLocals(Resource.ResourceManager,Thread.CurrentThread.CurrentUICulture);
       }
Click here to see the above in action. Observe the code to see the full example.