82 votes

Comment inclure une vue partielle dans un formulaire Web ?

Un site que je programme utilise à la fois ASP.NET MVC et WebForms.

J'ai une vue partielle et je veux l'inclure dans un formulaire web. La vue partielle contient du code qui doit être traité par le serveur, de sorte que l'utilisation de Response.WriteFile ne fonctionne pas. Cela devrait fonctionner avec javascript désactivé.

Comment puis-je le faire ?

0 votes

J'ai le même problème - Html.RenderPartial ne peut pas fonctionner sur les WebForms, mais il devrait quand même y avoir un moyen de le faire.

101voto

Keith Points 46288

J'ai jeté un coup d'œil à la source MVC pour voir si je pouvais trouver comment faire cela. Il semble y avoir un couplage très étroit entre le contexte du contrôleur, les vues, les données de vue, les données de routage et les méthodes de rendu html.

Pour que cela se produise, il faut créer tous ces éléments supplémentaires. Certains d'entre eux sont relativement simples (comme les données de vue) mais certains sont un peu plus complexes - par exemple, les données de routage considèrent que la page WebForms actuelle est ignorée.

Le gros problème semble être le HttpContext - les pages MVC s'appuient sur un HttpContextBase (plutôt que sur un HttpContext comme le font les WebForms) et bien que les deux implémentent IServiceProvider, ils ne sont pas liés. Les concepteurs de MVC ont délibérément décidé de ne pas modifier les WebForms existants pour utiliser la nouvelle base de contexte, mais ils ont fourni un wrapper.

Cela fonctionne et vous permet d'ajouter une vue partielle à un WebForm :

public class WebFormController : Controller { }

public static class WebFormMVCUtil
{

    public static void RenderPartial( string partialName, object model )
    {
        //get a wrapper for the legacy WebForm context
        var httpCtx = new HttpContextWrapper( System.Web.HttpContext.Current );

        //create a mock route that points to the empty controller
        var rt = new RouteData();
        rt.Values.Add( "controller", "WebFormController" );

        //create a controller context for the route and http context
        var ctx = new ControllerContext( 
            new RequestContext( httpCtx, rt ), new WebFormController() );

        //find the partial view using the viewengine
        var view = ViewEngines.Engines.FindPartialView( ctx, partialName ).View;

        //create a view context and assign the model
        var vctx = new ViewContext( ctx, view, 
            new ViewDataDictionary { Model = model }, 
            new TempDataDictionary() );

        //render the partial view
        view.Render( vctx, System.Web.HttpContext.Current.Response.Output );
    }

}

Ensuite, dans votre WebForm, vous pouvez faire ceci :

<% WebFormMVCUtil.RenderPartial( "ViewName", this.GetModel() ); %>

1 votes

Cela fonctionne pour une demande de page de base, mais view.Render() explose avec l'exception "Validation of viewstate MAC failed..." si vous faites des post backs sur la page du conteneur. Pouvez-vous confirmer la même chose, Keith ?

0 votes

Je n'obtiens pas cette erreur d'état de vue - cependant, je pense qu'elle se produirait si la vue partielle que vous rendez comprenait des contrôles WebForm. Cette méthode RenderPartial se déclenche au moment du rendu - après tout état de vue. Les contrôles WebForm à l'intérieur de la vue partielle vont être cassés et en dehors du cycle de vie normal de la page.

0 votes

En fait, je l'ai maintenant - il semble se produire pour certaines hiérarchies de contrôle WebForms et pas pour d'autres. Bizarrement, l'erreur est déclenchée à l'intérieur des méthodes de rendu MVC, comme si l'appel sous-jacent à Page. Render s'attend à faire la validation de la page et de l'événement MAC, ce qui serait toujours complètement faux en MVC.

41voto

Daniel Points 93

Cela a pris un certain temps, mais j'ai trouvé une excellente solution. La solution de Keith fonctionne pour beaucoup de gens, mais dans certaines situations, ce n'est pas la meilleure, parce que parfois vous voulez que votre application passer par le processus du contrôleur pour le rendu de la vue, et La solution de Keith rend simplement la vue avec un modèle donné. Je présente ici une nouvelle solution qui exécutera le processus normal.

Étapes générales :

  1. Créer une classe Utilitaire
  2. Créer un contrôleur fictif avec une vue fictive.
  3. Dans votre aspx o master page Dans le cas de la méthode de rendu partiel, appelez la méthode utilitaire en passant le contrôleur, la vue et, si nécessaire, le modèle à rendre (sous forme d'objet),

Vérifions-le de près dans cet exemple

1) Créez une classe appelée MVCUtility et créez les méthodes suivantes :

    //Render a partial view, like Keith's solution
    private static void RenderPartial(string partialViewName, object model)
    {
        HttpContextBase httpContextBase = new HttpContextWrapper(HttpContext.Current);
        RouteData routeData = new RouteData();
        routeData.Values.Add("controller", "Dummy");
        ControllerContext controllerContext = new ControllerContext(new RequestContext(httpContextBase, routeData), new DummyController());
        IView view = FindPartialView(controllerContext, partialViewName);
        ViewContext viewContext = new ViewContext(controllerContext, view, new ViewDataDictionary { Model = model }, new TempDataDictionary(), httpContextBase.Response.Output);
        view.Render(viewContext, httpContextBase.Response.Output);
    }

    //Find the view, if not throw an exception
    private static IView FindPartialView(ControllerContext controllerContext, string partialViewName)
    {
        ViewEngineResult result = ViewEngines.Engines.FindPartialView(controllerContext, partialViewName);
        if (result.View != null)
        {
            return result.View;
        }
        StringBuilder locationsText = new StringBuilder();
        foreach (string location in result.SearchedLocations)
        {
            locationsText.AppendLine();
            locationsText.Append(location);
        }
        throw new InvalidOperationException(String.Format("Partial view {0} not found. Locations Searched: {1}", partialViewName, locationsText));
    }       

    //Here the method that will be called from MasterPage or Aspx
    public static void RenderAction(string controllerName, string actionName, object routeValues)
    {
        RenderPartial("PartialRender", new RenderActionViewModel() { ControllerName = controllerName, ActionName = actionName, RouteValues = routeValues });
    }

Créer une classe pour passer les paramètres, je vais appeler ici RendeActionViewModel (vous pouvez créer dans le même fichier de la classe MvcUtility)

    public class RenderActionViewModel
    {
        public string ControllerName { get; set; }
        public string ActionName { get; set; }
        public object RouteValues { get; set; }
    }

2) Maintenant, créez un contrôleur nommé DummyController

    //Here the Dummy controller with Dummy view
    public class DummyController : Controller
    {
      public ActionResult PartialRender()
      {
          return PartialView();
      }
    }

Créez une vue factice appelée PartialRender.cshtml (razor view) pour le DummyController avec le contenu suivant, notez qu'il effectuera une autre action de rendu en utilisant l'aide Html.

@model Portal.MVC.MvcUtility.RenderActionViewModel
@{Html.RenderAction(Model.ActionName, Model.ControllerName, Model.RouteValues);}

3) Maintenant, mettez ceci dans votre MasterPage o aspx pour rendre partiellement la vue que vous souhaitez. Notez que c'est une excellente réponse lorsque vous avez plusieurs vues de rasoir que vous voulez mélanger avec votre MasterPage o aspx pages. (Supposons que nous ayons une PartialView appelée Login pour le contrôleur Home).

    <% MyApplication.MvcUtility.RenderAction("Home", "Login", new { }); %>

ou si vous avez un modèle à passer dans l'action

    <% MyApplication.MvcUtility.RenderAction("Home", "Login", new { Name="Daniel", Age = 30 }); %>

Cette solution est géniale, n'utilise pas d'appel ajax qui ne provoquera pas de rendu tardif pour les vues imbriquées, il ne fait pas de nouvelle WebRequest donc il ne vous apportera pas une nouvelle session et il traitera la méthode pour récupérer l'ActionResult pour la vue que vous voulez, il fonctionne sans passer de modèle

Merci à Utilisation de MVC RenderAction dans un formulaire Web

1 votes

J'ai essayé toutes les autres solutions proposées dans cet article et cette réponse est de loin la meilleure. Je recommande à toute autre personne d'essayer d'abord cette solution.

0 votes

Salut daniel. Pouvez-vous m'aider ? J'ai suivi votre solution mais j'ai frappé à un endroit. Je l'ai soulevé sous stackoverflow.com/questions/38241661/

0 votes

C'est certainement l'une des meilleures réponses que j'ai vues sur SO. Grand merci.

20voto

Alexander Taran Points 3907

La manière la plus évidente serait via AJAX

quelque chose comme ceci (en utilisant jQuery)

<div id="mvcpartial"></div>

<script type="text/javascript">
$(document).load(function () {
    $.ajax(
    {    
        type: "GET",
        url : "urltoyourmvcaction",
        success : function (msg) { $("#mvcpartial").html(msg); }
    });
});
</script>

9 votes

A été ajouté après ma réponse )- :

11voto

Dr. C. Hilarius Points 493

C'est génial, merci !

J'utilise MVC 2 sur .NET 4, qui exige qu'un TextWriter soit passé dans le ViewContext, donc vous devez passer dans httpContextWrapper.Response.Output comme indiqué ci-dessous.

    public static void RenderPartial(String partialName, Object model)
    {
        // get a wrapper for the legacy WebForm context
        var httpContextWrapper = new HttpContextWrapper(HttpContext.Current);

        // create a mock route that points to the empty controller
        var routeData = new RouteData();
        routeData.Values.Add(_controller, _webFormController);

        // create a controller context for the route and http context
        var controllerContext = new ControllerContext(new RequestContext(httpContextWrapper, routeData), new WebFormController());

        // find the partial view using the viewengine
        var view = ViewEngines.Engines.FindPartialView(controllerContext, partialName).View as WebFormView;

        // create a view context and assign the model
        var viewContext = new ViewContext(controllerContext, view, new ViewDataDictionary { Model = model }, new TempDataDictionary(), httpContextWrapper.Response.Output);

        // render the partial view
        view.Render(viewContext, httpContextWrapper.Response.Output);
    }

6voto

aarondcoleman Points 416

Voici une approche similaire qui a fonctionné pour moi. La stratégie consiste à convertir la vue partielle en une chaîne de caractères, puis à l'afficher dans la page WebForm.

 public class TemplateHelper
{
    /// <summary>
    /// Render a Partial View (MVC User Control, .ascx) to a string using the given ViewData.
    /// http://www.joeyb.org/blog/2010/01/23/aspnet-mvc-2-render-template-to-string
    /// </summary>
    /// <param name="controlName"></param>
    /// <param name="viewData"></param>
    /// <returns></returns>
    public static string RenderPartialToString(string controlName, object viewData)
    {
        ViewDataDictionary vd = new ViewDataDictionary(viewData);
        ViewPage vp = new ViewPage { ViewData = vd};
        Control control = vp.LoadControl(controlName);

        vp.Controls.Add(control);

        StringBuilder sb = new StringBuilder();
        using (StringWriter sw = new StringWriter(sb))
        {
            using (HtmlTextWriter tw = new HtmlTextWriter(sw))
            {
                vp.RenderControl(tw);
            }
        }

        return sb.ToString();
    }
}

Dans la page codebehind, vous pouvez faire

public partial class TestPartial : System.Web.UI.Page
{
    public string NavigationBarContent
    {
        get;
        set;
    }

    protected void Page_Load(object sender, EventArgs e)
    {
        NavigationVM oVM = new NavigationVM();

        NavigationBarContent = TemplateHelper.RenderPartialToString("~/Views/Shared/NavigationBar.ascx", oVM);

    }
}

et dans la page vous aurez accès au contenu rendu

<%= NavigationBarContent %>

J'espère que cela vous aidera !

0 votes

C'est en fait génial, surtout quand on peut mettre des blocs script quelque part !

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X