ASP.NET MVC Razor: Como renderizar o HTML de uma visualização parcial do Razor dentro da ação do controlador

97

É conhecido como gerar um HTML de uma determinada exibição parcial no mecanismo de exibição ASP.NET .

Mas se essa funcionalidade for usada na visualização parcial do razor, ela não funcionará, pois a exceção diz que a visualização parcial não deriva de "UserControl".

Como consertar a renderização para oferecer suporte à visão parcial de navalha?

Eu preciso disso porque eu gero e-mails a partir dessas visualizações parciais ...

ATUALIZAR:

Código que falha (@mcl):

public string RenderPartialToString(string controlName, object viewData)
    {
        ViewPage viewPage = new ViewPage() { ViewContext = new ViewContext() };
        viewPage.Url = this.GetUrlHelper();

        string fullControlName = "~/Views/Email/" + controlName + ".ascx";

        viewPage.ViewData = new ViewDataDictionary(viewData);
        viewPage.Controls.Add(viewPage.LoadControl(fullControlName));

        StringBuilder sb = new StringBuilder();
        using (StringWriter sw = new StringWriter(sb))
        {
            using (HtmlTextWriter tw = new HtmlTextWriter(sw))
            {
                viewPage.RenderControl(tw);
            }
        }
        return sb.ToString();
    }
Peter Stegnar
fonte
1
Você pode mostrar o código que você tem até agora que gera a exceção?
mlibby

Respostas:

154
@Html.Partial("nameOfPartial", Model)

Atualizar

protected string RenderPartialViewToString(string viewName, object model)
{
    if (string.IsNullOrEmpty(viewName))
        viewName = ControllerContext.RouteData.GetRequiredString("action");

    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();
    }
}
jgauffin
fonte
Sim, é assim que você renderiza uma vista parcial dentro de uma vista. Mas como renderizá-lo dentro de uma ação do controlador?
Peter Stegnar
Ótimo, é isso agora! Trabalha com Razon e notação ASP.
Peter Stegnar
2
Uma pergunta: Como renderizar a visualização que está em outro escopo do Controlador que o atual? Vamos dizer que está no escopo "EmailController" (pasta de visualização de email)?
Peter Stegnar
1
Esta foi uma ótima solução. Eu tinha exatamente a necessidade de e-mail e optei por usar isso.
uadrive
2
@AmeyKhadatkar: não. jquery está do lado do cliente, a visualização é gerada no lado do servidor antes de ser enviada para o navegador.
jgauffin de
8

Embora respostas adequadas já tenham sido dadas, eu gostaria de propor uma solução menos prolixo, que pode ser usada sem os métodos auxiliares disponíveis em uma classe de controlador MVC. Usando uma biblioteca de terceiros chamada "RazorEngine", você pode usar o arquivo .Net IO para obter o conteúdo do arquivo do razor e chamar

string html = Razor.Parse(razorViewContentString, modelObject);

Obtenha a biblioteca de terceiros aqui .

Scott Terry
fonte
5

Você também pode usar a RenderView Controller extensionpartir daqui ( fonte )

e use-o assim:

public ActionResult Do() {
var html = this.RenderView("index", theModel);
...
}

funciona para mecanismos de visualização de navalha e formulários da web

Omu
fonte
Verifiquei o link. @ChurkNorris é o autor de ASP.net MVC Awesome , que é um produto comercial da versão 2.0 (atualmente o último lançamento em 12 de março de 2012). A versão 1.9 (última versão em 9 de junho de 2011) ainda é um código aberto, mas provavelmente não será mais desenvolvida. Algum garfo de 1.9 lá fora?
Joel Purra
@Omu: RenderView está vazio. Consulte msdn.microsoft.com/en-us/library/…
roland
@Roland, esta é uma extensão do controlador personalizado
Omu
1

Eu vi que alguém estava se perguntando como fazer isso para outro controlador.

No meu caso, eu tinha todos os meus modelos de email na pasta Views / Email, mas você poderia modificar isso para passar o controlador ao qual você tem views associadas.

public static string RenderViewToString(Controller controller, string viewName, object model)
    {
        var oldController = controller.RouteData.Values["controller"].ToString();

        if (controller.GetType() != typeof(EmailController))
            controller.RouteData.Values["controller"] = "Email";

        var oldModel = controller.ViewData.Model;
        controller.ViewData.Model = model;
        try
        {
            using (var sw = new StringWriter())
            {
                var viewResult = ViewEngines.Engines.FindView(controller.ControllerContext, viewName,
                                                                           null);

                var viewContext = new ViewContext(controller.ControllerContext, viewResult.View, controller.ViewData, controller.TempData, sw);
                viewResult.View.Render(viewContext, sw);

                //Cleanup
                controller.ViewData.Model = oldModel;
                controller.RouteData.Values["controller"] = oldController;

                return sw.GetStringBuilder().ToString();
            }
        }
        catch (Exception ex)
        {
            Elmah.ErrorSignal.FromCurrentContext().Raise(ex);

            throw ex;
        }
    }

Essencialmente, o que isso faz é pegar um controlador, como AccountController, e modificá-lo para pensar que é um EmailController para que o código apareça na Views/Emailpasta. É necessário fazer isso porque o FindViewmétodo não pega um caminho direto como parâmetro, ele quer um ControllerContext.

Depois de renderizar a string, ele retorna o AccountController ao seu estado inicial para ser usado pelo objeto Response.

O Homem Muffin
fonte
1

ótimo código; dica: se às vezes você tem que ignorar mais dados e não apenas o modelo de visualização ..

 if (model is ViewDataDictionary)
 {
     controller.ViewData = model as ViewDataDictionary;
 } else {
     controller.ViewData.Model = model;
 }
David Riewe
fonte
2
Você não completou sua resposta
poohdedoo
0

Emprestando a resposta @jgauffin como uma extensão HtmlHelper:

public static class HtmlHelperExtensions
{
    public static MvcHtmlString RenderPartialViewToString(
        this HtmlHelper html, 
        ControllerContext controllerContext, 
        ViewDataDictionary viewData,
        TempDataDictionary tempData,
        string viewName, 
        object model)
    {
        viewData.Model = model;
        string result = String.Empty;

        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);

            result = sw.GetStringBuilder().ToString();
        }

        return MvcHtmlString.Create(result);
    }
}

Uso em uma visualização de navalha:

Html.RenderPartialViewToString(ViewContext, ViewData, TempData, "Search", Model)
abbaf33f
fonte
1
Você poderia explicar a diferença com o uso de @ Html.Partial (string partialViewName, modelo de objeto, ViewDataDictionary viewData)? Quais são os benefícios, uma vez que requer HtmlHelper?
bkqc de