DisplayNameFor () Dari Daftar <Object> di Model

88

Saya yakin ini cukup sederhana, saya sepertinya tidak dapat menemukan cara yang tepat untuk menampilkan nama tampilan item dalam daftar dalam model saya.

Model saya yang disederhanakan:

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

dan Nama:

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

Sekarang saya ingin membuat tabel untuk menampilkan semua nama seseorang, dan mendapatkan DisplayNameFor FullName. Jelas,

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

tidak akan berhasil, dan

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

akan rusak jika tidak ada nama. Adakah 'cara terbaik' untuk mendapatkan nama tampilan di sini?

Jonesopolis
sumber

Jawaban:

144

Ini benar-benar berfungsi, bahkan tanpa item dalam daftar:

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

Ini berfungsi karena MVC mengurai ekspresi alih-alih mengeksekusinya. Ini memungkinkannya menemukan properti dan atribut yang tepat tanpa perlu ada elemen dalam daftar.

Perlu dicatat bahwa parameter (di modelatas) bahkan tidak perlu digunakan. Ini juga berfungsi:

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

Seperti ini:

@{ Namespace.Of.PersonNameViewModel dummyModel = null; }
@Html.DisplayNameFor(dummyParam => dummyModel.FullName)
Tim S.
sumber
Saya berasumsi bahwa jika alih-alih List<PersonNameViewModel>Anda memiliki IEnumerable<PersonNameViewModel>, maka itu juga aman untuk menggunakan ekspresi model => model.Names.First().FullName. Apakah ini benar? Yang mengatakan, saya pikir saya paling suka dummyModelcontoh terbaik dari ketiganya. Jika tidak, Anda dapat memiliki beberapa properti yang perlu Anda ketik atau tempel model.Names[0].. Kemudian lagi, mungkin Anda harus mengubah bagian menjadi tampilan parsial yang menerima Listatau IEnumerablesebagai modelnya.
aaaantoine
6
Saya menguji dan FirstOrDefault()bekerja dengan daftar kosong.
Josh K
Sayangnya ini tidak berfungsi pada properti IEnumerable.
Willy David Jr
7

Ada cara lain untuk melakukannya, dan saya rasa itu lebih jelas:

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

Dan kemudian, dalam tampilan:

@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
sumber
3

Sebagai solusi alternatif, Anda dapat mencoba:

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

Ini akan berfungsi bahkan jika daftarnya kosong!

Fábio Fabian
sumber
mengapa saya harus menggunakan solusi ini daripada solusi Tim S?
EKanadily
1

Saya suka solusi T-moty. Saya membutuhkan solusi menggunakan obat generik sehingga solusi saya pada dasarnya adalah ini:

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

Menggunakan ini dalam tampilan sama dengan contohnya. Saya membuat satu instance dari objek kosong jadi saya tidak membuat instance baru setiap kali saya mereferensikan Default .

Catatan, batasan new () diperlukan untuk memanggil T () baru . Jika kelas model Anda tidak memiliki kontraktor default, atau Anda perlu menambahkan argumen ke konstruktor, Anda dapat menggunakan ini:

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

Tampilan akan memiliki baris @model seperti:

@model CustomList<Namespace.Of.PersonNameViewModel.Model>
shox
sumber
Perhatikan bahwa DisplayNameFortidak pernah benar-benar memanggil getuntuk properti, jadi tidak masalah jika Anda membuat sebuah instance.
NetMage