From 8247c5575a3228383b86c0bd902cb7be550c3a0b Mon Sep 17 00:00:00 2001 From: Rob Moore Date: Sat, 10 Oct 2015 23:26:47 +0800 Subject: [PATCH] Added HtmlHelper extesions for creating dependent HTMLHelper against sub-property or a random model type (with and without instance) Partially addresses #95 and #107 --- ChameleonForms/ChameleonForms.csproj | 1 + ChameleonForms/HtmlHelperExtensions.cs | 91 ++++++++++++++++++++++++++ 2 files changed, 92 insertions(+) create mode 100644 ChameleonForms/HtmlHelperExtensions.cs diff --git a/ChameleonForms/ChameleonForms.csproj b/ChameleonForms/ChameleonForms.csproj index c0da423a..2f0ed2a5 100644 --- a/ChameleonForms/ChameleonForms.csproj +++ b/ChameleonForms/ChameleonForms.csproj @@ -91,6 +91,7 @@ + diff --git a/ChameleonForms/HtmlHelperExtensions.cs b/ChameleonForms/HtmlHelperExtensions.cs new file mode 100644 index 00000000..7d0ae1c9 --- /dev/null +++ b/ChameleonForms/HtmlHelperExtensions.cs @@ -0,0 +1,91 @@ +using System; +using System.Linq.Expressions; +using System.Web.Mvc; +using System.Web.Routing; + +namespace ChameleonForms +{ + /// + /// Extension methods against HtmlHelper. + /// + // http://maxtoroq.github.io/2012/07/patterns-for-aspnet-mvc-plugins-viewmodels.html + public static class HtmlHelperExtensions + { + /// + /// Creates a HTML helper from a parent model to use a sub-property as it's model. + /// + /// The model of the parent type + /// The model of the sub-property to use + /// The parent HTML helper + /// The sub-property to use + /// Whether to set field names to bind to the parent model type (true) or the sub-property type (false) + /// A HTML helper against the sub-property + public static DisposableHtmlHelper For(this HtmlHelper helper, + Expression> propertyFor, bool bindFieldsToParent) + { + return helper.For(propertyFor.Compile().Invoke(helper.ViewData.Model), + bindFieldsToParent ? ExpressionHelper.GetExpressionText(propertyFor) : null); + } + + /// + /// Creates a HTML helper based on another HTML helper against a different model type. + /// + /// The model type to create a helper for + /// The original HTML helper + /// An instance of the model type to use as the model + /// A prefix value to use for field names + /// The HTML helper against the other model type + public static DisposableHtmlHelper For(this HtmlHelper htmlHelper, TModel model = default(TModel), string htmlFieldPrefix = null) + { + var viewDataContainer = CreateViewDataContainer(htmlHelper.ViewData, model); + + var templateInfo = viewDataContainer.ViewData.TemplateInfo; + + if (!String.IsNullOrEmpty(htmlFieldPrefix)) + templateInfo.HtmlFieldPrefix = templateInfo.GetFullHtmlFieldName(htmlFieldPrefix); + + var viewContext = htmlHelper.ViewContext; + var newViewContext = new ViewContext(viewContext.Controller.ControllerContext, viewContext.View, viewDataContainer.ViewData, viewContext.TempData, viewContext.Writer); + + return new DisposableHtmlHelper(newViewContext, viewDataContainer, htmlHelper.RouteCollection); + } + + static IViewDataContainer CreateViewDataContainer(ViewDataDictionary viewData, object model) + { + + var newViewData = new ViewDataDictionary(viewData) + { + Model = model + }; + + newViewData.TemplateInfo = new TemplateInfo + { + HtmlFieldPrefix = newViewData.TemplateInfo.HtmlFieldPrefix + }; + + return new ViewDataContainer + { + ViewData = newViewData + }; + } + + class ViewDataContainer : IViewDataContainer + { + + public ViewDataDictionary ViewData { get; set; } + } + } + + /// + /// HTML helper that can be created in a using block. + /// + /// The model type of the HTML helper + public class DisposableHtmlHelper : HtmlHelper, IDisposable + { + /// + public DisposableHtmlHelper(ViewContext viewContext, IViewDataContainer viewDataContainer) : base(viewContext, viewDataContainer) {} + /// + public DisposableHtmlHelper(ViewContext viewContext, IViewDataContainer viewDataContainer, RouteCollection routeCollection) : base(viewContext, viewDataContainer, routeCollection) {} + public void Dispose() {} + } +}