ASP.NET MVC Razor: jak renderować kod HTML widoku Razor częściowego wewnątrz akcji kontrolera

97

Znany jest sposób generowania kodu HTML danego widoku częściowego w aparacie widoku ASP.NET .

Ale jeśli ta funkcja jest używana w widoku częściowym maszynki do golenia, nie działa, ponieważ wyjątek mówi, że widok częściowy nie pochodzi od „UserControl”.

Jak naprawić renderowanie, aby obsługiwać częściowy widok maszynki do golenia?

Potrzebuję tego, ponieważ generuję e-maile z tych częściowych widoków ...

AKTUALIZACJA:

Kod, który się nie udaje (@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
źródło
1
Czy możesz pokazać kod, który masz do tej pory, który generuje wyjątek?
mlibby

Odpowiedzi:

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

Aktualizacja

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
źródło
Tak, w ten sposób renderujesz częściowy widok wewnątrz widoku. Ale jak wyrenderować go wewnątrz akcji kontrolera?
Peter Stegnar
Świetnie, to jest to teraz! Działa z notacją Razon i ASP.
Peter Stegnar
2
Jedno pytanie dodatkowe: Jak wyrenderować widok znajdujący się w innym zakresie kontrolera niż bieżący? Powiedzmy, że znajduje się w zakresie „EmailController” (folder widoku poczty e-mail)?
Peter Stegnar
1
To było świetne rozwiązanie. Miałem taką potrzebę w przypadku poczty e-mail i zdecydowałem się z niej skorzystać.
uadrive,
2
@AmeyKhadatkar: nie. jquery jest po stronie klienta, widok jest generowany po stronie serwera przed wysłaniem do przeglądarki.
jgauffin
8

Chociaż udzielono już odpowiednich odpowiedzi, chciałbym zaproponować mniej rozwlekłe rozwiązanie, którego można używać bez metod pomocniczych dostępnych w klasie kontrolera MVC. Korzystając z biblioteki innej firmy o nazwie „RazorEngine”, możesz użyć IO pliku .Net, aby pobrać zawartość pliku Razor i wywołać

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

Pobierz bibliotekę innej firmy tutaj .

Scott Terry
źródło
5

Można również użyć RenderView Controller extensionz tutaj ( źródła )

i użyj go w ten sposób:

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

działa dla maszynek do golenia i przeglądarek formularzy internetowych

Omu
źródło
Sprawdziłem link. @ChurkNorris jest autorem ASP.net MVC Awesome , który jest komercyjnym produktem od wersji 2.0 (obecnie ostatnie wydanie 12 marca 2012). Wersja 1.9 (ostatnie wydanie 9 czerwca 2011) jest nadal open source, ale prawdopodobnie nie będzie już rozwijana. Jakieś widelce 1,9?
Joel Purra,
@Omu: RenderView jest nieważne. Zobacz msdn.microsoft.com/en-us/library/…
roland
@ Roland to jest niestandardowe rozszerzenie kontrolera
Omu
1

Widziałem, że ktoś się zastanawiał, jak to zrobić dla innego kontrolera.

W moim przypadku wszystkie moje szablony wiadomości e-mail miałem w folderze Widoki / E-mail, ale możesz to zmodyfikować, aby przekazać kontroler, z którym masz skojarzone widoki.

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

Zasadniczo polega to na pobraniu kontrolera, takiego jak AccountController, i zmodyfikowaniu go tak, aby uważał, że jest to EmailController, aby kod wyglądał w Views/Emailfolderze. Jest to konieczne, ponieważ FindViewmetoda nie przyjmuje prostej ścieżki w górę jako parametru, a chce ControllerContext.

Po zakończeniu renderowania ciągu zwraca AccountController z powrotem do stanu początkowego, który ma być używany przez obiekt Response.

The Muffin Man
źródło
1

świetny kod; mała wskazówka: jeśli czasami musisz ominąć więcej danych, a nie tylko model widoku ..

 if (model is ViewDataDictionary)
 {
     controller.ViewData = model as ViewDataDictionary;
 } else {
     controller.ViewData.Model = model;
 }
David Riewe
źródło
2
Nie uzupełniłeś odpowiedzi
poohdedoo
0

Wypożyczenie odpowiedzi @jgauffin jako rozszerzenie 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);
    }
}

Użycie w widoku brzytwy:

Html.RenderPartialViewToString(ViewContext, ViewData, TempData, "Search", Model)
ansielf
źródło
1
Czy możesz wyjaśnić różnicę w używaniu @ Html.Partial (ciąg częściowyViewName, model obiektu, ViewDataDictionary viewData)? Jakie są korzyści, ponieważ wymaga HtmlHelper?
bkqc