ASP.NET MVC Razor: Comment rendre le HTML d'une vue partielle de rasoir à l'intérieur de l'action du contrôleur

97

Comment générer un HTML d'une vue partielle donnée sur le moteur de vue ASP.NET est connu .

Mais si cette fonctionnalité est utilisée sur une vue partielle de rasoir, elle ne fonctionne pas, car l'exception indique que la vue partielle ne dérive pas de "UserControl".

Comment réparer le rendu pour prendre en charge la vue partielle du rasoir?

J'en ai besoin car je génère des e-mails à partir de ces vues partielles ...

METTRE À JOUR:

Code qui échoue (@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
la source
1
Pouvez-vous montrer le code que vous avez jusqu'à présent qui génère l'exception?
mlibby

Réponses:

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

Mettre à jour

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
la source
Oui, c'est ainsi que vous effectuez le rendu d'une vue partielle dans une vue. Mais comment le rendre dans une action de contrôleur?
Peter Stegnar
Super c'est ça maintenant! Fonctionne avec la notation Razon et ASP.
Peter Stegnar
2
Une sous-question: comment rendre la vue qui est dans une autre portée de contrôleur que l'actuelle? Disons qu'il est dans la portée "EmailController" (dossier de vue Email)?
Peter Stegnar
1
C'était une excellente solution. J'avais le besoin exact de courrier électronique et j'ai choisi de l'utiliser.
uadrive
2
@AmeyKhadatkar: non. jquery est côté client, la vue est générée côté serveur avant d'être envoyée au navigateur.
jgauffin
8

Bien que des réponses adéquates aient déjà été données, j'aimerais proposer une solution moins verbeuse, qui peut être utilisée sans les méthodes d'assistance disponibles dans une classe de contrôleur MVC. En utilisant une bibliothèque tierce appelée "RazorEngine", vous pouvez utiliser le fichier .Net IO pour obtenir le contenu du fichier de rasoir et appeler

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

Obtenez la bibliothèque tierce ici .

Scott Terry
la source
5

Vous pouvez également utiliser le à RenderView Controller extensionpartir d' ici ( source )

et utilisez-le comme ceci:

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

cela fonctionne pour les moteurs d'affichage de rasoir et de formulaires Web

Omu
la source
Vérifié le lien. @ChurkNorris est l'auteur de ASP.net MVC Awesome , qui est un produit commercial de la version 2.0 (actuellement dernière version le 12 mars 2012). La version 1.9 (dernière version le 9 juin 2011) est toujours open source, mais ne sera probablement plus développée. Des fourchettes de 1,9 là-bas?
Joel Purra
@Omu: RenderView est nul. Voir msdn.microsoft.com/en-us/library/…
roland
@Roland ceci est une extension de contrôleur personnalisé
Omu
1

J'ai vu que quelqu'un se demandait comment le faire pour un autre contrôleur.

Dans mon cas, j'avais tous mes modèles de courrier électronique dans le dossier Views / Email, mais vous pouvez le modifier pour passer au contrôleur auquel vous avez des vues associées.

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

Ce que cela fait essentiellement, c'est de prendre un contrôleur, tel que AccountController, et de le modifier pour qu'il pense que c'est un EmailController afin que le code regarde dans le Views/Emaildossier. Il est nécessaire de le faire car la FindViewméthode ne prend pas un chemin direct en tant que paramètre, elle veut un ControllerContext.

Une fois le rendu de la chaîne terminé, il renvoie le AccountController à son état initial pour être utilisé par l'objet Response.

L'homme aux muffins
la source
1

bon code; petit conseil: si vous devez parfois contourner plus de données et pas seulement le viewmodel ..

 if (model is ViewDataDictionary)
 {
     controller.ViewData = model as ViewDataDictionary;
 } else {
     controller.ViewData.Model = model;
 }
David Riewe
la source
2
Vous n'avez pas terminé votre réponse
poohdedoo
0

Emprunter la réponse @jgauffin en tant qu'extension 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);
    }
}

Utilisation dans une vue rasoir:

Html.RenderPartialViewToString(ViewContext, ViewData, TempData, "Search", Model)
Ansielf
la source
1
Pourriez-vous expliquer la différence avec l'utilisation de @ Html.Partial (chaîne partialViewName, modèle d'objet, ViewDataDictionary viewData)? Quels sont les avantages car il nécessite HtmlHelper?
bkqc