Click here to Skip to main content
Click here to Skip to main content

Required BoundField in GridView

By , 31 May 2011
 

Introduction

I like to use the ASP.NET GridView for administrative functions in my applications. Like editing and managing statuses, types, etc.., usually items that end up in drop down lists where there is a finite number of columns that need to be managed.

Recently, I thought it would be nice if I didn't have to create a TemplateField every time I wanted to use validation. The regular BoundField works very well and makes it easy to use the OrderedDictionaries to retrieve the original and new values so you can update your data source. I found this article that tells you how to create your own BoundField that inherits from the DataControlField. In my case, I didn't need to do this much work because I wasn't binding to different controls, I was just validating the current controls.

Here I am creating a new class and inheriting it from BoundField. By doing this, I don't have to deal with the databinding and value extraction from the controls when updating. I also have two properties that I have added: ErrorForColor and the NumericType which is an enum that I can use to add the appropriate validator.

public class RequiredBoundField : BoundField
{
    const string NumericOnlyString = "NumericOnly";
    const string ErrorForeColorString = "ErrorForeColor";
    const string RequiredFieldErrorText = "*";
    const string NumericOnlyMessage = "Must be numeric";

    public virtual Color ErrorForeColor
    {
        get
        {
            object o = base.ViewState[ErrorForeColorString];
            if (o != null)
            {
                return (Color)o;
            }
            else
            {
                return Color.Red;
            }
        }
        set
        {
            base.ViewState[ErrorForeColorString] = value;
            OnFieldChanged();
        }
    }

    public virtual NumbericFieldType NumericType
    {
        get
        {
            object o = base.ViewState[NumericOnlyString];
            if (o != null)
            {
                return (NumbericFieldType)o;
            }
            else
            {
                return NumbericFieldType.Undefined;
            }
        }
        set
        {
            base.ViewState[NumericOnlyString] = value;
            OnFieldChanged();
        }
    }
}

At this point, we are going to override two functions: CreateField and InitializeDataCell. See the code below:

protected override DataControlField CreateField()
{
    return new RequiredBoundField();
}

protected override void InitializeDataCell(DataControlFieldCell cell, 
                        DataControlRowState rowState)
{
    base.InitializeDataCell(cell, rowState);

    DataControlRowState state = rowState & DataControlRowState.Edit;
    if ((!ReadOnly && (state != DataControlRowState.Normal)))
    {
        TextBox textBox = cell.Controls[0] as TextBox;
        textBox.ID = this.DataField;

        RequiredFieldValidator validator = new RequiredFieldValidator();
        validator.ControlToValidate = this.DataField;
        validator.ErrorMessage = RequiredFieldErrorText;
        validator.ForeColor = this.ErrorForeColor;
        cell.Controls.Add(validator);

        if (this.NumericType != NumbericFieldType.Undefined)
        {
            CompareValidator compareValidator = new CompareValidator();
            compareValidator.ControlToValidate = this.DataField;
            compareValidator.ErrorMessage = NumericOnlyMessage;
            compareValidator.ForeColor = this.ErrorForeColor;
            compareValidator.Operator = ValidationCompareOperator.DataTypeCheck;

            switch (this.NumericType)
            {
                case NumbericFieldType.Integer:
                    compareValidator.Type = ValidationDataType.Integer;
                    break;
                case NumbericFieldType.Decimal:
                    compareValidator.Type = ValidationDataType.Double;
                    break;
            }                    
            
            cell.Controls.Add(compareValidator);
        }

    }
}

By using these key lines:

TextBox textBox = cell.Controls[0] as TextBox;
textBox.ID = this.DataField; 

I can add the validators and use the ID to assign the validators to the appropriate text box. This will only work for cases where you would have used a BoundField. Not a CheckBoxField or something else like that.

OK, so now let's look at the implementation. First, in my case, I add the control namespace and assembly to the configuration file. Make sure to add it in the system.web element.

<pages controlRenderingCompatibilityVersion="4.0" clientIDMode="AutoID">
   <controls>
      <add assembly="RequiredBoundFieldDemo" 
         namespace="RequiredBoundFieldDemo.Controls" 
         tagPrefix="custom" />
   </controls>
</pages>

Now that this is in the config file, you will see this in intellisense when you are trying to add columns to a GridView. Here is the code for my GridView:

<asp:GridView ID="sampleGridView" runat="server" 
        GridLines="None" AutoGenerateColumns="False"
        DataKeyNames="Id" AutoGenerateEditButton="True" 
        CellPadding="5" 
        OnRowCancelingEdit="sampleGridView_RowCancelingEdit"
        OnRowEditing="sampleGridView_RowEditing" 
        OnRowUpdating="sampleGridView_RowUpdating">
    <Columns>
        <asp:BoundField HeaderText="Id" 
            DataField="Id" ReadOnly="true" />
        <custom:RequiredBoundField HeaderText="Required Text" 
            DataField="RequiredText" />
        <custom:RequiredBoundField HeaderText="Required Integer" 
            DataField="RequiredInteger"
            NumericType="Integer" />
        <custom:RequiredBoundField HeaderText="Required Decimal" 
            DataField="RequiredDecimal"
            NumericType="Decimal" />
        <asp:BoundField DataField="CreatedBy" 
            ReadOnly="true" HeaderText="Created By" />
        <asp:BoundField DataField="CreateTime" 
            ReadOnly="true" HeaderText="Date Created"
            DataFormatString="{0:d}" />
    </Columns>
</asp:GridView>

You can see that I am using a combination of regular BoundFields and my own that is named custom:RequiredBoundField. You also see on two of these columns that I have added the NumericType. Intellisense will also prompt you for the available options in the NumericType because it is an enum.

So now when you want to edit and update a row, you can use the e.NewValues Dictionary to retrieve the values that have been updated because the BoundField is taking care of all of that for you. See the code below:

protected void sampleGridView_RowUpdating(object sender, GridViewUpdateEventArgs e)
{
    int selectedId = (int)e.Keys[Id];

    using (SampleEntities context = new SampleEntities())
    {
        Demo demoObject = context.Demoes.FirstOrDefault(i => i.Id == selectedId);

        demoObject.RequiredText = Convert.ToString(e.NewValues[RequiredText]);
        demoObject.RequiredInteger = Convert.ToInt32(e.NewValues[RequiredInteger]);
        demoObject.RequiredDecimal = Convert.ToDecimal(e.NewValues[RequiredDecimal]);

        context.SaveChanges();

        sampleGridView.DataSource = context.Demoes.ToList();
    }

    sampleGridView.EditIndex = -1;
    sampleGridView.DataBind();

}

So that is it. Please feel free to download the demo project if you want to see a version that is running from start to finish. This will make my life easier from now on and I hope it does the same for you!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Ben Kotvis
United States United States
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionUnable to override the "Visible" Property of a Bound Columnmembermoiztankiwala18 May '12 - 7:16 
I am trying to add custom logic to the BoundColumn by inheriting it and overriding the "Visible" property. However I am unable to override this property.
 
"Visible" is a property on the DataControlField and BoundColumn inherits from DataControlField.
 
Why is it not possible to override this field in a class that derives from BoundColumn?
 
If the Visibility cannot be overridden here, what is an alternative? My requirement is show a column only if the user is in certain role and there will be logic inside the Visible:Get that will ultimately control the rendering of a particular column.
 
Thanks in advance for reading my question thus far and for proposing a solutions.
 
-Moiz Tankiwala
If you think you can do something, you can do it.

AnswerRe: Unable to override the "Visible" Property of a Bound ColumnmemberBen Kotvis18 May '12 - 8:14 
The DataControlField's Visible property is not virtual so it can't be overriden. I am not sure why you need to override it. If you are inheriting my RequiredBoundField class you could set the visibility in the Initialize() method. Or you can use the "new" keyword on a "Visible" property. See below:
 
        public new virtual bool Visible
        {
            get { return this.Visible; }
            set { this.Visible = value; }
        }
 
Let me know if you need any additional help.
GeneralMy vote of 5memberMonjurul Habib6 Jun '11 - 21:03 
my 5

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web01 | 2.6.130516.1 | Last Updated 31 May 2011
Article Copyright 2011 by Ben Kotvis
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid