DisplayNameFor () из списка <объект> в модели

87

Я считаю, что это довольно просто, я просто не могу найти правильный способ показать отображаемое имя для элемента в списке в моей модели.

Моя упрощенная модель:

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; } }      
}

и имена:

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; }
}

Теперь я хотел бы создать таблицу, в которой будут отображаться все имена для человека, и получить DisplayNameFor FullName. Очевидно,

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

не будет работать, и

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

сломается, если нет имен. Есть ли здесь «лучший способ» получить здесь отображаемое имя?

Jonesopolis
источник

Ответы:

143

Это действительно работает даже без элементов в списке:

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

Это работает, потому что MVC анализирует выражение, а не выполняет его. Это позволяет ему находить нужное свойство и атрибут без необходимости наличия элемента в списке.

Стоит отметить, что параметр (см. modelВыше) даже не нужно использовать. Это тоже работает:

@Html.DisplayNameFor(dummy => Model.Names[0].FullName)

Как это делает:

@{ Namespace.Of.PersonNameViewModel dummyModel = null; }
@Html.DisplayNameFor(dummyParam => dummyModel.FullName)
Тим С.
источник
Я предполагаю, что если вместо a у List<PersonNameViewModel>вас есть IEnumerable<PersonNameViewModel>, то можно также использовать выражение model => model.Names.First().FullName. Это правильно? Тем не менее, я думаю, что мне больше всего нравится dummyModelпример из трех. В противном случае у вас может быть несколько свойств, которые вам нужно ввести или вставить model.Names[0].. Опять же, возможно, вам следует реорганизовать раздел до частичного представления, которое принимает Listили в IEnumerableкачестве своей модели.
aaaantoine
6
Я тестировал и FirstOrDefault()работаю с пустым списком.
Josh K
К сожалению, это не работает со свойством IEnumerable.
Вилли Дэвид-младший,
7

Есть другой способ сделать это, и я думаю, он более понятен:

public class Model
{
    [Display(Name = "Some Name for A")]
    public int PropA { get; set; }

    [Display(Name = "Some Name for B")]
    public string PropB { get; set; }
}

public class ModelCollection
{
    public List<Model> Models { get; set; }

    public Model Default
    {
        get { return new Model(); }
    }
}

А потом в представлении:

@model ModelCollection

<div class="list">
    @foreach (var m in Model.Models)
    {
        <div class="item">
            @Html.DisplayNameFor(model => model.Default.PropA): @m.PropA
            <br />
            @Html.DisplayNameFor(model => model.Default.PropB): @m.PropB
        </div>
    }
</div>
T-moty
источник
1

Мне нравится решение T-moty. Мне нужно было решение с использованием дженериков, поэтому мое решение по сути таково:

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; }
    }
}

Использование этого в представлении аналогично его примеру. Я создаю один экземпляр пустого объекта, поэтому я не создаю новый экземпляр каждый раз, когда ссылаюсь на Default .

Обратите внимание: ограничение new () необходимо для вызова new T () . Если у вашего класса модели нет конструктора по умолчанию или вам нужно добавить аргументы в конструктор, вы можете использовать это:

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

В представлении будет строка @model, например:

@model CustomList<Namespace.Of.PersonNameViewModel.Model>
Shox
источник
Обратите внимание, что на DisplayNameForсамом деле никогда не вызывается getдля свойства, поэтому не имеет значения, создаете ли вы экземпляр.
NetMage
1

В качестве альтернативного решения вы можете попробовать:

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

Он будет работать, даже если список пуст!

Фабио Фабиан
источник
почему я должен использовать это решение вместо решения Тима С.?
EKanadily