Utilizing Generics to make a Class structure more mutable…

While the ASP.NET GridView control supports automatic paging I found it faster to use custom paging in several situations.  I found myself rewriting the same code over and over just to add the basic sorting capabilities to an ASP.NET GridView object.  So today I took just a little bit of time to encapsulate it all into a Class I can use and reuse on any page with a GridView.  In fact, it will probably take longer to write this blog entry than it took to encapsulate the functionality.

This set of code is the new class:

   1:  /// <summary>
   2:  /// Binds a List&lt;T&gt; data source to an ASP.NET GridView object.
   3:  /// </summary>
   4:  /// <typeparam name="T">Typically a DTO or POCO object.</typeparam>
   5:  internal class SortingGridView<T>
   6:  {
   7:      // http://stackoverflow.com/questions/3945645/sorting-gridview-with-entity-framework - almost there...
   8:      // http://stackoverflow.com/questions/722868/sorting-a-list-using-lambda-linq-to-objects - .AsQueryable()...
   9:   
  10:      #region Private Declarations
  11:      private StateBag _viewState = new StateBag(true);
  12:      #endregion
  13:   
  14:      #region Internal Properties
  15:      internal int TakeIndex { get { return _viewState["takeIndex"].ToString().ToInt32(); } set { _viewState["takeIndex"] = value.ToString(); } }
  16:      internal int SkipIndex { get { return _viewState["skipIndex"].ToString().ToInt32(); } set { _viewState["skipIndex"] = value.ToString(); } }
  17:      internal int TotalIndex { get { return _viewState["totalIndex"].ToString().ToInt32(); } set { _viewState["totalIndex"] = value.ToString(); } }
  18:      internal string SortingField { get { return _viewState["sortingField"].ToString(); } set { _viewState["sortingField"] = value.ToString(); } }
  19:      internal string SortingDirection { get { return _viewState["sortingDirection"].ToString(); } set { _viewState["sortingDirection"] = value.ToString(); } }
  20:      internal GridView SortedGridView { get; set; }
  21:      internal bool CanMovePrev { get; set; }
  22:      internal bool CanMoveNext { get; set; }
  23:      internal string NavigationText { get; set; }
  24:      internal List<T> SourceList { get; set; }
  25:      #endregion
  26:   
  27:      #region Constructor(s)
  28:      /// <summary>
  29:      /// Use this on all Post Backs
  30:      /// </summary>
  31:      /// <param name="viewState">ViewState from the page you are embedding this on</param>
  32:      /// <param name="GridView">The target GridView object</param>
  33:      /// <param name="SourceDataList">The List&lt;T&gt; Data Source to bind to the grid</param>
  34:      internal SortingGridView(StateBag viewState, GridView GridView, List<T> SourceDataList)
  35:      {
  36:          _viewState = viewState;
  37:          SortedGridView = GridView;
  38:          SourceList = SourceDataList;
  39:          BindGrid();
  40:      }
  41:      /// <summary>
  42:      /// Use this the first time to initialize all the ViewState paramaters.
  43:      /// </summary>
  44:      /// <param name="viewState">ViewState from the page you are embedding this on</param>
  45:      /// <param name="GridView">The target GridView object</param>
  46:      /// <param name="SourceDataList">The List&lt;T&gt; Data Source to bind to the grid</param>
  47:      /// <param name="Take">Default value for LINQ's .Take()</param>
  48:      /// <param name="Skip">Default value for LINQ's .Skip()</param>
  49:      /// <param name="Total">Default value for a total count of List&lt;T&gt; items</param>
  50:      /// <param name="Field">Default sorting column</param>
  51:      /// <param name="Direction">Default sorting order ("" for Ascending or "DESC" for Descending)</param>
  52:      internal SortingGridView(StateBag viewState, GridView GridView, List<T> SourceDataList, int Take, int Skip, int Total, string Field, string Direction)
  53:      {
  54:          _viewState = viewState;
  55:          TakeIndex = Take;
  56:          SkipIndex = Skip;
  57:          TotalIndex = Total;
  58:          SortingField = Field;
  59:          SortingDirection = Direction;
  60:          SortedGridView = GridView;
  61:          SourceList = SourceDataList;
  62:          BindGrid();
  63:      }
  64:      #endregion
  65:   
  66:      #region Internal Methods
  67:      internal void DoSorting(string SortExpression)
  68:      {
  69:          if (SortExpression.Equals(SortingField, StringComparison.CurrentCultureIgnoreCase))
  70:          {
  71:              if (SortingDirection.Equals("DESC", StringComparison.CurrentCultureIgnoreCase))
  72:                  SortingDirection = "";
  73:              else
  74:                  SortingDirection = "DESC";
  75:          }
  76:          else
  77:          {
  78:              SortingDirection = "";
  79:              SortingField = SortExpression;
  80:          }
  81:          BindGrid();
  82:      }
  83:      internal void GetFirst()
  84:      {
  85:          SkipIndex = 0;
  86:          BindGrid();
  87:      }
  88:      internal void GetPrevious()
  89:      {
  90:          if (SkipIndex > 0) SkipIndex--;
  91:          BindGrid();
  92:      }
  93:      internal void GetNext()
  94:      {
  95:          int lastIndex = (int)Math.Floor((decimal)TotalIndex / (decimal)TakeIndex);
  96:          if (SkipIndex < lastIndex) SkipIndex++;
  97:          BindGrid();
  98:      }
  99:      internal void GetLast()
 100:      {
 101:          int lastIndex = (int)Math.Floor((decimal)TotalIndex / (decimal)TakeIndex);
 102:          SkipIndex = lastIndex;
 103:          BindGrid();
 104:      }
 105:      #endregion
 106:   
 107:      #region Private Methods
 108:      private void BindGrid()
 109:      {
 110:          bool canMovePrev = true;
 111:          bool canMoveNext = true;
 112:          int start = (SkipIndex * TakeIndex) + 1;
 113:          int end = (SkipIndex * TakeIndex) + TakeIndex;
 114:   
 115:          RivWorks.DTO.FeedClientList feedClientList = RivWorks.Controller.FeedInfo.GetClients();
 116:   
 117:          //TotalIndex = feedClientList.ClientList.Count;
 118:          TotalIndex = SourceList.Count;
 119:   
 120:          if (SkipIndex <= 0)
 121:          {
 122:              canMovePrev = false;
 123:              canMoveNext = true;
 124:          }
 125:          else if (SkipIndex >= (int)Math.Floor((decimal)TotalIndex / (decimal)TakeIndex))
 126:          {
 127:              canMovePrev = true;
 128:              canMoveNext = false;
 129:          }
 130:          else
 131:          {
 132:              canMovePrev = true;
 133:              canMoveNext = true;
 134:          }
 135:          this.CanMovePrev = canMovePrev;
 136:          this.CanMoveNext = canMoveNext;
 137:   
 138:          this.NavigationText = string.Format("Records {0}-{1} of {2}.", start, end, TotalIndex);
 139:   
 140:          string sortExpression = SortingField + " " + SortingDirection;
 141:          SortedGridView.DataSource = SourceList.AsQueryable().SortBy(sortExpression.Trim()).Skip(SkipIndex * TakeIndex).Take(TakeIndex).ToList();
 142:          SortedGridView.DataBind();
 143:      }
 144:      #endregion
 145:  }

Notice on line 5 that I defined this class to accept a Generic Object.  It is important to note here that it is the object and not a List<T> generic list!  You can see in both Constructors that we can now pass in a List<T> generic object.  I could just as easily pass in any other Generic<T> object if I wanted to define them in the Constructor or exposed method. 

By defining the class this way I do not need to define the DTO/POCO object to use beforehand.  This is what makes reuse in .NET very nice! 

The following code example is for a web page’s code behind using the new class:

   1:  public partial class Default : System.Web.UI.Page
   2:  {
   3:      #region Internal Properties
   4:      internal SortingGridView<DTO.Feed.Client> SortingMainGrid;
   5:      #endregion
   6:   
   7:      #region Event Handlers
   8:      protected void Page_Load(object sender, EventArgs e)
   9:      {
  10:          DTO.FeedClientList fcl = new DTO.FeedClientList();
  11:   
  12:          if (!IsPostBack)
  13:              SortingMainGrid = new SortingGridView<DTO.Feed.Client>(this.ViewState, ClientGrid, fcl.ClientList, 15, 0, 0, "ClientID", "");
  14:          else
  15:              SortingMainGrid = new SortingGridView<DTO.Feed.Client>(this.ViewState, ClientGrid, fcl.ClientList);
  16:              
  17:          UpdatePager();
  18:      }
  19:      protected void ClientGrid_Sorting(object sender, GridViewSortEventArgs e)
  20:      {
  21:          SortingMainGrid.DoSorting(e.SortExpression);
  22:      }
  23:      protected void getFirst_Click(object sender, EventArgs e)
  24:      {
  25:          SortingMainGrid.GetFirst();
  26:          UpdatePager();
  27:      }
  28:      protected void getPrv_Click(object sender, EventArgs e)
  29:      {
  30:          SortingMainGrid.GetPrevious();
  31:          UpdatePager();
  32:      }
  33:      protected void getNext_Click(object sender, EventArgs e)
  34:      {
  35:          SortingMainGrid.GetNext();
  36:          UpdatePager();
  37:      }
  38:      protected void getLast_Click(object sender, EventArgs e)
  39:      {
  40:          SortingMainGrid.GetLast();
  41:          UpdatePager();
  42:      }
  43:      #endregion
  44:   
  45:      #region Private Methods
  46:      private void UpdatePager()
  47:      {
  48:          getFirst.Enabled = SortingMainGrid.CanMovePrev;
  49:          getPrev.Enabled = SortingMainGrid.CanMovePrev;
  50:          getNext.Enabled = SortingMainGrid.CanMoveNext;
  51:          getLast.Enabled = SortingMainGrid.CanMoveNext;
  52:          navIndicator.Text = SortingMainGrid.NavigationText;
  53:      }
  54:      #endregion
  55:  }

Note:  Since I did not play around too much in the HttpContext with this, every time I call into the page I am passing in the ViewState, Target GridView and a List<T> data source. 

Still things left to do.  I am probably going to move this into a User Control with the paging controls so it is entirely encapsulated.

While I know most everyone now knows how to do this I wanted to document what I am doing.  :)

1 Comment

  • Fine post to test imperceptable men and women.
    But I am additionally looking it intended for Skype hey, today bing has patched brand new identify approach additionally we will need to hang on extra time intended for i am imperceptable to operate flawlessly: )

Comments have been disabled for this content.