7

Consider the following setup:

Model:

public class Product
{
    [ReadOnly(true)]
    public int ProductID
    {
        get;
        set;
    }

    public string Name
    {
        get;
        set;
    }
}

View:

<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" 
Inherits="System.Web.Mvc.ViewPage<MvcApplication4.Models.Product>" %>

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
    Home Page
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
    <%= Html.EditorForModel() %>
</asp:Content>

Controller:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        return View(new Product
            {
                ProductID = 1,
                Name = "Banana"
            });
    }
}

There result is this: alt text

I was expecting that the ProductID property was not going to be editable via the ReadOnly(true) attribute. Is this supported? If not is there any way to hint ASP.NET MVC that some properties of my model are read-only? I would not like to just hide ProductID via [ScaffoldColumn(false)].

Atanas Korchev
  • 30,562
  • 8
  • 59
  • 93

3 Answers3

11

I solved this problem by adding a UIHintAttribute to the property on my class of "ReadOnly".

[UIHint("ReadOnly")]
public int ClassID { get; set; }

Then I simply added a ~\Views\Shared\EditorTemplates\ReadOnly.ascx file to my project with this in it:

<%= Model %>

A really simple way to add custom templates, you could include formatting or whatever.

pwhe23
  • 1,196
  • 1
  • 14
  • 15
  • 6
    An even cooler method could be to subclass the **DataAnnotationsModelMetadataProvider** class like in [Brad Wilson's post](http://bradwilson.typepad.com/blog/2010/01/why-you-dont-need-modelmetadataattributes.html) and simply add this line: `if (metadata.IsReadOnly) metadata.TemplateHint = "ReadOnly";` Combined with the ReadOnly.ascx file in my post, this should make the ReadOnly(true) attribute work like you would expect. ASP.NET MVC rocks! – pwhe23 Feb 25 '11 at 21:17
  • 1
    I know this is old, but the technique works. However, it might result in data being erased. My ReadOnly.cshtml is : @Model @Html.HiddenFor(x=>Model) to ensure the value is always returned. – Andiih Mar 30 '15 at 13:53
9

The ReadOnly and Required attributes will be consumed by the metadata provider but won't be used. If you want to get rid of the input with EditorForModel you'll need a custom template, or [ScaffoldColumn(false)].

For custom template ~/Views/Home/EditorTemplates/Product.ascx:

<%@ Control Language="C#" Inherits="ViewUserControl<Product>" %>

<%: Html.LabelFor(x => x.ProductID) %>
<%: Html.TextBoxFor(x => x.ProductID, new { @readonly = "readonly" }) %>

<%: Html.LabelFor(x => x.Name) %>
<%: Html.TextBoxFor(x => x.Name) %>

Also note that the default model binder won't copy a value into a property with [ReadOnly(false)]. This attribute won't influence the UI rendered by the default templates.

Mikayil Abdullayev
  • 12,117
  • 26
  • 122
  • 206
Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • 1
    Thanks. I thought so. Too bad I don't want to use [ScaffoldColumn(false)] and creating an editor template would kind of defeat the purpose... – Atanas Korchev Aug 18 '10 at 13:01
  • 1
    Well @naso, there are moments like this when we simply curse at Microsoft and continue with our daily jobs :-) Cheers. – Darin Dimitrov Aug 18 '10 at 13:53
2
<%@ Control Language="C#" Inherits="ViewUserControl<Product>" %>

<%: Html.LabelFor(x => x.ProductID) %>
<%: Html.TextBoxFor(x => x.ProductID, new { @readonly = true }) %>

<%: Html.LabelFor(x => x.Name) %>
<%: Html.TextBoxFor(x => x.Name) %>
McDowell
  • 107,573
  • 31
  • 204
  • 267
Dinesh
  • 21
  • 1