File: UI\WebControls\DataPager.cs
Project: ndp\fx\src\xsp\system\Extensions\System.Web.Extensions.csproj (System.Web.Extensions)
//------------------------------------------------------------------------------
// <copyright file="DataPager.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
 
using System;
using System.ComponentModel;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using System.Drawing.Design;
using System.Globalization;
using System.Text;
using System.Web;
using System.Web.Resources;
using System.Web.UI;
 
namespace System.Web.UI.WebControls {
    [
    ParseChildren(true),
    PersistChildren(false),
    Themeable(true),
    SupportsEventValidation,
    Designer("System.Web.UI.Design.WebControls.DataPagerDesigner, " + AssemblyRef.SystemWebExtensionsDesign),
    ToolboxBitmap(typeof(DataPager), "DataPager.bmp")
    ]
    public class DataPager : Control, IAttributeAccessor, INamingContainer, ICompositeControlDesignerAccessor {
        private readonly new IPage _page;
        private DataPagerFieldCollection _fields;
        private IPageableItemContainer _pageableItemContainer;
        private int _startRowIndex = 0;
        private int _maximumRows = 10;
        private int _totalRowCount;
        private bool _setPageProperties = false;
        private bool _initialized = false;
        private AttributeCollection _attributes;
        private bool _creatingPagerFields;
        private bool _queryStringHandled;
        private string _queryStringNavigateUrl;
 
        public DataPager() {
        }
 
        internal DataPager(IPage page) {
            _page = page;
        }
 
        /// <devdoc>
        ///    <para>Gets the collection of attribute name/value pairs expressed on the list item
        ///       control but not supported by the control's strongly typed properties.</para>
        /// </devdoc>
        [
        Browsable(false),
        DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
        ]
        public AttributeCollection Attributes {
            get {
                if (_attributes == null)
                    _attributes = new AttributeCollection(new StateBag(true));
 
                return _attributes;
            }
        }
 
        /// <devdoc>
        /// Ensure that the child controls have been created before returning the controls collection
        /// </devdoc>
        public override ControlCollection Controls {
            get {
                EnsureChildControls();
                return base.Controls;
            }
        }
 
        [
        DefaultValue(null),
        Editor("System.Web.UI.Design.WebControls.DataPagerFieldTypeEditor, " + AssemblyRef.SystemWebExtensionsDesign, typeof(UITypeEditor)),
        MergableProperty(false),
        PersistenceMode(PersistenceMode.InnerProperty),
        Category("Default"),
        ResourceDescription("DataPager_Fields"),
        ]
        public virtual DataPagerFieldCollection Fields {
            get {
                if (_fields == null) {
                    _fields = new DataPagerFieldCollection(this);
                    if (IsTrackingViewState) {
                        ((IStateManager)_fields).TrackViewState();
                    }
                    _fields.FieldsChanged += new EventHandler(OnFieldsChanged);
                }
                return _fields;
            }
        }
 
        [
        Browsable(false),
        DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
        ]
        public int MaximumRows {
            get {
                return _maximumRows;
            }
        }
 
        [
        DefaultValue(""),
        IDReferenceProperty(typeof(IPageableItemContainer)),
        WebCategory("Paging"),
        ResourceDescription("DataPager_PagedControlID"),
        SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "ID"),
        Themeable(false),
        ]
        public virtual string PagedControlID {
            get {
                object o = ViewState["PagedControlID"];
                if (o != null) {
                    return (string)o;
                }
                return String.Empty;
            }
            set {
                ViewState["PagedControlID"] = value;
            }
        }
 
        internal IPage IPage {
            get {
                if (_page != null) {
                    return _page;
                }
                else {
                    Page page = Page;
                    if (page == null) {
                        throw new InvalidOperationException(AtlasWeb.Common_PageCannotBeNull);
                    }
                    return new PageWrapper(page);
                }
            }
        }
 
        [
        DefaultValue(10),
        WebCategory("Paging"),
        ResourceDescription("DataPager_PageSize"),
        ]
        public int PageSize {
            get {
                return _maximumRows;
            }
            set {
                if (value < 1) {
                    throw new ArgumentOutOfRangeException("value");
                }
                if (value != _maximumRows) {
                    _maximumRows = value;
                    if (_initialized) {
                        CreatePagerFields();
                        SetPageProperties(_startRowIndex, _maximumRows, true);
                    }
                }
            }
        }
 
        [
        WebCategory("Paging"),
        DefaultValue(""),
        ResourceDescription("DataPager_QueryStringField")
        ]
        public string QueryStringField {
            get {
                object o = ViewState["QueryStringField"];
                if (o != null) {
                    return (string)o;
                }
                return String.Empty;
            }
            set {
                ViewState["QueryStringField"] = value;
            }
        }
 
        internal bool QueryStringHandled {
            get {
                return _queryStringHandled;
            }
            set {
                _queryStringHandled = value;
            }
        }
 
        internal string QueryStringValue {
            get {
                if (DesignMode) {
                    return String.Empty;
                }
                return IPage.Request.QueryString[QueryStringField];
            }
        }
 
        [
        Browsable(false),
        DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
        ]
        public int StartRowIndex {
            get {
                return _startRowIndex;
            }
        }
 
        [
        Browsable(false),
        DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
        ]
        protected virtual HtmlTextWriterTag TagKey {
            get {
                return HtmlTextWriterTag.Span;
            }
        }
 
        [
        Browsable(false),
        DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
        ]
        public int TotalRowCount {
            get {
                return _totalRowCount;
            }
        }
 
        protected virtual void AddAttributesToRender(HtmlTextWriter writer) {
            if (this.ID != null) {
                writer.AddAttribute(HtmlTextWriterAttribute.Id, ClientID);
            }
 
            // add expando attributes
            if (_attributes != null) {
                AttributeCollection atrColl = Attributes;
                IEnumerator keys = atrColl.Keys.GetEnumerator();
                while (keys.MoveNext()) {
                    string attrName = (string)(keys.Current);
                    writer.AddAttribute(attrName, atrColl[attrName]);
                }
            }
        }
 
        protected virtual void ConnectToEvents(IPageableItemContainer container) {
            if (container == null) {
                throw new ArgumentNullException("container");
            }
 
            // 
            _pageableItemContainer.TotalRowCountAvailable += new EventHandler<PageEventArgs>(OnTotalRowCountAvailable);
        }
 
        protected virtual void CreatePagerFields() {
            _creatingPagerFields = true;
            Controls.Clear();
            if (_fields != null) {
                foreach (DataPagerField field in _fields) {
                    DataPagerFieldItem fieldItem = new DataPagerFieldItem(field, this);
                    Controls.Add(fieldItem);
                    if (field.Visible) {
                        field.CreateDataPagers(fieldItem, _startRowIndex, _maximumRows, _totalRowCount, _fields.IndexOf(field));
                        fieldItem.DataBind();
                    }
                }
            }
            _creatingPagerFields = false;
        }
 
        /// <devdoc>
        /// Perform our own databinding, then perform our child controls' databinding.
        /// Does not call Base.DataBind(), since we need to call EnsureChildControls() between
        /// OnDataBinding() and DataBindChildren().
        /// </devdoc>
        public override void DataBind() {
            OnDataBinding(EventArgs.Empty);
 
            EnsureChildControls();
 
            DataBindChildren();
        }
 
        protected virtual IPageableItemContainer FindPageableItemContainer() {
            // the PagedControlID can be specified for finding a control within the same naming container
            // when the pager control isn't inside the IPageableItemContainter.
            if (!String.IsNullOrEmpty(PagedControlID)) {
                // The IPageableItemContainer is found by FindControl if specified.
                Control control = DataBoundControlHelper.FindControl(this, PagedControlID);
                if (control == null) {
                    throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, AtlasWeb.DataPager_PageableItemContainerNotFound, PagedControlID));
                }
                IPageableItemContainer container = control as IPageableItemContainer;
                if (container == null) {
                    throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, AtlasWeb.DataPager_ControlIsntPageable, PagedControlID));
                }
                return container;
            }
            else {
                // Look to see if parent container is IPageableItemContainer
                Control currentContainer = this.NamingContainer;
                IPageableItemContainer foundContainer = null;
 
                while (foundContainer == null && currentContainer != this.Page) {
                    if (currentContainer == null) {
                        throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, AtlasWeb.DataPager_NoNamingContainer, ID));
                    }
                    foundContainer = currentContainer as IPageableItemContainer;
                    currentContainer = currentContainer.NamingContainer;
                }
 
                return foundContainer;
            }
        }
 
        internal string GetQueryStringNavigateUrl(int pageNumber) {
            if (_queryStringNavigateUrl == null) {
                string queryStringField = QueryStringField;
                StringBuilder sb = new StringBuilder();
                if (DesignMode) {
                    sb.Append("?");
                }
                else {
                    bool methodGet = (IPage.Form != null) &&
                        IPage.Form.Method.Equals("GET", StringComparison.OrdinalIgnoreCase);
                    HttpRequestBase request = IPage.Request;
                    sb.Append(request.Path);
                    sb.Append("?");
                    foreach (string field in request.QueryString.AllKeys) {
                        //skip null/empty query string fields
                        if (String.IsNullOrEmpty(field)) {
                            continue;
                        }
                        // exclude query string postback data.
                        if (methodGet && ControlUtil.IsBuiltInHiddenField(field)) {
                            continue;
                        }
                        // append all query string fields except the current page.                        
                        if (!field.Equals(queryStringField, StringComparison.OrdinalIgnoreCase)) {
                            sb.Append(HttpUtility.UrlEncode(field));
                            sb.Append("=");
                            sb.Append(HttpUtility.UrlEncode(request.QueryString[field]));
                            sb.Append("&");
                        }
                    }
                }
                sb.Append(queryStringField);
                sb.Append("=");
                _queryStringNavigateUrl = sb.ToString();
            }
 
            return _queryStringNavigateUrl + pageNumber.ToString(CultureInfo.InvariantCulture);
        }
 
        /// <devdoc>
        /// <para>Loads the control state for those properties that should persist across postbacks
        ///   even when EnableViewState=false.</para>
        /// </devdoc>
        protected internal override void LoadControlState(object savedState) {
            // Any properties that could have been set in the persistance need to be
            // restored to their defaults if they're not in ControlState, or they will
            // be restored to their persisted state instead of their empty state.
            _startRowIndex = 0;
            _maximumRows = 10;
            _totalRowCount = -1;
            object[] state = savedState as object[];
 
            if (state != null) {
                base.LoadControlState(state[0]);
 
                if (state[1] != null) {
                    _startRowIndex = (int)state[1];
                }
 
                if (state[2] != null) {
                    _maximumRows = (int)state[2];
                }
 
                if (state[3] != null) {
                    _totalRowCount = (int)state[3];
                }
            }
            else {
                base.LoadControlState(null);
            }
 
            if (_pageableItemContainer == null) {
                _pageableItemContainer = FindPageableItemContainer();
                if (_pageableItemContainer == null) {
                    throw new InvalidOperationException(AtlasWeb.DataPager_NoPageableItemContainer);
                }
                ConnectToEvents(_pageableItemContainer);
            }
 
            _pageableItemContainer.SetPageProperties(_startRowIndex, _maximumRows, false);
            _setPageProperties = true;
        }
 
        bool HasAttributes {
            get {
                return (_attributes != null && _attributes.Count > 0);
            }
        }
 
        protected override void LoadViewState(object savedState) {
            if (savedState == null)
                return;
 
            object[] state = (object[])savedState;
 
            base.LoadViewState(state[0]);
            if (state[1] != null) {
                ((IStateManager)Fields).LoadViewState(state[1]);
            }
        }
 
        [SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers", MessageId = "1#")]
        protected override bool OnBubbleEvent(object source, EventArgs e) {
            DataPagerFieldCommandEventArgs cea = e as DataPagerFieldCommandEventArgs;
            bool handled = false;
 
            if (cea != null) {
                DataPagerFieldItem item = cea.Item;
                if (item != null && item.PagerField != null) {
                    item.PagerField.HandleEvent(cea);
                    handled = true;
                }
            }
            return handled;
        }
 
        private void OnFieldsChanged(object source, EventArgs e) {
            // force the paged control to rebind to pick up the changes.
            if (_initialized) {
                SetPageProperties(_startRowIndex, _maximumRows, true);
            }
        }
 
        [SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers", MessageId = "0#")]
        protected internal override void OnInit(EventArgs e) {
            base.OnInit(e);
            // We can't try to find another control in the designer in Init.
            if (!DesignMode) {
                _pageableItemContainer = FindPageableItemContainer();
                if (_pageableItemContainer != null) {
                    ConnectToEvents(_pageableItemContainer);
 
                    if (!String.IsNullOrEmpty(QueryStringField)) {
                        _startRowIndex = GetStartRowIndexFromQueryString();
                    }
 
                    _pageableItemContainer.SetPageProperties(_startRowIndex, _maximumRows, false);
                    _setPageProperties = true;
                }
 
                if (Page != null) {
                    Page.RegisterRequiresControlState(this);
                }
            }
 
 
            _initialized = true;
        }
 
        private int GetStartRowIndexFromQueryString() {
            int startRowIndex = 0;
            int pageIndex = 0;
            if (Int32.TryParse(QueryStringValue, out pageIndex)) {                
                startRowIndex = (pageIndex - 1) * _maximumRows;
            }
            return startRowIndex;
        }
 
        [SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers", MessageId = "0#")]
        protected internal override void OnLoad(EventArgs e) {
            if (_pageableItemContainer == null) {
                _pageableItemContainer = FindPageableItemContainer();
            }
            if (_pageableItemContainer == null) {
                throw new InvalidOperationException(AtlasWeb.DataPager_NoPageableItemContainer);
            }
 
            // Page properties may not have been set in OnInit because the IPageableItemContainer 
            // could have been added in OnLoad
            if (!_setPageProperties) {
                ConnectToEvents(_pageableItemContainer);
                if (!String.IsNullOrEmpty(QueryStringField)) {
                    _startRowIndex = GetStartRowIndexFromQueryString();
                }
                _pageableItemContainer.SetPageProperties(_startRowIndex, _maximumRows, false);
                _setPageProperties = true;
            }
 
            base.OnLoad(e);
        }
 
        [SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers", MessageId = "1#")]
        protected virtual void OnTotalRowCountAvailable(object sender, PageEventArgs e) {
            _totalRowCount = e.TotalRowCount;
            _startRowIndex = e.StartRowIndex;
            _maximumRows = e.MaximumRows;
 
            if (_totalRowCount <= _startRowIndex && _totalRowCount > 0) {
                // The last item got deleted or the results set changed and has fewer items.  Move to the prior page.
                int newStartIndex = _startRowIndex - _maximumRows;
                if (newStartIndex < 0) {
                    newStartIndex = 0;
                }
 
                // If we can't just go back one page, go back to the first page.  Most likely it's a new
                // results set.
                if (newStartIndex >= _totalRowCount) {
                    newStartIndex = 0;
                }
 
                // Rebind the IPageableItemContainer with the corrected values
                _pageableItemContainer.SetPageProperties(newStartIndex, _maximumRows, true);
                return;
            }
 
            if (!_creatingPagerFields) {
                CreatePagerFields();
            }
        }
 
        protected virtual void RecreateChildControls() {
            ChildControlsCreated = false;
            EnsureChildControls();
        }
 
        protected internal override void Render(HtmlTextWriter writer) {
            if (DesignMode) {
                EnsureChildControls();
                // Call OnTotalRowCountAvailable at design time so we see the pager.
                OnTotalRowCountAvailable(null, new PageEventArgs(0, PageSize, 101));
            }
 
            RenderBeginTag(writer);
            RenderContents(writer);
            writer.RenderEndTag();
        }
 
        public virtual void RenderBeginTag(HtmlTextWriter writer) {
            AddAttributesToRender(writer);
            writer.RenderBeginTag(TagKey);
        }
 
        protected virtual void RenderContents(HtmlTextWriter writer) {
            base.Render(writer);
        }
 
        /// <devdoc>
        /// <para>Saves the control state for those properties that should persist across postbacks
        ///   even when EnableViewState=false.</para>
        /// </devdoc>
        protected internal override object SaveControlState() {
            object baseState = base.SaveControlState();
            if (baseState != null ||
                _startRowIndex != 0 ||
                _maximumRows != 10 ||
                _totalRowCount != -1) {
 
                object[] state = new object[4];
 
                state[0] = baseState;
                state[1] = (_startRowIndex == 0) ? null : (object)_startRowIndex;
                state[2] = (_maximumRows == 10) ? null : (object)_maximumRows;
                state[3] = (_totalRowCount == -1) ? null : (object)_totalRowCount;
 
                return state;
            }
            return null;
        }
 
        protected override object SaveViewState() {
            // call base
            object baseState = base.SaveViewState();
            object myState = (_fields != null) ? ((IStateManager)_fields).SaveViewState() : null;
            return new object[2] { baseState, myState };
        }
 
        [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "databind",
            Justification = "Cannot change to 'dataBind' as would break binary compatibility with legacy code.")]
        public virtual void SetPageProperties(int startRowIndex, int maximumRows, bool databind) {
            if (DesignMode) {
                return;
            }
 
            if (_pageableItemContainer == null) {
                throw new InvalidOperationException(AtlasWeb.DataPager_PagePropertiesCannotBeSet);
            }
            _startRowIndex = startRowIndex;
            _maximumRows = maximumRows;
            _pageableItemContainer.SetPageProperties(startRowIndex, maximumRows, databind);
        }
 
        protected override void TrackViewState() {
            base.TrackViewState();
            if (_fields != null) {
                ((IStateManager)_fields).TrackViewState();
            }
        }
 
        #region ICompositeControlDesignerAccessor implementation
        void ICompositeControlDesignerAccessor.RecreateChildControls() {
            RecreateChildControls();
        }
        #endregion
 
        #region IAttributeAccessor
        /// <internalonly/>
        /// <devdoc>
        /// Returns the attribute value of the list item control
        /// having the specified attribute name.
        /// </devdoc>
        [SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes",
                         Justification = "Equivalent functionality is provided by public Attributes property.")]
        string IAttributeAccessor.GetAttribute(string name) {
            return Attributes[name];
        }
 
 
        /// <internalonly/>
        /// <devdoc>
        /// <para>Sets an attribute of the list
        /// item control with the specified name and value.</para>
        /// </devdoc>
        [SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes",
                         Justification = "Equivalent functionality is provided by public Attributes property.")]
        void IAttributeAccessor.SetAttribute(string name, string value) {
            Attributes[name] = value;
        }
        #endregion IAttributeAccessor
    }
}