This is a simple trick that came into my mind working on a legacy ASP.NET application, the scenario is: we have a series of components that use a GridView to show some data, each GridView have several columns and according to the situation some have to be hidden and some shown (depending on the client configuration).

The problem here is that you can access the GridView.Columns[] collection using only an integer indexer, the implementation I found just stored in the database a list of the integer corresponding to the columns to hide, and use a for cycle to do the job:

For Each id As Integer In indexes
    gv.Columns(id).Visible = False
Next

It works, but it have a major drawback...if you add or remove columns from the control you have to update the index list...and if the application stores the information for each customer or user...that goes for all of them too.

The solution is simple, we just want to add an ID string field to the BoundField, TemplateField, etc...that a GridView uses to define its columns, to do the job we can inherit from those classes and add the required behaviour. We do not have multiple inheritance, so we are forced to use an interface to give a common aspect to all our inherited classes.

public interface IExtendedBoundField
{
   string Id { get; set; }
}
 
public class ExtendedBoundField : BoundField, IExtendedBoundField
{
   public string Id { get; set; }
}
 
public class ExtendedTemplateField : TemplateField, IExtendedBoundField
{
   public string Id { get; set; }
}

The Asp.NET code that uses this new fields will be like:

<asp:GridView ID="GridView1" runat="server" AllowPaging="True" AutoGenerateColumns="False"
   PageSize="30" DataKeyNames="cvcr_id" meta:resourcekey="GridView1Resource1" AllowSorting="True">
   <Columns>
      <avc:ExtendedBoundField Id="cognome" HeaderText="Cognome" DataField="cvcr_surname" SortExpression="cvcr_surname"
         meta:resourcekey="BoundFieldResource2" />
      <avc:ExtendedBoundField Id="nome" DataField="cvcr_name" SortExpression="cvcr_name" HeaderText="Nome"
         meta:resourcekey="BoundFieldResource3" />
      <avc:ExtendedTemplateField Id="eta" HeaderText="Et&#224;" SortExpression="age">
         <ItemTemplate>
            <asp:Label ID="lblAge" runat="server" Text='<%# Databinder.Eval(Container.DataItem, "age") %>'
               ToolTip='<%# Databinder.Eval(Container.DataItem,"cvcr_birthdate", "{0:dd/MM/yyyy}") %>'></asp:Label>
         </ItemTemplate>
      </avc:ExtendedTemplateField>
...

In the end you need to write an extension method that allows you to access the DataControlField given the Id

public static class GridViewExtensions
{
   public static DataControlField GetColumn(this GridView grid, string id)
   {
      foreach (var column in grid.Columns)
         if (column is IExtendedBoundField)
            if (((IExtendedBoundField) column).Id == id)
               return (DataControlField) column;
      return null;
   }
}

You are now able to show and hide columns (or do whatever you want with them) given you know the Id used to tag the column, this way we can save the Ids instead of the ordinal position of the column and the problem of the first implementation is gone.

Related Content