How to Render Partial View into a String - asp.net-mvc

I have the following code:
public ActionResult SomeAction()
{
return new JsonpResult
{
Data = new { Widget = "some partial html for the widget" }
};
}
I'd like to modify it so that I could have
public ActionResult SomeAction()
{
// will render HTML that I can pass to the JSONP result to return.
var partial = RenderPartial(viewModel);
return new JsonpResult
{
Data = new { Widget = partial }
};
}
is this possible? Could somebody explain how?
note, I edited the question before posting the solution.

I opted for an extension method like the following for an ASP.NET MVC 4 app. I think it's simpler than some of the suggestions I've seen:
public static class ViewExtensions
{
public static string RenderToString(this PartialViewResult partialView)
{
var httpContext = HttpContext.Current;
if (httpContext == null)
{
throw new NotSupportedException("An HTTP context is required to render the partial view to a string");
}
var controllerName = httpContext.Request.RequestContext.RouteData.Values["controller"].ToString();
var controller = (ControllerBase)ControllerBuilder.Current.GetControllerFactory().CreateController(httpContext.Request.RequestContext, controllerName);
var controllerContext = new ControllerContext(httpContext.Request.RequestContext, controller);
var view = ViewEngines.Engines.FindPartialView(controllerContext, partialView.ViewName).View;
var sb = new StringBuilder();
using (var sw = new StringWriter(sb))
{
using (var tw = new HtmlTextWriter(sw))
{
view.Render(new ViewContext(controllerContext, view, partialView.ViewData, partialView.TempData, tw), tw);
}
}
return sb.ToString();
}
}
It allows me to do the following:
var html = PartialView("SomeView").RenderToString();
Also, this approach persists any Model, ViewBag and other view data for the view.

This is a slightly modified version of an answer that works:
public static string RenderPartialToString(string controlName, object viewData)
{
ViewPage viewPage = new ViewPage() { ViewContext = new ViewContext() };
viewPage.ViewData = new ViewDataDictionary(viewData);
viewPage.Controls.Add(viewPage.LoadControl(controlName));
StringBuilder sb = new StringBuilder();
using (StringWriter sw = new StringWriter(sb))
{
using (HtmlTextWriter tw = new HtmlTextWriter(sw))
{
viewPage.RenderControl(tw);
}
}
return sb.ToString();
}
Usage:
string ret = RenderPartialToString("~/Views/MyController/MyPartial.ascx", model);

DaveDev's answer worked well for me, however when the partial view calls another partial I get "Value cannot be null. Parameter name: view"
Searching around I have made a variant of the following that seems to work well.
public static string RenderPartialToString(string viewName, object model, ControllerContext ControllerContext)
{
if (string.IsNullOrEmpty(viewName))
viewName = ControllerContext.RouteData.GetRequiredString("action");
ViewDataDictionary ViewData = new ViewDataDictionary();
TempDataDictionary TempData = new TempDataDictionary();
ViewData.Model = model;
using (StringWriter sw = new StringWriter())
{
ViewEngineResult viewResult = ViewEngines.Engines.FindPartialView(ControllerContext, viewName);
ViewContext viewContext = new ViewContext(ControllerContext, viewResult.View, ViewData, TempData, sw);
viewResult.View.Render(viewContext, sw);
return sw.GetStringBuilder().ToString();
}
}
Usage:
String result = MVCHelpers.RenderPartialToString("PartialViewHere", Model, ControllerContext)

You can create extension that render view into string.
public static class RenderPartialToStringExtensions
{
/// <summary>
/// render PartialView and return string
/// </summary>
/// <param name="context"></param>
/// <param name="partialViewName"></param>
/// <param name="model"></param>
/// <returns></returns>
public static string RenderPartialToString(this ControllerContext context, string partialViewName, object model)
{
return RenderPartialToStringMethod(context, partialViewName, model);
}
/// <summary>
/// render PartialView and return string
/// </summary>
/// <param name="context"></param>
/// <param name="partialViewName"></param>
/// <param name="viewData"></param>
/// <param name="tempData"></param>
/// <returns></returns>
public static string RenderPartialToString(ControllerContext context, string partialViewName, ViewDataDictionary viewData, TempDataDictionary tempData)
{
return RenderPartialToStringMethod(context, partialViewName, viewData, tempData);
}
public static string RenderPartialToStringMethod(ControllerContext context, string partialViewName, ViewDataDictionary viewData, TempDataDictionary tempData)
{
ViewEngineResult result = ViewEngines.Engines.FindPartialView(context, partialViewName);
if (result.View != null)
{
StringBuilder sb = new StringBuilder();
using (StringWriter sw = new StringWriter(sb))
{
using (HtmlTextWriter output = new HtmlTextWriter(sw))
{
ViewContext viewContext = new ViewContext(context, result.View, viewData, tempData, output);
result.View.Render(viewContext, output);
}
}
return sb.ToString();
}
return String.Empty;
}
public static string RenderPartialToStringMethod(ControllerContext context, string partialViewName, object model)
{
ViewDataDictionary viewData = new ViewDataDictionary(model);
TempDataDictionary tempData = new TempDataDictionary();
return RenderPartialToStringMethod(context, partialViewName, viewData, tempData);
}
}
And then use it in action
[HttpPost]
public ActionResult GetTreeUnit(string id)
{
int _id = id.ExtractID();
string render = ControllerContext.RenderPartialToString("SomeView");
return Json(new { data = render });
}

Works perfect (Only view name required)
* for parameters you can use a model
* can call this from a view also
View side or Calling Side
BuyOnlineCartMaster ToInvoice1 = new BuyOnlineCartMaster(); // for passing parameters
ToInvoice1.CartID = 1;
string HtmlString = RenderPartialViewToString("PartialInvoiceCustomer", ToInvoice1);
Function Generating HTML
public class BuyOnlineCartMaster
{
public int CartID { get; set; }
}
public static string RenderPartialViewToString(string viewName, object model)
{
using (var sw = new StringWriter())
{
BuyOnlineController controller = new BuyOnlineController(); // instance of the required controller (you can pass this as a argument if needed)
// Create an MVC Controller Context
var wrapper = new HttpContextWrapper(System.Web.HttpContext.Current);
RouteData routeData = new RouteData();
routeData.Values.Add("controller", controller.GetType().Name
.ToLower()
.Replace("controller", ""));
controller.ControllerContext = new ControllerContext(wrapper, routeData, controller);
controller.ViewData.Model = model;
var viewResult = ViewEngines.Engines.FindPartialView(controller.ControllerContext, viewName);
var viewContext = new ViewContext(controller.ControllerContext, viewResult.View, controller.ViewData, controller.TempData, sw);
viewResult.View.Render(viewContext, sw);
return sw.ToString();
}
}
Partial View page
#{
var ModelContents = (Common.BuyOnlineCartMaster)ViewData.Model;
}
Your cart id : #(ModelContents.CartID)

Dave,
a variation on the same theme (mvc v1.0):
protected static string RenderPartialToString(Controller controller, string partialName, object model)
{
var vd = new ViewDataDictionary(controller.ViewData);
var vp = new ViewPage
{
ViewData = vd,
ViewContext = new ViewContext(),
Url = new UrlHelper(controller.ControllerContext.RequestContext)
};
ViewEngineResult result = ViewEngines
.Engines
.FindPartialView(controller.ControllerContext, partialName);
if (result.View == null)
{
throw new InvalidOperationException(
string.Format("The partial view '{0}' could not be found", partialName));
}
var partialPath = ((WebFormView)result.View).ViewPath;
vp.ViewData.Model = model;
Control control = vp.LoadControl(partialPath);
vp.Controls.Add(control);
var sb = new StringBuilder();
using (var sw = new StringWriter(sb))
{
using (var tw = new HtmlTextWriter(sw))
{
vp.RenderControl(tw);
}
}
return sb.ToString();
}
usage within controller:
public string GetLocationHighlites()
{
IBlockData model = WebPagesMapper.GetLocationHighlites();
// **this** being the controoler instance
// LocationPartial.ascx can be defined in shared or in view folder
return RenderPartialToString(**this**,"LocationPartial", model);
}

You can do that out of the box with:
var partial = new HtmlString(Html.Partial("_myPartial", Model).ToString());

public virtual string RenderPartialViewToString(string viewName, object viewmodel)
{
if (string.IsNullOrEmpty(viewName))
{
viewName = this.ControllerContext.RouteData.GetRequiredString("action");
}
ViewData.Model = viewmodel;
using (var sw = new StringWriter())
{
ViewEngineResult viewResult = System.Web.Mvc.ViewEngines.Engines.FindPartialView(this.ControllerContext, viewName);
var viewContext = new ViewContext(this.ControllerContext, viewResult.View, this.ViewData, this.TempData, sw);
viewResult.View.Render(viewContext, sw);
viewResult.ViewEngine.ReleaseView(ControllerContext, viewResult.View);
return sw.GetStringBuilder().ToString();
}
}

Old post, but I think you are making this too difficult.
Why not return PartialView("<my partial>", model); will return the string you desire.
Just make the Get/Post method an IActionResult as JsonResult, PartialResultand other inherit from ActionResult you can send back anything including a JsonResult
[HttpPost]
public IActionResult CheckForContent(string id, string type){
try{
if(type = "something"){
return Json(new {
success = true,
myProp = "this prop"
});
} else {
MyViewModel model = new MyViewModel();
model.Content = _service.GetContent(new GetContentRequest(){ id = id }
return PartialView("<my partial>", model);
} catch(Exception ex){
_logger.LogError(ex.Message);
return Json(new { success = false }
}
}
Ajax:
$.ajax({
url: '/CheckForContent',
type: 'POST',
cache: false,
contentType: false,
processData: false,
success: function (result) {
if(!result && !result.success){
console.log("Failed to load");
} else if(result && result.success){
$('#MyProp').val(result.myProp);
} else {
$('#MyContainer').html(result);
}
},
});

Related

on a button click I want to pass value to Action method of type HttpGet and then display it on cshtml

on a button click I want to pass value to Action method of type HttpGet and then display it on cshtml
Ex:Controller method
[HttpGet]
public ActionResult GetMandateListForCustomer(int userId, int customerId)
{
var receiptBankUserList = new CustomerMandateReport()
{
GetMandateListForCustomer = _mandateManager.GetMandateListForCustomer(userId, customerId)
};
return View(receiptBankUserList);
}
Here is some example how to do this:
Controller:
[HttpGet]
public string GetMandateListForCustomer(int userId, int customerId)
{
var receiptBankUserList = new CustomerMandateReport()
{
GetMandateListForCustomer = _mandateManager.GetMandateListForCustomer(userId, customerId)
};
return RenderRazorViewToString("ViewName", receiptBankUserList);
}
// use this method to change your view into string
public string RenderRazorViewToString(string viewName, object model)
{
ViewData.Model = model;
using (var sw = new StringWriter())
{
var viewResult = ViewEngines.Engines.FindPartialView(ControllerContext, viewName);
var viewContext = new ViewContext(ControllerContext, viewResult.View, ViewData, TempData, sw);
viewResult.View.Render(viewContext, sw);
viewResult.ViewEngine.ReleaseView(ControllerContext, viewResult.View);
return sw.GetStringBuilder().ToString();
}
}
}
Method to render view as string is from here
Javascript
$('#btnAddElement').click(function () {
$.get("UrlToAction?userIDd=123&customerId=321", function (data) {
// replace element with class result with
// our new html which we get from controller
$(".result").html(data);
});
});

how to return model from controller to view using jquery?

Code in javascript
function SubmitQuery(Org_UID) {
var ddlProduct_No = $("#Org_UID");
$.ajax({
type: 'POST',
url: '#Url.Action("ShowNewProfessionalWindow", "UpdateOrganizationUID")',
dataType: "html",
data: {
countryno: $("#Org_UID").val() }
My Controller
[HttpPost]
public ActionResult ShowNewProfessionalWindow(string countryno)
{
UpdateOrganizationUIDViewModel model = new UpdateOrganizationUIDViewModel();
//model.Org_UID = OrgUID;
model.org_name_long = "test";
model.org_name_short = "test";
return RedirectToAction("Index", model);
}
How can I return value of model to view using jquery?
Use JsonResult
Change your code and return JsonResult, So use
return Json(model);
instead of
return RedirectToAction("Index", model);
If you want to return View use
First you can create a method as, which will accept ViewName and Model it will return HTML string
public static string RenderRazorViewToString(ControllerContext controllerContext, string viewName, object model)
{
controllerContext.Controller.ViewData.Model = model;
using (var sw = new StringWriter())
{
var viewResult = ViewEngines.Engines.FindPartialView(controllerContext, viewName);
var viewContext = new ViewContext(controllerContext, viewResult.View,
controllerContext.Controller.ViewData, controllerContext.Controller.TempData, sw);
viewResult.View.Render(viewContext, sw);
viewResult.ViewEngine.ReleaseView(controllerContext, viewResult.View);
return sw.GetStringBuilder().ToString();
}
}
Usage also you have to return JsonResult instead of PartialViewResult
public JsonResult yourpartialviewresult()
{
return Json(new
{
data = RenderRazorViewToString(this.ControllerContext, "partialview", model)
});
}

signalR send partial view result string via hub

I need to be able to return partial view as string in my MVC app via signalR. I'm using hubs.
I'm using the following method to return a partial view string (from here):
public static string RenderPartialView(string controllerName, string partialView, object model)
{
var context = httpContextBase as HttpContextBase;
var routes = new RouteData();
routes.Values.Add("controller", controllerName);
var requestContext = new RequestContext(context, routes);
string requiredString = requestContext.RouteData.GetRequiredString("controller");
var controllerFactory = ControllerBuilder.Current.GetControllerFactory();
var controller = controllerFactory.CreateController(requestContext, requiredString) as ControllerBase;
controller.ControllerContext = new ControllerContext(context, routes, controller);
var ViewData = new ViewDataDictionary();
var TempData = new TempDataDictionary();
ViewData.Model = model;
using (var sw = new StringWriter())
{
var viewResult = ViewEngines.Engines.FindPartialView(controller.ControllerContext, partialView);
var viewContext = new ViewContext(controller.ControllerContext, viewResult.View, ViewData, TempData, sw);
viewResult.View.Render(viewContext, sw);
return sw.GetStringBuilder().ToString();
}
}
In order for this method to work I need HttpContext.Current therefore in my OnConnected (I noticed that this always exists) I set it like so:
public class TaskActionStatus : Hub
{
private static HttpContextBase httpContextBase;
...
public override Task OnConnected()
{
httpContextBase = new HttpContextWrapper(HttpContext.Current) as HttpContextBase;
...
and then I use it in my RenderPartialView method:
var context = httpContextBase as HttpContextBase;
This way I always have access to current HttpContext. However, I noticed that sometimes my static copy of HttpContext is null. Why is that?.
What's the best approach here?
Is there a way to render partial view without HttpContext?
I used fake http context to generate view:
public static string GetRazorViewAsString(object model, string filePath)
{
HttpContext httpContext = MockHelper.FakeHttpContext();
var st = new StringWriter();
var context = new HttpContextWrapper(httpContext);
var routeData = new RouteData();
var controllerContext = new ControllerContext(new RequestContext(context, routeData), new FakeController());
var razor = new RazorView(controllerContext, filePath, null, false, null);
razor.Render(
new ViewContext(controllerContext, razor, new ViewDataDictionary(model), new TempDataDictionary(), st),
st);
return st.ToString();
}
#endregion
}
public class FakeController : Controller
{
}
public class MockHelper
{
#region Public Methods and Operators
public static HttpContext FakeHttpContext()
{
var httpRequest = new HttpRequest(string.Empty, "http://novomatic/", string.Empty);
var stringWriter = new StringWriter();
var httpResponce = new HttpResponse(stringWriter);
var httpContext = new HttpContext(httpRequest, httpResponce);
var sessionContainer = new HttpSessionStateContainer(
"id",
new SessionStateItemCollection(),
new HttpStaticObjectsCollection(),
10,
true,
HttpCookieMode.AutoDetect,
SessionStateMode.InProc,
false);
httpContext.Items["AspSession"] =
typeof(HttpSessionState).GetConstructor(
BindingFlags.NonPublic | BindingFlags.Instance,
null,
CallingConventions.Standard,
new[] { typeof(HttpSessionStateContainer) },
null).Invoke(new object[] { sessionContainer });
return httpContext;
}
#endregion
}

Merge output of partial view

I have partial view which takes customer object and renders html. For customers list, how to merge output of the partial view on server side like similar to having renderpartial with foreach loop in the view.
//how to write action method for below
foreach(var item in customerslist)
{
//get html by calling the parview
outputhtml += //output from new _partialviewCustomer(item);
}
return outputhtml;
You could render a partial to a string using the following extension method:
public static class HtmlExtensions
{
public static string RenderPartialViewToString(this ControllerContext context, string viewName, object model)
{
if (string.IsNullOrEmpty(viewName))
{
viewName = context.RouteData.GetRequiredString("action");
}
context.Controller.ViewData.Model = model;
using (var sw = new StringWriter())
{
var viewResult = ViewEngines.Engines.FindPartialView(context, viewName);
var viewContext = new ViewContext(context, viewResult.View, context.Controller.ViewData, context.Controller.TempData, sw);
viewResult.View.Render(viewContext, sw);
return sw.GetStringBuilder().ToString();
}
}
}
and then:
foreach(var item in customerslist)
{
//get html by calling the parview
outputhtml += ControllerContext.RenderPartialViewToString("~/Views/SomeController/_Customer.cshtml", item)
}
return outputhtml;

Question about ViewEngines.Engines.FindView method and the masterName parameter

This method works great if I pass null to the last param masterName, My views setup in my class derivved from RazorViewEngine work and all is good. Out of curiosity what is the masterName parameter used for? I first thought maybe it was for a layout.cshtml, however; when I pass it a layout it throws an exception.... Any ideas on how this is supposed to be used, what is it looking for?
Custom View Engine (Hardly LOL)
public class CustomRazorViewEngine : RazorViewEngine
{
private readonly string[] NewViewFormats = new[]
{
"~/Views/Messaging/{0}.cshtml"
};
public CustomRazorViewEngine()
{
base.ViewLocationFormats = base.ViewLocationFormats.Union(NewViewFormats).ToArray();
}
}
public string RenderViewToString(string viewName, object model, ControllerContext controllerContext,
string masterName)
{
if (string.IsNullOrEmpty(viewName))
viewName = controllerContext.RouteData.GetRequiredString("action");
controllerContext.Controller.ViewData.Model = model;
using (var stringWriter = new StringWriter())
{
ViewEngineResult viewEngineResult = ViewEngines.Engines.FindView(controllerContext, viewName, masterName);
var viewContext = new ViewContext(controllerContext, viewEngineResult.View,
controllerContext.Controller.ViewData,
controllerContext.Controller.TempData,
stringWriter);
viewEngineResult.View.Render(viewContext, stringWriter);
return stringWriter.GetStringBuilder().ToString();
}
}
So after some more debugging I have found what appears to be the correct answer. First let me state that the masterName parameter is the name of the "Layout" so to say that the view being rendered will use. The catch here is that layout must be able to be located. So instead of the code for the ViewEngine in my original post the following code works as desired.
public string RenderViewToString(string viewName, object model, ControllerContext controllerContext,
string masterName)
{
if (string.IsNullOrEmpty(viewName))
viewName = controllerContext.RouteData.GetRequiredString("action");
controllerContext.Controller.ViewData.Model = model;
using (var stringWriter = new StringWriter())
{
ViewEngineResult viewEngineResult = ViewEngines.Engines.FindView(controllerContext, viewName, masterName);
var viewContext = new ViewContext(controllerContext, viewEngineResult.View,
controllerContext.Controller.ViewData,
controllerContext.Controller.TempData,
stringWriter);
viewEngineResult.View.Render(viewContext, stringWriter);
return stringWriter.GetStringBuilder().ToString();
}
}
public class CustomRazorViewEngine : RazorViewEngine
{
private readonly string[] NewMasterViewFormats = new[]
{
"~/Views/Messaging/Layouts/{0}.cshtml"
};
private readonly string[] NewViewFormats = new[]
{
"~/Views/Messaging/{0}.cshtml"
};
public CustomRazorViewEngine()
{
base.ViewLocationFormats = base.ViewLocationFormats.Union(NewViewFormats).ToArray();
base.MasterLocationFormats = base.MasterLocationFormats.Union(NewMasterViewFormats).ToArray();
}
}
Now when calling
string returnViewToString = _viewUtils.RenderViewToString("RegistrationEmail", new RegistrationEmailModel
{ UserName = userName
},
this.ControllerContext,"_RegistrationEmailLayout");
Everything is happy and my layout for the passed in view, if it exists in the folder gets used. This was the highlight of my day... LOL

Resources