Showing Different fields in EditorForModel vs. DisplayForModel modes in MVC2
Asked Answered
K

4

10

I have a viewmodel that has an int StateID field and a string StateName field like so:

public class DepartmentViewModel : BaseViewModel, IModelWithId
{
    // only show in edit mode
    public int StateId { get; set; }

    // only show in display mode
    public string StateName { get; set; }
}

I have a read only view that uses DisplayForModel and an update view that uses EditorForModel. I want the DisplayForModel view to show the StateName property, and the EditorForModel view use the StateID property (I am actually rendering a dropdownlist based on this).

I have not been able to figure out how to decorate my viewmodel properties to create this behavior.

Kish answered 2/6, 2010 at 15:14 Comment(0)
K
14

I wanted a solution that was more generic, so I created a new attribute:

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public sealed class RenderModeAttribute : Attribute
{
    public RenderMode RenderMode { get; set; }

    public RenderModeAttribute(RenderMode renderMode)
    {
        RenderMode = renderMode;
    }
}

public enum RenderMode
{
    Any,
    EditModeOnly,
    DisplayModeOnly
}

And included the following code in my custom DataAnnotationsModelMetadataProvider:

var renderModeAttribute = attributes.OfType<RenderModeAttribute>();
if (renderModeAttribute.Any())
{
    var renderMode = renderModeAttribute.First().RenderMode;
    switch (renderMode)
    {
        case RenderMode.DisplayModeOnly:
            metadata.ShowForDisplay = true;
            metadata.ShowForEdit = false;
            break;
        case RenderMode.EditModeOnly:
            metadata.ShowForDisplay = false;
            metadata.ShowForEdit = true;
            break;
    }
}

So that I could just decorate my model as so:

public class DepartmentViewModel    
{     
    [RenderMode(RenderMode.EditModeOnly)]   
    public int StateId { get; set; }     

    [RenderMode(RenderMode.DisplayModeOnly)]    
    public string StateName { get; set; }     
} 
Kish answered 2/6, 2010 at 18:10 Comment(3)
How do you implement the custom DataAnnotationsModelMetadataProvider? Thanks.Ubiquitarian
@Ubiquitarian - Inherit from DataAnnotationsModelMetadataProvider. weblogs.asp.net/seanmcalinden/archive/2010/06/11/…Kish
Thanks for sharing your solution. It would be useful though if you could give details of where the second code snippet needs to go (I assume we need to override CreateMetadata and put it in there?) and also how to register our custom DataAnnotationsModelMetadataProvider once created.Blisse
W
17

A comment on CodeGrue's answer.

Make the attribute inherit IMetadataAware instead. That way you don't need to build your own DataAnnotationsModelMetadataProvider.

The new attribute would become something like:

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public sealed class RenderModeAttribute : Attribute, IMetadataAware
{
    public RenderMode RenderMode { get; set; }

    public RenderModeAttribute(RenderMode renderMode)
    {
        RenderMode = renderMode;
    }

    public void OnMetadataCreated(ModelMetadata metadata)
    {
        switch (RenderMode)
        {
            case RenderMode.DisplayModeOnly:
                metadata.ShowForDisplay = true;
                metadata.ShowForEdit = false;
                break;

            case RenderMode.EditModeOnly:
                metadata.ShowForDisplay = false;
                metadata.ShowForEdit = true;
                break;

            case RenderMode.None:
                metadata.ShowForDisplay = false;
                metadata.ShowForEdit = false;
                break;
        }
    }
}

public enum RenderMode
{
    Any,
    EditModeOnly,
    DisplayModeOnly
}
Watch answered 6/12, 2011 at 7:36 Comment(0)
K
14

I wanted a solution that was more generic, so I created a new attribute:

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public sealed class RenderModeAttribute : Attribute
{
    public RenderMode RenderMode { get; set; }

    public RenderModeAttribute(RenderMode renderMode)
    {
        RenderMode = renderMode;
    }
}

public enum RenderMode
{
    Any,
    EditModeOnly,
    DisplayModeOnly
}

And included the following code in my custom DataAnnotationsModelMetadataProvider:

var renderModeAttribute = attributes.OfType<RenderModeAttribute>();
if (renderModeAttribute.Any())
{
    var renderMode = renderModeAttribute.First().RenderMode;
    switch (renderMode)
    {
        case RenderMode.DisplayModeOnly:
            metadata.ShowForDisplay = true;
            metadata.ShowForEdit = false;
            break;
        case RenderMode.EditModeOnly:
            metadata.ShowForDisplay = false;
            metadata.ShowForEdit = true;
            break;
    }
}

So that I could just decorate my model as so:

public class DepartmentViewModel    
{     
    [RenderMode(RenderMode.EditModeOnly)]   
    public int StateId { get; set; }     

    [RenderMode(RenderMode.DisplayModeOnly)]    
    public string StateName { get; set; }     
} 
Kish answered 2/6, 2010 at 18:10 Comment(3)
How do you implement the custom DataAnnotationsModelMetadataProvider? Thanks.Ubiquitarian
@Ubiquitarian - Inherit from DataAnnotationsModelMetadataProvider. weblogs.asp.net/seanmcalinden/archive/2010/06/11/…Kish
Thanks for sharing your solution. It would be useful though if you could give details of where the second code snippet needs to go (I assume we need to override CreateMetadata and put it in there?) and also how to register our custom DataAnnotationsModelMetadataProvider once created.Blisse
C
1

Here is my custom provider, I add a None render mode

public class CustomModelMetadataProvider : DataAnnotationsModelMetadataProvider
{
    protected override ModelMetadata CreateMetadata(System.Collections.Generic.IEnumerable<System.Attribute> attributes, System.Type containerType, System.Func<object> modelAccessor, System.Type modelType, string propertyName)
    {
        var metadata = base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName);

        var renderModeAttribute = attributes.OfType<RenderModeAttribute>();
        if (renderModeAttribute.Any())
        {
            var renderMode = renderModeAttribute.First().RenderMode;
            switch (renderMode)
            {
                case RenderMode.DisplayModeOnly:
                    metadata.ShowForDisplay = true;
                    metadata.ShowForEdit = false;
                    break;

                case RenderMode.EditModeOnly:
                    metadata.ShowForDisplay = false;
                    metadata.ShowForEdit = true;
                    break;

                case RenderMode.None:
                    metadata.ShowForDisplay = false;
                    metadata.ShowForEdit = false;
                    break;
            }
        } 

        return metadata;
    }
}
Coastward answered 2/3, 2011 at 18:51 Comment(0)
P
0

Override the templates:

In ~/Shared/EditorTemplates/DepartmentViewModel.ascx put:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Namespace.DepartmentViewModel>" %>
<%= Html.LabelFor(x => x.StateId) %>
<%= Html.TextBoxFor(x => x.StateId) %>

And in your ~/Shared/DisplayTemplates/DepartmentViewModel.ascx:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Namespace.DepartmentViewModel>" %>
<div><%= Html.Encode(Model.StateName) %></div>
Polyvinyl answered 2/6, 2010 at 16:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.