tags:

views:

317

answers:

3

If I have a HomeController displaying its Index view, how would I proceed in order to have the Index view imbed a UserControl from another Controller?

Here's a look at the content of the Home/Index View:

<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %>

<asp:Content ID="indexTitle" ContentPlaceHolderID="TitleContent" runat="server">
    <%=Resources.Global.HomeTitle %>
</asp:Content>

<asp:Content ID="indexContent" ContentPlaceHolderID="MainContent" runat="server">
    <h2><%= Html.Encode(ViewData["Message"]) %></h2>
    <p><%=Resources.Global.HomeIndex %></p>

    <h3>Partial title</h3>
    <% Html.RenderPartial("~/Views/OtherController/SomeAction.ascx"); %>

</asp:Content>

Here's the OtherController content:

public class OtherController : BaseController
{
    private readonly IRepositoryContract<SomeType> repo = new SomeTypeRepository();

    public ActionResult SomeAction()
    {
        IQueryable<SomeType> items = repo.GetAllItems();
        return View("SomeAction", items);
    }
}

This gives me an NullReferenceException since the Controller is never being called by the RenderPartial() method. Changing the following line

<% Html.RenderPartial("~/Views/OtherController/SomeAction.ascx"); %>

by this

<% Html.RenderPartial("~/Views/OtherController/SomeAction.ascx",((ViewResult) new OtherController().SomeAction()).ViewData.Model); %>

works, but it sure is ugly as hell. There has to be a better way to imbed partials from another controller?

Update :: Solution found

Here's the code after implementing Adrian Grigore's solution:

<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %>
<%@ Import Namespace="Microsoft.Web.Mvc"%>

    <asp:Content ID="indexTitle" ContentPlaceHolderID="TitleContent" runat="server">
        <%=Resources.Global.HomeTitle %>
    </asp:Content>

    <asp:Content ID="indexContent" ContentPlaceHolderID="MainContent" runat="server">
        <h2><%= Html.Encode(ViewData["Message"]) %></h2>
        <p><%=Resources.Global.HomeIndex %></p>

        <h3>Partial title</h3>
        <% Html.RenderAction("SomeAction","OtherController"); %>

    </asp:Content>
+1  A: 

Put partials used by more than one controller into the Shared folder.

The model must be passed by the page. Construct it in the controller, not in the view. Then pass it like this:

<% Html.RenderPartial("SomeAction", Model.SomeActionData); %>

Note that if Model.SomeActionData is null, then MVC will pass Model instead of Model.SomeActionData. Make sure your code can tolerate that.

Craig Stuntz
You mean that I would have to duplicate (or inherit) the model construction logic in every controller that imbed the shared partial?I understand I could just pass a ViewModel containing the info used by that shared partial to the Home/Index view, but that means I would need to construct/add this data to every other View's ViewModel using this partial as well...It would make more sense to me if there was a way to actually render partials directly from another controller in which the related Model/ViewModel is implemented.
matthew.perron
No, you should never duplicate code. At times you do need to modularize it, though. Adrian's suggestion works fine if you don't mind using Futures; the suggestion I made works if you would prefer to avoid it.
Craig Stuntz
+2  A: 

Use the Html.RenderAction method from the ASP.NET MVC Futures library.

Adrian Grigore
That works great! Thank you sir. I changed the <% Html.RenderPartial("~/Views/OtherController/SomeAction.ascx"); %> line by <% Html.RenderAction("SomeAction","OtherController"); %> and it works wonder, calling the OtherController and displaying the correct data.
matthew.perron
A: 

It sounds like you should have the shared UserControl in a master page (perhaps nested) so that the View doesn't need to know about controllers other than it's parent. Stephen Walther has a few good strategies for passing data to master pages and user controls.

  1. Code-behind on master page (BAD)
  2. ActionFilter
  3. Calling the partial directly
  4. Abstract base controller classes
Kelly Adams