If you search for rendering partial views to strings you'll also come across some good leads. That's what I did to come up with the following extension method for the ControllerBase class:
public static string RenderPartialViewToString( this ControllerBase controller, string partialPath, ViewDataDictionary viewData = null )
{
if( string.IsNullOrEmpty(partialPath) )
partialPath = controller.ControllerContext.RouteData.GetRequiredString("action");
using( StringWriter sw = new StringWriter() )
{
ViewEngineResult viewResult = ViewEngines.Engines.FindPartialView(controller.ControllerContext, partialPath);
ViewContext viewContext = new ViewContext(controller.ControllerContext,
viewResult.View,
( viewData == null ) ? controller.ViewData : viewData,
controller.TempData,
sw);
// copy retVal state items to the html helper
foreach( var item in viewContext.Controller.ViewData.ModelState )
{
if( !viewContext.ViewData.ModelState.Keys.Contains(item.Key) )
viewContext.ViewData.ModelState.Add(item);
}
viewResult.View.Render(viewContext, sw);
return sw.GetStringBuilder().ToString();
}
}
Conceptually, the procedure to follow involves using the ViewEngines defined for your app to find a partial view by its name. You then create a ViewContext off of that partial, and copy the various model state properties over to it.
The code assigns an optional ViewDataDictionary that you can provide to the ViewContext. If you don't provide the ViewDataDictionary it grabs the ViewDataDictionary defined for the controller that it's being called against.
What this means is that you can either define ViewData values (or ViewBag properties) directly in your controller and then call the extension method -- which will apply those ViewData/ViewBag properties to the partial when it gets rendered -- or you can create a separate ViewDataDictionary object in your action method and pass it to the extension method. The first is quicker/easier, but it "pollutes" the ViewData for your action method, while the second takes a little longer to set up but lets you keep your partial view data separate from your action method's ViewData.