DisplayNameFor() From List<Object> in Model

Viewed 37218

I believe this is pretty simple, I just can't seem to find the right way to show the display name for an item within a list within my model.

My simplified model:

public class PersonViewModel
{
    public long ID { get; set; }
    
    private List<PersonNameViewModel> names = new List<PersonNameViewModel>();

    [Display(Name = "Names")]
    public List<PersonNameViewModel> Names { get { return names; } set { names = value; } }      
}

and Names:

public class PersonNameViewModel
{
    public long ID { get; set; }

    [Display(Name = "Set Primary")]
    public bool IsPrimary { get; set; }

    [Display(Name = "Full Name")]
    public string FullName { get; set; }
}

Now I'd like to make a table to show all the names for a person, and get the DisplayNameFor FullName. Obviously,

@Html.DisplayNameFor(model => model.Names.FullName);

wouldn't work, and

@Html.DisplayNameFor(model => model.Names[0].FullName);  

will break if there are no names. Is there a 'best way' to obtain the display name here?

5 Answers

As an alternate solution you could try:

@Html.DisplayNameFor(x => x.GetEnumerator().Current.ItemName)

It will work even if the list is empty!

In ASP.NET Core, Html.DisplayNameForInnerType() solves this. It can be used this way:

Html.DisplayNameForInnerType((PersonNameViewModel person) => person.FullName)

Note that the type PersonNameViewModel has to be explicitly specified in the lambda expression's parameter.

This is what the API documentation has to say:

Returns the display name for the specified expression if the current model represents a collection.

It works when the current Model itself is a collection and also when one of its members is a collection.

I like T-moty's solution. I needed a solution using generics so my solution is essentially this:

public class CustomList<T> : List<T> where T : new()
{       
    public static async Task<CustomList<T>> CreateAsync(IQueryable<T> source)
    {           
        return new CustomList<T>(List<T> list); //do whatever you need in the contructor, constructor omitted
    }

    private T defaultInstance = new T();

    public T Default
    {
        get { return defaultInstance; }
    }
}

Using this in the view is the same as his example. I create a single instance of an empty object so I'm not creating a new instance every time I reference Default.

Note, the new() constraint is needed in order to call new T(). If your model class doesn't have a default contructor, or you need to add arguments to the constructor you can use this:

private T defaultInstance = (T)Activator.CreateInstance(typeof(T), new object[] { "args" });

The view will have an @model line like:

@model CustomList<Namespace.Of.PersonNameViewModel.Model>
Related