diff --git a/src/Umbraco.Web/Mvc/RenderModelBinder.cs b/src/Umbraco.Web/Mvc/RenderModelBinder.cs
index f8b1df7520..8d9d33c29c 100644
--- a/src/Umbraco.Web/Mvc/RenderModelBinder.cs
+++ b/src/Umbraco.Web/Mvc/RenderModelBinder.cs
@@ -1,11 +1,14 @@
+using System;
+using System.Globalization;
using System.Web.Mvc;
+using Umbraco.Core;
+using Umbraco.Core.Models;
using Umbraco.Web.Models;
namespace Umbraco.Web.Mvc
{
public class RenderModelBinder : IModelBinder
{
-
///
/// Binds the model to a value by using the specified controller context and binding context.
///
@@ -15,17 +18,90 @@ namespace Umbraco.Web.Mvc
/// The controller context.The binding context.
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
- var requestMatchesType = typeof(RenderModel) == bindingContext.ModelType;
+ if (bindingContext.ModelType != typeof (RenderModel)) return null;
- if (requestMatchesType)
- {
- //get the model from the route data
- if (!controllerContext.RouteData.DataTokens.ContainsKey("umbraco"))
- return null;
- var model = controllerContext.RouteData.DataTokens["umbraco"] as RenderModel;
- return model;
- }
- return null;
+ object model;
+ if (controllerContext.RouteData.DataTokens.TryGetValue("umbraco", out model) == false)
+ return null;
+
+ return model as RenderModel;
}
- }
+
+ // source is the model that we have
+ // modelType is the type of the model that we need to bind to
+ // culture is the CultureInfo that we have, used by RenderModel
+ //
+ // create a model object of the modelType by mapping:
+ // { RenderModel, RenderModel, IPublishedContent }
+ // to
+ // { RenderModel, RenderModel, IPublishedContent }
+ //
+ public static object BindModel(object source, Type modelType, CultureInfo culture)
+ {
+ // null model, return
+ if (source == null) return null;
+
+ // if types already match, return
+ var sourceType = source.GetType();
+ if (sourceType.Inherits(modelType)) // includes ==
+ return source;
+
+ // try to grab the content
+ var sourceContent = source as IPublishedContent; // check if what we have is an IPublishedContent
+ if (sourceContent == null && sourceType.Implements())
+ {
+ // else check if it's an IRenderModel, and get the content
+ sourceContent = ((IRenderModel)source).Content;
+ }
+ if (sourceContent == null)
+ {
+ // else check if we can convert it to a content
+ var attempt1 = source.TryConvertTo();
+ if (attempt1.Success) sourceContent = attempt1.Result;
+ }
+
+ // if we have a content
+ if (sourceContent != null)
+ {
+ // try to grab the culture
+ // using supplied culture by default
+ var sourceRenderModel = source as RenderModel;
+ if (sourceRenderModel != null)
+ culture = sourceRenderModel.CurrentCulture;
+
+ // if model is IPublishedContent, check content type and return
+ if (modelType.Implements())
+ {
+ if ((sourceContent.GetType().Inherits(modelType)) == false)
+ throw new Exception(string.Format("Cannot bind source content type {0} to model type {1}.",
+ sourceContent.GetType(), modelType));
+ return sourceContent;
+ }
+
+ // if model is RenderModel, create and return
+ if (modelType == typeof(RenderModel))
+ {
+ return new RenderModel(sourceContent, culture);
+ }
+
+ // if model is RenderModel, check content type, then create and return
+ if (modelType.IsGenericType && modelType.GetGenericTypeDefinition() == typeof(RenderModel<>))
+ {
+ var targetContentType = modelType.GetGenericArguments()[0];
+ if ((sourceContent.GetType().Inherits(targetContentType)) == false)
+ throw new Exception(string.Format("Cannot bind source content type {0} to model content type {1}.",
+ sourceContent.GetType(), targetContentType));
+ return Activator.CreateInstance(modelType, sourceContent, culture);
+ }
+ }
+
+ // last chance : try to convert
+ var attempt2 = source.TryConvertTo(modelType);
+ if (attempt2.Success) return attempt2.Result;
+
+ // fail
+ throw new Exception(string.Format("Cannot bind source type {0} to model type {1}.",
+ sourceType, modelType));
+ }
+ }
}
\ No newline at end of file
diff --git a/src/Umbraco.Web/Mvc/UmbracoViewPageOfTModel.cs b/src/Umbraco.Web/Mvc/UmbracoViewPageOfTModel.cs
index 480f182855..a7fc18d2e3 100644
--- a/src/Umbraco.Web/Mvc/UmbracoViewPageOfTModel.cs
+++ b/src/Umbraco.Web/Mvc/UmbracoViewPageOfTModel.cs
@@ -141,92 +141,56 @@ namespace Umbraco.Web.Mvc
// maps model
protected override void SetViewData(ViewDataDictionary viewData)
{
- // if view data contains no model, nothing to do
- var source = viewData.Model;
- if (source == null)
- {
- base.SetViewData(viewData);
- return;
- }
+ // capture the model before we tinker with the viewData
+ var viewDataModel = viewData.Model;
- // get the type of the view data model (what we have)
- // get the type of this view model (what we want)
- var sourceType = source.GetType();
- var targetType = typeof (TModel);
+ // map the view data (may change its type, may set model to null)
+ viewData = MapViewDataDictionary(viewData, typeof (TModel));
- // it types already match, nothing to do
- if (sourceType.Inherits()) // includes ==
- {
- base.SetViewData(viewData);
- return;
- }
-
- // try to grab the content
- // if no content is found, return, nothing we can do
- var sourceContent = source as IPublishedContent; // check if what we have is an IPublishedContent
- if (sourceContent == null && sourceType.Implements())
- {
- // else check if it's an IRenderModel => get the content
- sourceContent = ((IRenderModel)source).Content;
- }
- if (sourceContent == null)
- {
- // else check if we can convert it to a content
- var attempt = source.TryConvertTo();
- if (attempt.Success) sourceContent = attempt.Result;
- }
-
- var ok = sourceContent != null;
- if (sourceContent != null)
- {
- // try to grab the culture
- // using context's culture by default
- var culture = UmbracoContext.PublishedContentRequest.Culture;
- var sourceRenderModel = source as RenderModel;
- if (sourceRenderModel != null)
- culture = sourceRenderModel.CurrentCulture;
-
- // reassign the model depending on its type
- if (targetType.Implements())
- {
- // it TModel implements IPublishedContent then use the content
- // provided that the content is of the proper type
- if ((sourceContent is TModel) == false)
- throw new InvalidCastException(string.Format("Cannot cast source content type {0} to view model type {1}.",
- sourceContent.GetType(), targetType));
- viewData.Model = sourceContent;
- }
- else if (targetType == typeof(RenderModel))
- {
- // if TModel is a basic RenderModel just create it
- viewData.Model = new RenderModel(sourceContent, culture);
- }
- else if (targetType.IsGenericType && targetType.GetGenericTypeDefinition() == typeof(RenderModel<>))
- {
- // if TModel is a strongly-typed RenderModel<> then create it
- // provided that the content is of the proper type
- var targetContentType = targetType.GetGenericArguments()[0];
- if ((sourceContent.GetType().Inherits(targetContentType)) == false)
- throw new InvalidCastException(string.Format("Cannot cast source content type {0} to view model content type {1}.",
- sourceContent.GetType(), targetContentType));
- viewData.Model = Activator.CreateInstance(targetType, sourceContent, culture);
- }
- else
- {
- ok = false;
- }
- }
-
- if (ok == false)
- {
- // last chance : try to convert
- var attempt = source.TryConvertTo();
- if (attempt.Success) viewData.Model = attempt.Result;
- }
+ // bind the model (use context culture as default)
+ var culture = UmbracoContext.PublishedContentRequest.Culture;
+ viewData.Model = RenderModelBinder.BindModel(viewDataModel, typeof (TModel), culture);
+ // set the view data
base.SetViewData(viewData);
}
+ // viewData is the ViewDataDictionary (maybe ) that we have
+ // modelType is the type of the model that we need to bind to
+ //
+ // figure out whether viewData can accept modelType else replace it
+ //
+ private static ViewDataDictionary MapViewDataDictionary(ViewDataDictionary viewData, Type modelType)
+ {
+ var viewDataType = viewData.GetType();
+
+ // if viewData is not generic then it is a simple ViewDataDictionary instance and its
+ // Model property is of type 'object' and will accept anything, so it is safe to use
+ // viewData
+ if (viewDataType.IsGenericType == false)
+ return viewData;
+
+ // ensure it is the proper generic type
+ var def = viewDataType.GetGenericTypeDefinition();
+ if (def != typeof(ViewDataDictionary<>))
+ throw new Exception("Could not map viewData of type \"" + viewDataType.FullName + "\".");
+
+ // get the viewData model type and compare with the actual view model type:
+ // viewData is ViewDataDictionary and we will want to assign an
+ // object of type modelType to the Model property of type viewDataModelType, we
+ // need to check whether that is possible
+ var viewDataModelType = viewDataType.GenericTypeArguments[0];
+
+ if (viewDataModelType.IsAssignableFrom(modelType))
+ return viewData;
+
+ // if not possible then we need to create a new ViewDataDictionary
+ var nViewDataType = typeof(ViewDataDictionary<>).MakeGenericType(modelType);
+ var tViewData = new ViewDataDictionary(viewData) { Model = null }; // temp view data to copy values
+ var nViewData = (ViewDataDictionary)Activator.CreateInstance(nViewDataType, tViewData);
+ return nViewData;
+ }
+
///
/// This will detect the end /body tag and insert the preview badge if in preview mode
///