Search code examples
asp.net-mvc-4

Define order for properties in model mvc4


we are showing model properties in grid format using DisplayForModel().

I just want to know is there any way we can define the order for the meta data properties.

For ex:

Current display: Party, PartyRole, Id, Title FromDate, ThruDate

Desired display: Title, Party, PartyRole, FromDate, ThruDate, Id

Is it possible in any way ???

display template - object.cshtml

@functions
{
bool ShouldShow(ModelMetadata metadata)
{
    return metadata.ShowForDisplay
        && metadata.ModelType != typeof(System.Data.EntityState)
        && !metadata.IsComplexType
        && !ViewData.TemplateInfo.Visited(metadata);
}
}
<table>
<tr>
    @if (TempData["header"] == null)
    {
        foreach (var property in ViewData.ModelMetadata.Properties.Where(pm => ShouldShow(pm)))
        {
            if (property.DisplayName != "Id")
            { 
        <th>
            @property.GetDisplayName()
        </th>
            }
        }
        TempData["header"] = "true";
    }
</tr>
<tr>
    @foreach (var prop in ViewData.ModelMetadata.Properties.Where(pm => ShouldShow(pm)))
    {
        if (prop.DisplayName == "Id")
        {
             <td>@Html.ActionLink("Edit", "Edit", new { id = @Html.Display(prop.PropertyName)  }) </td>
        }
        else
        { 
           <td>@Html.Display(prop.PropertyName)</td>
        }
    }
    </tr>
</table>

grid.cshtml

@model IEnumerable<object>
<div class="grid">
@Html.DisplayForModel()

</div>
<div id="my-dialog"></div>

Thanks a lot in advance..


Solution

  • If you're using the default DataAnnotationsModelMetadataProvider, you can use the DisplayAttribute's Order property (info here) on the properties of the model, i.e. (guessing at your ViewModel):

    public class ViewModel
    {
        [Display(Order = 1)]
        public string Party { get; set; }
    
        [Display(Order = 2)]
        public SomeRole PartyRole { get; set; }
    
        [Display(Order = 5)]
        public int Id { get; set; }
    
        [Display(Order = 0)]
        public string Title { get; set; }
    
        [Display(Order = 3)]
        public DateTime FromDate { get; set; }
    
        [Display(Order = 4)]
        public DateTime ThruDate { get; set; }
    }
    

    ModelMetadata.Properties is sorted by that attribute value by default.

    Note that in some cases (but see below!), this really only makes sense when using inheritance in your models - because if all the properties are defined on a single ViewModel class, it's mostly simpler to just put them in the correct order in that class in the first place, i.e.:

    public class ViewModel
    {
        public string Title { get; set; }
        public string Party { get; set; }
        public SomeRole PartyRole { get; set; }
        public DateTime FromDate { get; set; }
        public DateTime ThruDate { get; set; }
        public int Id { get; set; }
    }
    

    Late addition, 2021: However, the source order is actually an implementation detail - properties are not guaranteed to be returned by Reflection in source order (although they often are in practice). The safe way of doing this is to use some kind of Order property.

    In the case of MVC, you still need to use the Display(Order = ...) attribute like above, but in other cases - where the consumer of the attribute is your own code, note that there's a hack that doesn't require you to define the order manually - you can create your own OrderAttribute and use the CallerLineNumber attribute in its constructor to ensure ordering properties decorated with your attribute ([Order]) by source order "automatically":

    [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
    public sealed class OrderAttribute : Attribute
    {
        public int Order { get; }
        public OrderAttribute([CallerLineNumber] int order = 0)
        {
            Order = order;
        }
    }