5

I'm building a custom HTML helper with an expression to draw a tag cloud where the data that goes into the tag cloud comes from the expression. I'll let the code do the talking here:

View Model

public class ViewModel
{
    public IList<MyType> MyTypes { get; set; }
    public IList<MyOtherType> MyOtherTypes { get; set; }
}

View

<div>
    @Html.TagCloudFor(m => m.MyTypes)
</div>

<div>
    @Html.TagCloudFor(m => m.MyOtherTypes)
</div>

Helper

public static MvcHtmlString TagCloudFor<TModel, TProperty>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> expression) where TProperty : IList<MyType> where TProperty : IList<MyOtherType>
{
    // So my actual question here is: how do I get my IList<TProperty> collection 
    // so I can iterate through and build my HTML control
}

I've had a quick look around and have done the usual Google searches, but I can't quite seem to find that specific answer. I assume it's somewhere in the region of expression.Compile().Invoke() but I'm not sure what the correct parameters to pass are.

I should also mention that MyType and MyOtherType have a similar property of Id but there's no inheritance here, they are completely separate objects hence I'm constraining my TProperty as IList<MyType> and IList<MyOtherType>. Am I heading down the wrong path here, I feel like this should be obvious, but my brain isn't playing.

2
  • 1
    If you have to constraints, one where TProperty: IList<MyType> and where TProperty: IList<MyOtherType>, and MyType and MyOtherType are incompatible, than it's not going to work. Both constraints are applied. Commented Aug 23, 2012 at 13:03
  • 1
    @Maarten: yes, I thought this too I've removed the 2nd constraint for now to get the list working, and then will re-look at my inheritance structure. These objects are POCO entities for Entity Framework so it's looking like I'll need a common ancestor for each. Commented Aug 23, 2012 at 13:10

2 Answers 2

9

The following should do it:

public static MvcHtmlString TagCloudFor<TModel , TProperty>( this HtmlHelper<TModel> helper , Expression<Func<TModel , TProperty>> expression )
        where TProperty : IList<MyType>, IList<MyOtherType> {

        //grab model from view
        TModel model = (TModel)helper.ViewContext.ViewData.ModelMetadata.Model;
        //invoke model property via expression
        TProperty collection = expression.Compile().Invoke(model);

        //iterate through collection after casting as IEnumerable to remove ambiguousity
        foreach( var item in (System.Collections.IEnumerable)collection ) {
            //do whatever you want
        }

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

5 Comments

If MyType and MyOtherType are incompatible, than the usage of your htmlhelper won't compile since no type will satisfy both your constraints. I think you need two html helpers, on for MyType, one for MyOtherType. The generic argument TProperty is then obsolete.
@Burnzy: that looks really promising, however I'm getting the following errors on the line expression.Compile().Invoke(model) - cannot convert from 'object' to 'TModel' and Delegate 'System.Func<TModel,TProperty>' has some invalid arguments
@Maarten I believe TProperty wont be explicitly defined, it'll be implicitly defined by the expression property type.
@Terric I believe you had to cast it. Look at newer edit. You posted right before I edited :)
@Burnzy: bingo! Yep that's done it, thanks for that much appreciated!
1

How about....

public static MvcHtmlString TagCloudFor<TModel , TItemType>( this HtmlHelper<TModel> helper , Expression<Func<TModel , IEnumerable<TItemType>>> expression ) 
// optional --- Where TItemType : MyCommonInterface
 { 

        TModel model = (TModel)helper.ViewContext.ViewData.ModelMetadata.Model; 
        //invoke model property via expression 
        IEnumerable<TItemType> collection = expression.Compile().Invoke(model); 

        foreach( var item in collection ) { 
            //do whatever you want 
        } 

    } 

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.