Untitled 1
Displaying Model State Errors Inside GridView Columns
ASP.NET 4.5 web forms support model binding features. Additionally they can
make use of model validation using data annotation validators. The validation
errors thrown by data annotation validators can be easily displayed in a web
form using ValiationSummary control. While this arrangement works great, at
times you want something customized. For example, consider the following two
scenarios:
- You may want to display validation error messages thrown by data
annotation validators in front of the respective data entry fields instead (
or in addition to ) of ValidationSummary control.
- Whenever there is any model error the ValidationSummary shows the error
message but there is no indication (such as * mark) about the field causing
the error. You may want to provide such an indication.
This article shows how model errors can be displayed inside a GridView
column. You can use similar technique for other databound controls such as
DetailsView and FormView.
To understand how validation errors can be displayed beside the data entry
controls let's develop a web form as shown below:

Have a look at the above figure carefully. It shows validation error for the
Country field beside the corresponding textbox. The above form uses Employee
data model class. To add this class a new ADO.NET Entity Framework Data
Model to your web application and configure it to select Employees table from
the Northwind database. The following figure shows how the Employee data model
class looks like:

Although the Employee class has many properties this example uses only four
of them viz. EmployeeID, FirstName, LastName and Country.
Now add a web form to the project and place a GridView control on it. Design
the GridView as per the following markup:
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" CellPadding="10"
DataKeyNames="EmployeeID" ItemType="DataAnnotationInWebForms.Employee"
SelectMethod="GridView1_GetData" UpdateMethod="GridView1_UpdateItem">
<Columns>
<asp:BoundField DataField="EmployeeID" HeaderText="Employee ID" ReadOnly="True" />
<asp:TemplateField HeaderText="Full Name">
<EditItemTemplate>
<asp:TextBox ID="TextBox2" runat="server" Text="<%# BindItem.FirstName %>" Columns="10"></asp:TextBox>
<asp:TextBox ID="TextBox3" runat="server" Text="<%# BindItem.LastName %>" Columns="10"></asp:TextBox>
<asp:RequiredFieldValidator ID="RequiredFieldValidator1" runat="server" ControlToValidate="TextBox2"
ErrorMessage="First Name is required!" Font-Bold="True" ForeColor="Red"></asp:RequiredFieldValidator>
<asp:RequiredFieldValidator ID="RequiredFieldValidator2" runat="server" ControlToValidate="TextBox3"
ErrorMessage="Last Name is required!" Font-Bold="True" ForeColor="Red"></asp:RequiredFieldValidator>
</EditItemTemplate>
<ItemTemplate>
<asp:Label ID="Label2" runat="server" Text="<%# Item.FirstName %>"></asp:Label>
<asp:Label ID="Label3" runat="server" Text="<%# Item.LastName %>"></asp:Label>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Country">
<EditItemTemplate>
<asp:TextBox ID="TextBox1" runat="server" Text="<%# BindItem.Country %>"></asp:TextBox>
<br />
<asp:Label ID="lblErr" runat="server" Font-Bold="True" ForeColor="Red" Text=""></asp:Label>
</EditItemTemplate>
<ItemTemplate>
<asp:Label ID="Label1" runat="server" Text="<%# Item.Country %>"></asp:Label>
</ItemTemplate>
</asp:TemplateField>
<asp:CommandField ButtonType="Button" ShowEditButton="True" />
</Columns>
</asp:GridView>
The GridView consists of three columns - one BoundField and two
TemplateFields. The BoundField is bound with EmployeeID column. The first
TemplateField shows FirstName and LastName columns whereas the second
TemplateField shows Country column.
The FirstName and LastName are validated using two RequiredFieldValidator
controls. The Country textbox has a Label (lblErr) placed beside it and the
country will be validated using data annotation validators. The validation erros
(if any) will be displayed in lblErr.
The ItemType property of the GridView is set to the fully qualified name of
the Employee data model class. This enables you to use Item and BindItem objects
inside the ItemTemplate and EditItemTemplate respectively. Also, SelectMethod
and UpdateMethod properties are set to GridView1_GetData and
GridView1_UpdateItem respectively. You will write these two methods shortly.
Below the GridView drag-n-drop a ValidationSummary control and ensure that
its ShowModelStateErrors property is set to True. This property causes the
ValidationSummary control to show all the model errors in addition to errors
thrown by validation controls.
Next step is to create a metadata class for the Employee model class so that
data annotation validators can be attached to the properties of the model class.
The EmployeeMetadata class that does this is shown below:
public class EmployeeMetadata
{
[Required]
[StringLength(20,ErrorMessage="First Name must be less than 20 characters.")]
public string FirstName { get; set; }
[Required]
[StringLength(20, ErrorMessage = "Last Name must be less than 20 characters.")]
public string LastName { get; set; }
[Required]
[StringLength(10, ErrorMessage = "Country must be less than 10 characters.")]
public string Country { get; set; }
}
As you can see the EmployeeMetadata class defines three properties FirstName,
LastName and Country. All the three are decorated with [Required] and [StringLength]
data annotation validators. Notice that the Country property. The [StringLength]
attribute specifies its maximum length to 10 and also specifies an error message
that will be passed on to the UI in case model validation fails.
The EmployeeMetadata class is currently not associated with the Employee
model class in any way. To associate EmployeeMetadata with the Employee class
create a partial class named Employee and use [MetadataType] attribute as shown
below:
[MetadataType(typeof(EmployeeMetadata))]
public partial class Employee
{
}
The [MetadataType] attribute specifies the type of a class that is supplying
metadata to the model class under consideration.
Next, write the GridView1_GetData method in the code behind file as shown
below:
NorthwindEntities db = new NorthwindEntities();
public IQueryable<DataAnnotationInWebForms.Employee> GridView1_GetData()
{
var data = from emp in db.Employees
orderby emp.EmployeeID
select emp;
return data;
}
The GridView1_GetData() method returns IQueryable of Employee objects by
executing a LINQ to Entities query against the Employees DbSet.
Now add GridView1_UpdateItem method as shown below:
public void GridView1_UpdateItem(int employeeid)
{
DataAnnotationInWebForms.Employee item = db.Employees.Find(employeeid);
if (item == null)
{
ModelState.AddModelError("", String.Format("Item with id {0} was not found", employeeid));
}
else
{
TryUpdateModel(item);
if (ModelState.IsValid)
{
//db.SaveChanges();
}
else
{
if (ModelState["Country"].Errors.Count > 0)
{
Label l = (Label)GridView1.Rows[GridView1.EditIndex].FindControl("lblErr");
l.Text = "";
foreach (ModelError err in ModelState["Country"].Errors)
{
l.Text += err.ErrorMessage + "<br/>";
}
}
}
}
}
Notice the code marked in bold letters. The code essentially checks the
ModelState.IsValid property to determine whether there are any model errors. If
IsValid returns false it accesses the ModelState dictionary for the Country key.
The Errors collection contains all the validation errors for that field. If
Errors.Count is greater than zero it means there are errors for Country model
property. It then grabs the lblErr placed inside the EditItemTemplate of Country
column using FindControl() method. It then iterates through the Errors
collection and for each ModelError found there adds the respective error message
in the Text property of the Label.
That's it! Run the web form and enter some Country value longer than 10
characters. You should see error message in the ValidationSummary as well as
lblErr. If required you can set lblErr to a * (or some other short phrase) and
use ValidationSummary to display the full error message.