ASP.NET MVC Razor: So rendern Sie den HTML-Code einer Razor Partial View innerhalb der Controller-Aktion

97

Es ist bekannt, wie ein HTML-Code einer bestimmten Teilansicht in der ASP.NET-Ansichts-Engine generiert wird .

Wenn diese Funktion jedoch in der Rasiermesser-Teilansicht verwendet wird, funktioniert sie nicht, da die Ausnahme besagt, dass die Teilansicht nicht von "UserControl" abgeleitet ist.

Wie kann ich das Rendering korrigieren, um die Rasiermesser-Teilansicht zu unterstützen?

Ich brauche das, weil ich E-Mails aus diesen Teilansichten generiere ...

AKTUALISIEREN:

Code, der fehlschlägt (@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
quelle
1
Können Sie den Code anzeigen, den Sie bisher haben und der die Ausnahme generiert?
mlibby

Antworten:

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

Aktualisieren

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
quelle
Ja, so rendern Sie eine Teilansicht innerhalb einer Ansicht. Aber wie kann man es in einer Controller-Aktion rendern?
Peter Stegnar
Großartig, das ist es jetzt! Funktioniert mit Razon- und ASP-Notation.
Peter Stegnar
2
Eine Unterfrage: Wie wird die Ansicht gerendert, die sich in einem anderen Controller-Bereich befindet als die aktuelle? Nehmen wir an, es befindet sich im Bereich "EmailController" (E-Mail-Ansichtsordner).
Peter Stegnar
1
Dies war eine großartige Lösung. Ich hatte genau das Bedürfnis mit E-Mail und entschied mich dafür.
Uadrive
2
@AmeyKhadatkar: nein. jquery ist clientseitig, die Ansicht wird serverseitig generiert, bevor sie an den Browser gesendet wird.
Jgauffin
8

Obwohl bereits angemessene Antworten gegeben wurden, möchte ich eine weniger ausführliche Lösung vorschlagen, die ohne die in einer MVC-Controller-Klasse verfügbaren Hilfsmethoden verwendet werden kann. Mit einer Drittanbieter-Bibliothek namens "RazorEngine" können Sie die .NET-Datei IO verwenden, um den Inhalt der Rasiererdatei abzurufen und aufzurufen

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

Holen Sie sich das Dritte Bibliothek hier .

Scott Terry
quelle
5

Sie können auch die RenderView Controller extensionvon hier ( Quelle) verwenden ) verwenden

und benutze es so:

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

es funktioniert für Rasierer und Webformulare viewengines

Omu
quelle
Überprüfte den Link. @ChurkNorris ist der Autor von ASP.net MVC Awesome , einem kommerziellen Produkt aus Version 2.0 (derzeit neueste Version 12. März 2012). Version 1.9 (neueste Version 9. Juni 2011) ist noch Open Source, wird aber wahrscheinlich nicht mehr entwickelt. Gibt es Gabeln mit 1,9?
Joel Purra
@ Omu: RenderView ist ungültig. Siehe msdn.microsoft.com/en-us/library/...
roland
@ Roland Dies ist eine benutzerdefinierte Controller-Erweiterung
Omu
1

Ich sah, dass sich jemand fragte, wie man das für einen anderen Controller macht.

In meinem Fall hatte ich alle meine E-Mail-Vorlagen im Ordner "Ansichten / E-Mail", aber Sie können dies so ändern, dass es an den Controller übergeben wird, für den Sie Ansichten zugeordnet haben.

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

Im Wesentlichen wird dazu ein Controller wie AccountController verwendet, um ihn als E-Mail-Controller zu betrachten, sodass der Code im Views/EmailOrdner angezeigt wird . Dies ist erforderlich, da die FindViewMethode keinen geraden Pfad als Parameter verwendet, sondern einen ControllerContext.

Nach dem Rendern der Zeichenfolge wird der AccountController wieder in den Ausgangszustand versetzt, der vom Response-Objekt verwendet werden soll.

Der Muffin-Mann
quelle
1

großartiger Code; kleiner tipp: wenn man manchmal mehr daten umgehen muss und nicht nur das viewmodel ..

 if (model is ViewDataDictionary)
 {
     controller.ViewData = model as ViewDataDictionary;
 } else {
     controller.ViewData.Model = model;
 }
David Riewe
quelle
2
Sie haben Ihre Antwort nicht abgeschlossen
poohdedoo
0

@ Jgauffin-Antwort als HtmlHelper-Erweiterung ausleihen:

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

Verwendung in einer Rasiermesseransicht:

Html.RenderPartialViewToString(ViewContext, ViewData, TempData, "Search", Model)
abbaf33f
quelle
1
Können Sie den Unterschied bei der Verwendung von @ Html.Partial (Zeichenfolge PartialViewName, Objektmodell, ViewDataDictionary viewData) erklären? Was sind die Vorteile, da HtmlHelper erforderlich ist?
bkqc