ASP.NET MVC Razor: come eseguire il rendering dell'HTML di una visualizzazione parziale Razor all'interno dell'azione del controller


97

È noto come generare un HTML di una data vista parziale sul motore di visualizzazione ASP.NET .

Ma se questa funzionalità viene utilizzata su una vista parziale razor non funziona, poiché l'eccezione dice che la vista parziale non deriva da "UserControl".

Come correggere il rendering per supportare la visualizzazione parziale del rasoio?

Ne ho bisogno perché generi email da queste visualizzazioni parziali ...

AGGIORNARE:

Codice che fallisce (@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();
    }

1
Puoi mostrare il codice che hai finora che genera l'eccezione?
mlibby

Risposte:


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

Aggiornare

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

Sì, è così che si esegue il rendering di una vista parziale all'interno di una vista. Ma come renderlo all'interno di un'azione del controller?
Peter Stegnar

Fantastico, ora è così! Funziona con Razon e notazione ASP.
Peter Stegnar

2
Una domanda secondaria: come eseguire il rendering della vista che si trova in un altro ambito controller rispetto a quello corrente? Diciamo che è in ambito "EmailController" (cartella di visualizzazione Email)?
Peter Stegnar

1
Questa è stata un'ottima soluzione. Avevo l'esatto bisogno con la posta elettronica e ho deciso di usarlo.
uadrive

2
@AmeyKhadatkar: no. jquery è lato client, la vista viene generata sul lato server prima di essere inviata al browser.
jgauffin

8

Sebbene siano già state fornite risposte adeguate, vorrei proporre una soluzione meno prolissa, che può essere utilizzata senza i metodi di supporto disponibili in una classe controller MVC. Usando una libreria di terze parti chiamata "RazorEngine" puoi usare .Net file IO per ottenere il contenuto del file razor e chiamare

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

Ottieni la libreria di terze parti qui .


5

Puoi anche usare RenderView Controller extensionda qui ( fonte )

e usalo in questo modo:

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

funziona per motori di visualizzazione razor e web-form


Ho controllato il collegamento. @ChurkNorris è l'autore di ASP.net MVC Awesome , che è un prodotto commerciale dalla versione 2.0 (attualmente l'ultima versione del 12 marzo 2012). La versione 1.9 (ultima release il 9 giugno 2011) è ancora open source, ma probabilmente non sarà più sviluppata. Ci sono fork di 1.9 là fuori?
Joel Purra,

@Omu: RenderView è nullo. Vedi msdn.microsoft.com/en-us/library/…
roland

@Roland questa è un'estensione del controller personalizzato
Omu

1

Ho visto che qualcuno si chiedeva come farlo per un altro controller.

Nel mio caso avevo tutti i miei modelli di posta elettronica nella cartella Visualizzazioni / Email, ma potresti modificarlo per passare al controller a cui sono associate le viste.

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

In sostanza, ciò che fa è prendere un controller, come AccountController e modificarlo per pensare che sia un EmailController in modo che il codice guardi nella Views/Emailcartella. È necessario farlo perché il FindViewmetodo non prende un percorso diretto come parametro, ma vuole un file ControllerContext.

Una volta eseguito il rendering della stringa, restituisce AccountController al suo stato iniziale per essere utilizzato dall'oggetto Response.


1

ottimo codice; piccolo accenno: se a volte devi bypassare più dati e non solo il viewmodel ..

 if (model is ViewDataDictionary)
 {
     controller.ViewData = model as ViewDataDictionary;
 } else {
     controller.ViewData.Model = model;
 }

2
Non hai completato la tua risposta
poohdedoo

0

Prendendo in prestito la risposta @jgauffin come estensione 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);
    }
}

Utilizzo in una vista rasoio:

Html.RenderPartialViewToString(ViewContext, ViewData, TempData, "Search", Model)

1
Potresti spiegare la differenza con l'utilizzo di @ Html.Partial (string partialViewName, object model, ViewDataDictionary viewData)? Quali sono i vantaggi poiché richiede HtmlHelper?
bkqc
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.