web-dev-qa-db-fra.com

ASP.NET MVC Razor: comment restituer le code HTML d'une vue partielle de rasoir dans l'action du contrôleur

Comment générer le code HTML d’une vue partielle donnée sur le moteur de vue ASP.NET est connue .

Mais si cette fonctionnalité est utilisée sur une vue partielle du rasoir, elle ne fonctionne pas. Une exception dit que la vue partielle ne dérive pas de "UserControl".

Comment réparer le rendu pour supporter la vue partielle du rasoir?

J'ai besoin de ça parce que je génère des emails depuis cette vue partielle ...

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();
    }
96
Peter Stegnar
@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();
    }
}
154
jgauffin

Bien que des réponses adéquates aient déjà été données, je voudrais proposer une solution moins détaillée, 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 rasoir et appeler

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

Récupérez la bibliothèque tierce ici .

8
Scott Terry

Vous pouvez également utiliser le RenderView Controller extension de ici ( source )

et l'utiliser comme ça:

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

cela fonctionne pour razor et web-forms viewengines

5
Omu

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

Dans mon cas, tous mes modèles de courrier électronique se trouvaient dans le dossier Vues/Courrier électronique, mais vous pouvez le modifier pour transmettre le contrôleur auquel vous avez associé des vues.

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

Cela consiste essentiellement à prendre un contrôleur, tel que AccountController, et à le modifier pour qu'il soit considéré comme un EmailController afin que le code apparaisse dans le dossier Views/Email. Cela est nécessaire car la méthode FindView ne prend pas un chemin directement vers le haut en tant que paramètre, elle veut une ControllerContext

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

1
The Muffin Man

excellent code; petit indice: si vous devez parfois ignorer plus de données et pas seulement le modèle de vue. 

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

Emprunter @jgauffin répond comme une 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)
0
jmb-mage