using System;
using System.Web;
using System.Web.Mvc;
using System.IO;
using System.Web.Routing;
using Westwind.Web.Mvc.Properties;
namespace Westwind.Web.Mvc
{
///
/// Class that renders MVC views to a string using the
/// standard MVC View Engine to render the view.
///
/// Requires that ASP.NET HttpContext is present to
/// work, but works outside of the context of MVC
///
public class ViewRenderer
{
///
/// Required Controller Context
///
protected ControllerContext Context { get; set; }
///
/// Initializes the ViewRenderer with a Context.
///
///
/// If you are running within the context of an ASP.NET MVC request pass in
/// the controller's context.
/// Only leave out the context if no context is otherwise available.
///
public ViewRenderer(ControllerContext controllerContext = null)
{
// Create a known controller from HttpContext if no context is passed
if (controllerContext == null)
{
if (HttpContext.Current != null)
controllerContext = CreateController().ControllerContext;
else
throw new InvalidOperationException(
"ViewRenderer must run in the context of an ASP.NET " +
"Application and requires HttpContext.Current to be present.");
}
Context = controllerContext;
}
///
/// Renders a full MVC view to a string. Will render with the full MVC
/// View engine including running _ViewStart and merging into _Layout
///
///
/// The path to the view to render. Either in same controller, shared by
/// name or as fully qualified ~/ path including extension
///
/// The model to render the view with
/// String of the rendered view or null on error
public string RenderViewToString(string viewPath, object model = null)
{
return RenderViewToStringInternal(viewPath, model, false);
}
///
/// Renders a full MVC view to a writer. Will render with the full MVC
/// View engine including running _ViewStart and merging into _Layout
///
///
/// The path to the view to render. Either in same controller, shared by
/// name or as fully qualified ~/ path including extension
///
/// The model to render the view with
/// String of the rendered view or null on error
public void RenderView(string viewPath, object model, TextWriter writer)
{
RenderViewToWriterInternal(viewPath, writer, model, false);
}
///
/// Renders a partial MVC view to string. Use this method to render
/// a partial view that doesn't merge with _Layout and doesn't fire
/// _ViewStart.
///
///
/// The path to the view to render. Either in same controller, shared by
/// name or as fully qualified ~/ path including extension
///
/// The model to pass to the viewRenderer
/// String of the rendered view or null on error
public string RenderPartialViewToString(string viewPath, object model = null)
{
return RenderViewToStringInternal(viewPath, model, true);
}
///
/// Renders a partial MVC view to given Writer. Use this method to render
/// a partial view that doesn't merge with _Layout and doesn't fire
/// _ViewStart.
///
///
/// The path to the view to render. Either in same controller, shared by
/// name or as fully qualified ~/ path including extension
///
/// The model to pass to the viewRenderer
/// Writer to render the view to
public void RenderPartialView(string viewPath, object model, TextWriter writer)
{
RenderViewToWriterInternal(viewPath, writer, model, true);
}
///
/// Renders a full MVC view to a writer. Will render with the full MVC
/// View engine including running _ViewStart and merging into _Layout
///
///
/// The path to the view to render. Either in same controller, shared by
/// name or as fully qualified ~/ path including extension
///
/// The model to pass to the viewRenderer
/// Active Controller context
/// String of the rendered view or null on error
public static string RenderView(string viewPath, object model = null,
ControllerContext controllerContext = null)
{
ViewRenderer renderer = new ViewRenderer(controllerContext);
return renderer.RenderViewToString(viewPath, model);
}
///
/// Renders a full MVC view to a writer. Will render with the full MVC
/// View engine including running _ViewStart and merging into _Layout
///
///
/// The path to the view to render. Either in same controller, shared by
/// name or as fully qualified ~/ path including extension
///
/// The model to pass to the viewRenderer
/// Writer to render the view to
/// Active Controller context
/// String of the rendered view or null on error
public static void RenderView(string viewPath, TextWriter writer, object model,
ControllerContext controllerContext)
{
ViewRenderer renderer = new ViewRenderer(controllerContext);
renderer.RenderView(viewPath, model, writer);
}
///
/// Renders a full MVC view to a writer. Will render with the full MVC
/// View engine including running _ViewStart and merging into _Layout
///
///
/// The path to the view to render. Either in same controller, shared by
/// name or as fully qualified ~/ path including extension
///
/// The model to pass to the viewRenderer
/// Active Controller context
/// optional out parameter that captures an error message instead of throwing
/// String of the rendered view or null on error
public static string RenderView(string viewPath, object model,
ControllerContext controllerContext,
out string errorMessage)
{
errorMessage = null;
try
{
ViewRenderer renderer = new ViewRenderer(controllerContext);
return renderer.RenderViewToString(viewPath, model);
}
catch (Exception ex)
{
errorMessage = ex.GetBaseException().Message;
}
return null;
}
///
/// Renders a full MVC view to a writer. Will render with the full MVC
/// View engine including running _ViewStart and merging into _Layout
///
///
/// The path to the view to render. Either in same controller, shared by
/// name or as fully qualified ~/ path including extension
///
/// The model to pass to the viewRenderer
/// Active Controller context
/// Writer to render the view to
/// optional out parameter that captures an error message instead of throwing
/// String of the rendered view or null on error
public static void RenderView(string viewPath, object model, TextWriter writer,
ControllerContext controllerContext,
out string errorMessage)
{
errorMessage = null;
try
{
ViewRenderer renderer = new ViewRenderer(controllerContext);
renderer.RenderView(viewPath, model, writer);
}
catch (Exception ex)
{
errorMessage = ex.GetBaseException().Message;
}
}
///
/// Renders a partial MVC view to string. Use this method to render
/// a partial view that doesn't merge with _Layout and doesn't fire
/// _ViewStart.
///
///
/// The path to the view to render. Either in same controller, shared by
/// name or as fully qualified ~/ path including extension
///
/// The model to pass to the viewRenderer
/// Active controller context
/// String of the rendered view or null on error
public static string RenderPartialView(string viewPath, object model = null,
ControllerContext controllerContext = null)
{
ViewRenderer renderer = new ViewRenderer(controllerContext);
return renderer.RenderPartialViewToString(viewPath, model);
}
///
/// Renders a partial MVC view to string. Use this method to render
/// a partial view that doesn't merge with _Layout and doesn't fire
/// _ViewStart.
///
///
/// The path to the view to render. Either in same controller, shared by
/// name or as fully qualified ~/ path including extension
///
/// The model to pass to the viewRenderer
/// Active controller context
/// Text writer to render view to
/// optional output parameter to receive an error message on failure
public static void RenderPartialView(string viewPath, TextWriter writer, object model = null,
ControllerContext controllerContext = null)
{
ViewRenderer renderer = new ViewRenderer(controllerContext);
renderer.RenderPartialView(viewPath, model, writer);
}
///
/// Internal method that handles rendering of either partial or
/// or full views.
///
///
/// The path to the view to render. Either in same controller, shared by
/// name or as fully qualified ~/ path including extension
///
/// Model to render the view with
/// Determines whether to render a full or partial view
/// Text writer to render view to
protected void RenderViewToWriterInternal(string viewPath, TextWriter writer, object model = null, bool partial = false)
{
// first find the ViewEngine for this view
ViewEngineResult viewEngineResult = null;
if (partial)
viewEngineResult = ViewEngines.Engines.FindPartialView(Context, viewPath);
else
viewEngineResult = ViewEngines.Engines.FindView(Context, viewPath, null);
if (viewEngineResult == null)
throw new FileNotFoundException();
// get the view and attach the model to view data
var view = viewEngineResult.View;
Context.Controller.ViewData.Model = model;
var ctx = new ViewContext(Context, view,
Context.Controller.ViewData,
Context.Controller.TempData,
writer);
view.Render(ctx, writer);
}
///
/// Internal method that handles rendering of either partial or
/// or full views.
///
///
/// The path to the view to render. Either in same controller, shared by
/// name or as fully qualified ~/ path including extension
///
/// Model to render the view with
/// Determines whether to render a full or partial view
/// String of the rendered view
private string RenderViewToStringInternal(string viewPath, object model,
bool partial = false)
{
// first find the ViewEngine for this view
ViewEngineResult viewEngineResult = null;
if (partial)
viewEngineResult = ViewEngines.Engines.FindPartialView(Context, viewPath);
else
viewEngineResult = ViewEngines.Engines.FindView(Context, viewPath, null);
if (viewEngineResult == null || viewEngineResult.View == null)
throw new FileNotFoundException(Resources.ViewCouldNotBeFound);
// get the view and attach the model to view data
var view = viewEngineResult.View;
Context.Controller.ViewData.Model = model;
string result = null;
using (var sw = new StringWriter())
{
var ctx = new ViewContext(Context, view,
Context.Controller.ViewData,
Context.Controller.TempData,
sw);
view.Render(ctx, sw);
result = sw.ToString();
}
return result;
}
///
/// Creates an instance of an MVC controller from scratch
/// when no existing ControllerContext is present
///
/// Type of the controller to create
/// Controller for T
/// thrown if HttpContext not available
public static T CreateController(RouteData routeData = null, params object[] parameters)
where T : Controller, new()
{
// create a disconnected controller instance
T controller = (T) Activator.CreateInstance(typeof (T), parameters);
// get context wrapper from HttpContext if available
HttpContextBase wrapper = null;
if (HttpContext.Current != null)
wrapper = new HttpContextWrapper(System.Web.HttpContext.Current);
else
throw new InvalidOperationException(
"Can't create Controller Context if no active HttpContext instance is available.");
if (routeData == null)
routeData = new RouteData();
// add the controller routing if not existing
if (!routeData.Values.ContainsKey("controller") && !routeData.Values.ContainsKey("Controller"))
routeData.Values.Add("controller", controller.GetType().Name
.ToLower()
.Replace("controller", ""));
controller.ControllerContext = new ControllerContext(wrapper, routeData, controller);
return controller;
}
}
///
/// Empty MVC Controller instance used to
/// instantiate and provide a new ControllerContext
/// for the ViewRenderer
///
public class EmptyController : Controller
{
}
}