ASP.NET MVC Razor: как визуализировать HTML-код частичного представления Razor внутри действия контроллера

97

Известно, как сгенерировать HTML-код данного частичного представления на движке представления ASP.NET .

Но если эта функция используется в частичном представлении бритвы, она не работает, поскольку в исключении указано, что частичное представление не является производным от «UserControl».

Как исправить рендеринг для поддержки частичного просмотра бритвы?

Мне это нужно, потому что я генерирую электронные письма из этих частичных представлений ...

ОБНОВИТЬ:

Код, который не работает (@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
Можете ли вы показать имеющийся у вас код, который генерирует исключение?
mlibby 03

Ответы:

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

Обновить

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();
    }
}
Джгауффин
источник
Да, именно так вы визуализируете частичное представление внутри представления. Но как сделать это внутри действия контроллера?
Питер Стегнар 03
Отлично, вот оно что! Работает с нотацией Razon и ASP.
Питер Стегнар 03
2
Одно подразделение: как визуализировать представление, которое находится в другой области действия контроллера, а не в текущей? Допустим, он находится в области «EmailController» (папка просмотра электронной почты)?
Питер Стегнар
1
Это было отличное решение. У меня была точная потребность в электронной почте, и я решил использовать ее.
uadrive
2
@AmeyKhadatkar: нет. jquery - это клиентская сторона, представление создается на стороне сервера перед отправкой в ​​браузер.
jgauffin
8

Хотя адекватные ответы уже были даны, я хотел бы предложить менее подробное решение, которое можно использовать без вспомогательных методов, доступных в классе контроллера MVC. Используя стороннюю библиотеку под названием "RazorEngine", вы можете использовать ввод-вывод файла .Net для получения содержимого файла razor и вызова

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

Загрузите стороннюю библиотеку здесь .

Скотт Терри
источник
5

Кроме того, можно использовать RenderView Controller extensionиз здесь ( источник )

и используйте это так:

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

он работает для движков просмотра бритв и веб-форм

Ому
источник
Проверял ссылку. @ChurkNorris является автором ASP.net MVC Awesome , коммерческого продукта версии 2.0 (последняя версия - 12 марта 2012 г.). Версия 1.9 (последний выпуск от 9 июня 2011 г.) по-прежнему имеет открытый исходный код, но, вероятно, больше не будет разрабатываться. Есть ли там вилки 1.9?
Joel Purra
@Omu: RenderView недействителен. См msdn.microsoft.com/en-us/library/...
ROLAND
@Roland, это настраиваемое расширение контроллера
Ому
1

Я видел, что кому-то было интересно, как это сделать для другого контроллера.

В моем случае у меня были все мои шаблоны электронной почты в папке Views / Email, но вы могли изменить это, чтобы передать контроллер, с которым у вас связаны представления.

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

По сути, при этом нужно взять контроллер, например AccountController, и изменить его так, чтобы он воспринимался как EmailController, чтобы код смотрел в Views/Emailпапку. Это необходимо сделать, потому что FindViewметод не принимает в качестве параметра прямой путь, ему нужен файл ControllerContext.

После завершения рендеринга строки он возвращает AccountController в исходное состояние, которое будет использоваться объектом Response.

Человек-маффин
источник
1

отличный код; Небольшая подсказка: если иногда приходится пропускать больше данных, а не только модель просмотра ..

 if (model is ViewDataDictionary)
 {
     controller.ViewData = model as ViewDataDictionary;
 } else {
     controller.ViewData.Model = model;
 }
Дэвид Рив
источник
2
Вы не завершили свой ответ
poohdedoo
0

Заимствование ответа @jgauffin как расширения 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);
    }
}

Использование в режиме бритвы:

Html.RenderPartialViewToString(ViewContext, ViewData, TempData, "Search", Model)
Ansielf
источник
1
Не могли бы вы объяснить разницу с использованием @ Html.Partial (строка partialViewName, объектная модель, ViewDataDictionary viewData)? Каковы преимущества, поскольку для этого требуется HtmlHelper?
bkqc