#region Licence
// Copyright (c) 2007 James Gregory (james@jagregory.com)
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// * Neither the name of James Gregory nor the names of its
// contributors may be used to endorse or promote products derived from this
// software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion
using System;
using System.Collections;
using System.ComponentModel;
using System.Web.UI.WebControls;
namespace JAGregory.Controls
{
///
/// A GridView derived control that pages using delegates for complete control over results.
///
///
/// Uses delegates to perform true server-side paging, so only the records from the database that
/// are needed for the current page are requested.
///
public class DeleGrid : GridView, IDeleGrid
{
private const string EventNotRaisedErrorMessage = "The DeleGrid '{0}' fired event {1} which wasn't handled.";
private readonly IPager pager;
private readonly ISorter sorter;
public event PageDataRequestEventHandler PageDataRequest;
public event TotalRecordCountRequestEventHandler TotalRecordCountRequest;
public DeleGrid()
{
pager = new DeleGridPager(this);
sorter = new DeleGridSorter(this);
}
public DeleGrid(IPager pager, ISorter sorter)
{
this.pager = pager;
this.sorter = sorter;
}
///
/// Requests the current page of data.
///
/// IEnumerable of requested data.
protected virtual IEnumerable OnPageDataRequest(DataRequestEventArgs e)
{
if (PageDataRequest != null)
return PageDataRequest(this, e);
else
throw new EventNotHandledException(string.Format(EventNotRaisedErrorMessage, ID, "PageDataRequest"));
}
///
/// Requests the total number of records in the complete data set.
///
/// Total record count.
protected virtual int OnTotalRecordCountRequest(DataRequestEventArgs e)
{
if (TotalRecordCountRequest != null)
return TotalRecordCountRequest(this, e);
else
throw new EventNotHandledException(string.Format(EventNotRaisedErrorMessage, ID, "TotalRecordCountRequest"));
}
///
/// Handles sorting of the grid.
///
/// Event arguments
protected override void OnSorting(GridViewSortEventArgs e)
{
sorter.Store(e.SortExpression);
DataBind();
}
///
/// Changes the current page index and re-binds the grid.
///
/// Event arguments
protected override void OnPageIndexChanging(GridViewPageEventArgs e)
{
PageIndex = e.NewPageIndex;
DataBind();
}
///
/// Requests the data from the user and binds it to the grid.
///
/// Event arguments
protected override void OnDataBinding(EventArgs e)
{
base.OnDataBinding(e);
DataSource = OnPageDataRequest(CreateEventArgs());
}
///
/// Initialises the pager row. Requests the total record count, then sets up the row's appearance.
///
/// Pager row
/// Column span of the primary pager cell
/// Data source bound to the grid
protected override void InitializePager(GridViewRow row, int columnSpan, PagedDataSource pagedDataSource)
{
pager.ConfigureDataSource(pagedDataSource, GetTotalRecordCount());
base.InitializePager(row, columnSpan, pagedDataSource);
}
///
/// Creates the EventArgs for the data request process.
///
/// Instance of RequestDataEventArgs.
private DataRequestEventArgs CreateEventArgs()
{
return new DataRequestEventArgs(PageIndex * PageSize, PageSize, SortingField, SortingDirection); ;
}
///
/// Gets the total number of records in the data set.
///
///
/// Only requests the total if it hasn't previously been requested.
///
///
private int GetTotalRecordCount()
{
if (TotalRecordCount <= 0 || AlwaysRequestTotal)
TotalRecordCount = OnTotalRecordCountRequest(CreateEventArgs());
return TotalRecordCount;
}
[DefaultValue(false)]
public bool AlwaysRequestTotal
{
get
{
bool? v = ViewState[ViewStateKeys.AlwaysRequestTotal] as bool?;
return v.HasValue ? v.Value : false;
}
set { ViewState[ViewStateKeys.AlwaysRequestTotal] = new bool?(value); }
}
///
/// Gets or sets the total number of records in the complete data set.
///
///
/// Stored in the ViewState so the query only needs to be done on initial binding.
/// Can be reset by the user to force a re-request of the total.
///
protected int TotalRecordCount
{
get
{
int? v = ViewState[ViewStateKeys.VirtualItemCount] as int?;
return v.HasValue ? v.Value : 0;
}
set { ViewState[ViewStateKeys.VirtualItemCount] = value; }
}
///
/// Gets or sets the field currently used for sorting.
///
///
/// Persisted in ViewState for binding use.
///
public string SortingField
{
get
{
string v = ViewState[ViewStateKeys.SortingField] as string;
return string.IsNullOrEmpty(v) ? string.Empty : v;
}
set { ViewState[ViewStateKeys.SortingField] = value; }
}
///
/// Gets or sets the sorting direction currently used for sorting.
///
///
/// Persisted in ViewState for binding use.
///
public SortDirection SortingDirection
{
get
{
SortDirection? v = ViewState[ViewStateKeys.SortingDirection] as SortDirection?;
return v.HasValue ? v.Value : SortDirection.Ascending;
}
set { ViewState[ViewStateKeys.SortingDirection] = value; }
}
///
/// Gets or sets the current page index for the grid.
///
public override int PageIndex
{
get
{
int? v = ViewState[ViewStateKeys.PageIndex] as int?;
return v.HasValue ? v.Value : 0;
}
set
{
ViewState[ViewStateKeys.PageIndex] = value;
base.PageIndex = value;
}
}
///
/// Constants for grid ViewState keys.
///
private class ViewStateKeys
{
public const string AlwaysRequestTotal = "AlwaysRequestTotal";
public const string PageIndex = "PageIndex";
public const string SortingDirection = "SortingDirection";
public const string SortingField = "SortingField";
public const string VirtualItemCount = "VirtualItemCount";
}
}
public delegate IEnumerable PageDataRequestEventHandler(object sender, DataRequestEventArgs e);
public delegate int TotalRecordCountRequestEventHandler(object sender, DataRequestEventArgs e);
}