#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); }