65.9K
CodeProject is changing. Read more.
Home

Bind PartialView / RenderPartialView to Model Child Objects Automatically

starIconstarIconstarIconstarIconstarIcon

5.00/5 (1 vote)

Aug 23, 2017

CPOL
viewsIcon

8732

Automatically bind MVC HTML RenderPartial and Partial view pages to your model's child objects without having to use HTMLFieldPrefix.

Introduction

Tired of fighting with PartialViews that won't bind to your model? This simple set of functions handles automatically fixing the HTML prefix and allows PartialPages to bind without any further effort.

Using the Code

Simply add the following class into your solution and then instead of using Html.Partial or Html.RenderPartial, call it like the functions below:

Usage:

@Html.RenderPartialFixChildPrefix("_PartialView", model.ChildObject)

or:

@Html.PartialFixChildPrefix("_PartialView", model.ChildObject)

Extension Class:

 public static class PartialExtensions
    {
        public static void RenderPartialFixChildPrefix
        (this HtmlHelper htmlHelper, string partialViewName, 
         object model, ViewDataDictionary viewData = null)
        {
            if (model == null)
            {
                throw new Exception("Null models cannot be processed correctly. 
                                          Initialize empty object for create calls.");
            }

            using (StringWriter writer = new StringWriter(CultureInfo.CurrentCulture))
            {
                string oldPrefix = FixPartialPrefixIfNeeded(htmlHelper, model, ref viewData);
                htmlHelper.RenderPartial(partialViewName, model, viewData);
                viewData.TemplateInfo.HtmlFieldPrefix = oldPrefix;
            }
        }

        private static string FixPartialPrefixIfNeeded
        (HtmlHelper htmlHelper, object model, ref ViewDataDictionary viewData)
        {
            var oldPrefix = viewData == null ? "" : viewData.TemplateInfo.HtmlFieldPrefix;

            // Only change prefix if we're using a difference Model for the partial view
            if (model != htmlHelper.ViewData.Model)
            {
                var modelPropertyName = GetPropertyNameFromParentObj(htmlHelper.ViewData.Model, model);

                if (viewData == null)
                {
                    viewData = new ViewDataDictionary()
                    {
                        TemplateInfo = new TemplateInfo()
                    };
                }

                viewData.TemplateInfo.HtmlFieldPrefix = modelPropertyName;
            }
            return oldPrefix;
        }

        public static MvcHtmlString PartialFixChildPrefix
        (this HtmlHelper htmlHelper, string partialViewName, 
         object model, ViewDataDictionary viewData = null)
        {
            if (model == null)
            {
                throw new Exception("Null models cannot be processed correctly. 
                Initialize empty object for create calls.");
            }

            using (StringWriter writer = new StringWriter(CultureInfo.CurrentCulture))
            {
                string oldPrefix = FixPartialPrefixIfNeeded(htmlHelper, model, ref viewData);
                var result = htmlHelper.Partial(partialViewName, model, viewData);
                viewData.TemplateInfo.HtmlFieldPrefix = oldPrefix;
                return result;
            }
        }

        private static string GetPropertyNameFromParentObj(object parentObj, object objToFind)
        {
            var props = parentObj.GetType().GetProperties();

            foreach (var p in props)
            {
                string name = p.Name;
                var value = parentObj.GetType().GetProperty(p.Name).GetValue(parentObj, null);

                if (objToFind == value)
                {
                    return p.Name;
                }
            }

            return "";
        }
}

History

  • 8/22/2017 -- Initial posting - Allen Biehle c/o Vertical AIT (Vertical Automation and Information Technology, LLC)