9

I'm trying to write a generic (meaning useful in many places) control that I can reuse throughout my company.

I'm having problems with actual C# generics in my view and viewmodel.

Here is an example of what I'm trying to do:

Generic partial view: (_Control.cshtml)

@model SimpleExample<dynamic> 

@Model.HtmlTemplate(Model.Data)

ViewData: (SimpleExample.cs)

public class SimpleExample<T>
{
    public T Data;
    public Func<T, System.Web.WebPages.HelperResult> HtmlTemplate;
}

Example usage: (FullView.cshtml)

@model Foo.MyViewData

@Html.Partial("_Control", new SimpleExample<MyViewData>
{
    Data = Model,
    HtmlTemplate = @<div>@item.SomeProperty</div>
})

The important part of the functionality I'm looking for is that consumers get a typed object when writing their Html inline so they can use Intellisense (as in FullView.cshtml).

Everything compiles fine and the intellisense is working, but I'm getting the error an runtime:

The model item passed into the dictionary is of type 
'AnytimeHealth.Web.ViewData.SimpleExample`1[Foo.MyViewData]', 
but this dictionary requires a model item of type 
'AnytimeHealth.Web.ViewData.SimpleExample`1[System.Object]'.

I've read that I might be able to use covarience on my generic type to get this to work, but I'm not sure how to do that.

Could you please instruct me how I can get this working?

2 Answers 2

5

Change the define in _Control.cshtml:

@model SimpleExample<dynamic> to @model dynamic.

It will work, but will lose the intellisense of SimpleExample, the intellisense of MyViewData will still work.

I think it's because the dynamic type will be known in runtime, but the type of generics

require an early time(maybe is compile time), at that point, only object is be known.

Sign up to request clarification or add additional context in comments.

1 Comment

This is working for me, except I lose intellisense in _Control :(
3

You could use a generic Object and then, using reflection, render the properties of this object (using an helper to list the properties). This is the same approach used by Twitter Bootstrap for MVC 4 (from wich part of this code has been copied, for convenience) : http://nuget.org/packages/twitter.bootstrap.mvc4

_Control.cshtml

@model Object
@using (Html.BeginForm())
{
    @Html.ValidationSummary(true)
    <fieldset>
        <legend>@Model.GetLabel()</legend>
        @foreach (var property in Model.VisibleProperties())
        {
            using(Html.ControlGroupFor(property.Name)){
                @Html.Label(property.Name)
                @Html.Editor(property.Name)
                @Html.ValidationMessage(property.Name, null)
            }
        }
        <button type="submit">Submit</button>
    </fieldset>
}

Helper.cs

public static string GetLabel(this PropertyInfo propertyInfo)
{
    var meta = ModelMetadataProviders.Current.GetMetadataForProperty(null, propertyInfo.DeclaringType, propertyInfo.Name);
    return meta.GetDisplayName();
}

public static PropertyInfo[] VisibleProperties(this IEnumerable Model)
{
    var elementType = Model.GetType().GetElementType();
    if (elementType == null)
    {
        elementType = Model.GetType().GetGenericArguments()[0];
    }
    return elementType.GetProperties().Where(info => info.Name != elementType.IdentifierPropertyName()).ToArray();
}

public static PropertyInfo[] VisibleProperties(this Object model)
{
    return model.GetType().GetProperties().Where(info => info.Name != model.IdentifierPropertyName()).ToArray();
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.