Search code examples
asp.net-mvc-3templatesasp.net-mvc-2modelmodelmetadata

MVC2 - How to obtain parent model (container) inside template


I'm writing an MVC2 app using DataAnnotations. I have a following Model:

public class FooModel 
{
    [ScaffoldColumn("false")]
    public long FooId { get; set; }

    [UIHint("BarTemplate")]
    public DateTime? Bar { get; set;}
}

I want to create a custom display template for Bar. I have created following template:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<DateTime?>" %>

<div class="display-label">
    <span><%: Html.LabelForModel() %></span>
</div>
<div class="display-field">
    <span><%: Html.DisplayForModel()%></span>
    <%: Html.ActionLink("Some link", "Action", new { id = ??FooId?? }) %>
</div>

Now, my problem is that inside template for Bar I want to access another property from my model. I don't want to create a separate template for FooModel because than I will have to hardcode all other FooModel properties.

After a brief investigation with a debugger I can see that:

  1. this.ViewData.ModelMetadata.ContainerType is FooModel (as expected)
  2. this.ViewData.TemplateInfo has a non-public property VisitedObjects (of type System.Collections.Generic.HashSet<object>) which contains two elements: FooModel and DateTime?.

How can I get access to my FooModel? I don't want to hack my way around using Reflection.

Update:

I've accepted mootinator's answer as it looks to me as the best solution that allows type-safety. I've also upvoted Tx3's answer, as mootinator's answer builds upon it. Nevertheless, I think that there should be a better support form MVC in those kind of scenarios, which I believe are quite common in real world but missing from sample apps.


Solution

  • Sorry if this suggestion seems daft, I haven't tried it, but couldn't you do what Tx3 suggested without having to create a bunch of new classes by defining a generic class to reference whatever type of parent you want?

        public class FooModel 
        {
            [ScaffoldColumn("false")]
            public long FooId { get; set; }
    
            [UIHint("BarTemplate")]
            public ParentedDateTime<FooModel> Bar { get; set;}
    
            public FooModel()
            {
                Bar = new ParentedDateTime<FooModel>(this);
            }
        }
    
    
        public class ParentedDateTime<T>
        {
            public T Parent {get; set;}
            public DateTime? Babar {get; set; }
    
            public ParentedDateTime(T parent)
            {
                Parent = parent;
            }
    
    }
    

    You could expand that to encapsulate any old type with a <Parent, Child> typed generic, even.

    That would also give you the benefit that your strongly typed template would be for

    Inherits="System.Web.Mvc.ViewUserControl<ParentedDateTime<FooType>> thus you would not have to explicity name which template to use anywhere. This is more how things are intended to work.