DisplayNameFor () z listy <Object> w modelu

87

Uważam, że jest to całkiem proste, po prostu nie mogę znaleźć właściwego sposobu wyświetlania nazwy pozycji na liście w moim modelu.

Mój uproszczony 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; } }      
}

i nazwy:

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

Teraz chciałbym utworzyć tabelę, aby pokazać wszystkie imiona i nazwiska osób, i pobrać DisplayNameFor FullName. Oczywiście,

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

nie zadziała i

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

pęknie, jeśli nie będzie nazw. Czy istnieje „najlepszy sposób” uzyskania tutaj nazwy wyświetlanej?

Jonesopolis
źródło

Odpowiedzi:

143

To faktycznie działa, nawet bez pozycji na liście:

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

Działa, ponieważ MVC analizuje wyrażenie, zamiast je wykonywać. Dzięki temu może znaleźć odpowiednią właściwość i atrybut bez konieczności umieszczania elementu na liście.

Warto zauważyć, że parametru ( modelpowyżej) nie trzeba nawet używać. To też działa:

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

Jak to robi:

@{ Namespace.Of.PersonNameViewModel dummyModel = null; }
@Html.DisplayNameFor(dummyParam => dummyModel.FullName)
Tim S.
źródło
Zakładam, że jeśli zamiast a List<PersonNameViewModel>masz, to możesz IEnumerable<PersonNameViewModel>również bezpiecznie użyć wyrażenia model => model.Names.First().FullName. Czy to jest poprawne? To powiedziawszy, myślę, że najbardziej podoba mi się dummyModelprzykład z tych trzech. W przeciwnym razie możesz mieć kilka właściwości, w których musisz wpisać lub wkleić model.Names[0].. Z drugiej strony, może powinieneś refaktoryzować przekrój do częściowego widoku, który akceptuje Listlub IEnumerablejako model.
aaaantoine
6
Przetestowałem i FirstOrDefault()działa z pustą listą.
Josh K
Niestety nie działa to na właściwości IEnumerable.
Willy David Jr
7

Jest na to inny sposób i wydaje mi się, że jest to bardziej przejrzyste:

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

A potem w widoku:

@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
źródło
1

Podoba mi się rozwiązanie T-moty. Potrzebowałem rozwiązania wykorzystującego typy generyczne, więc moje rozwiązanie jest zasadniczo takie:

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

Użycie tego w widoku jest tym samym, co jego przykład. Tworzę pojedyncze wystąpienie pustego obiektu, więc nie tworzę nowego wystąpienia za każdym razem, gdy odwołuję się do Default .

Uwaga, ograniczenie new () jest potrzebne do wywołania funkcji new T () . Jeśli twoja klasa modelu nie ma domyślnego konstruktora lub musisz dodać argumenty do konstruktora, możesz użyć tego:

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

Widok będzie miał linię @model , taką jak:

@model CustomList<Namespace.Of.PersonNameViewModel.Model>
shox
źródło
Zauważ, że w DisplayNameForrzeczywistości nigdy nie wywołuje getwłaściwości, więc nie ma znaczenia, czy utworzysz instancję.
NetMage
1

Jako alternatywne rozwiązanie możesz spróbować:

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

Będzie działać, nawet jeśli lista jest pusta!

Fábio Fabian
źródło
dlaczego powinienem używać tego rozwiązania zamiast rozwiązania Tima S.
EKanadily