File: UI\MobileControls\ObjectList.cs
Project: ndp\fx\src\mit\System\Web\System.Web.Mobile.csproj (System.Web.Mobile)
//------------------------------------------------------------------------------
// <copyright file="ObjectList.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>                                                                
//------------------------------------------------------------------------------
 
using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing.Design;
using System.Globalization;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
using System.Security.Permissions;
 
namespace System.Web.UI.MobileControls
{
    /*
     * Object List class.
     *
     * Copyright (c) 2000 Microsoft Corporation
     */
    /// <include file='doc\ObjectList.uex' path='docs/doc[@for="ObjectList"]/*' />
    [
        ControlBuilderAttribute(typeof(ObjectListControlBuilder)),
        DefaultEvent("ItemCommand"),
        DefaultProperty("DataSource"),
        Designer(typeof(System.Web.UI.Design.MobileControls.ObjectListDesigner)),
        DesignerAdapter(typeof(System.Web.UI.Design.MobileControls.Adapters.DesignerObjectListAdapter)),
        Editor(typeof(System.Web.UI.Design.MobileControls.ObjectListComponentEditor), typeof(ComponentEditor)),
        ToolboxData("<{0}:ObjectList runat=\"server\" LabelStyle-StyleReference=\"title\" CommandStyle-StyleReference=\"subcommand\"></{0}:ObjectList>"),
        ToolboxItem("System.Web.UI.Design.WebControlToolboxItem, " + AssemblyRef.SystemDesign)
    ]
    [AspNetHostingPermission(SecurityAction.LinkDemand, Level=AspNetHostingPermissionLevel.Minimal)]
    [AspNetHostingPermission(SecurityAction.InheritanceDemand, Level=AspNetHostingPermissionLevel.Minimal)]
    [Obsolete("The System.Web.Mobile.dll assembly has been deprecated and should no longer be used. For information about how to develop ASP.NET mobile applications, see http://go.microsoft.com/fwlink/?LinkId=157231.")]
    public class ObjectList : PagedControl, INamingContainer, ITemplateable, IPostBackEventHandler
    {
        private static readonly Object EventItemDataBind = new Object();
        private static readonly Object EventItemCommand = new Object();
        private static readonly Object EventItemSelect = new Object();
        private static readonly Object EventShowItemCommands = new Object();
 
        private Object _dataSource;
        private IEnumerable _resolvedDataSource;
        private ObjectListFieldCollection _fields;
        private ObjectListFieldCollection _autoGeneratedFields;
        private ObjectListFieldCollection _allFields;
        private ObjectListItemCollection _items;
        private ObjectListCommandCollection _commands;
        private ObjectListCommandCollection _globalCommands;
        private int _labelFieldIndex = -1;
        private int[] _tableFieldIndices = null;
        private IEnumerator _storedEnumerator = null;
        private Object _firstDataItem = null;
        private bool _storedDataValid = false;
        private ObjectListViewMode _viewMode = ObjectListViewMode.List;
        private bool _ignoreFieldsItemsViewModeViewState = false;
        private Style _commandStyle = null;
        private Style _labelStyle = null;
        private const string _itemCountViewStateKey = "_!ItemCount";
        private int _selectedIndex = -1;
        private bool _selectedIndexDirty = false;
        private bool _loadingItems = false;
 
        /// <include file='doc\ObjectList.uex' path='docs/doc[@for="ObjectList.SelectMoreCommand"]/*' />
        public static String SelectMoreCommand
        {
            get
            {
                return "More";
            }
        }
 
        /// <include file='doc\ObjectList.uex' path='docs/doc[@for="ObjectList.DataSource"]/*' />
        [
            Bindable(true),
            DefaultValue(null),
            DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
            MobileCategory(SR.Category_Data),
            MobileSysDescription(SR.ObjectList_DataSource)
        ]
        public virtual Object DataSource 
        {
            get 
            {
                return _dataSource;
            }
            set 
            {
                _dataSource = value;
                // _allFields and _resolvedDataSource need to be recalculated.  
                _allFields = null;
                _resolvedDataSource = null;
            }
        }
 
        /// <include file='doc\ObjectList.uex' path='docs/doc[@for="ObjectList.DataMember"]/*' />
        [
            Bindable(false),
            DefaultValue(""),
            MobileCategory(SR.Category_Data),
            MobileSysDescription(SR.List_DataMember),
            TypeConverter(typeof(System.Web.UI.Design.MobileControls.Converters.DataMemberConverter))
        ]
        public virtual String DataMember
        {
            get 
            {
                String s = (String)ViewState["DataMember"];
                return s == null ? String.Empty : s;
            }
            set 
            {
                ViewState["DataMember"] = value;
            }
        }
 
        /// <include file='doc\ObjectList.uex' path='docs/doc[@for="ObjectList.Fields"]/*' />
        [
            Bindable(false),
            DefaultValue(null),
            Editor(typeof(System.Web.UI.Design.MobileControls.FieldCollectionEditor), typeof(UITypeEditor)),
            MergableProperty(false),
            MobileCategory(SR.Category_Data),
            MobileSysDescription(SR.ObjectList_Fields),
            PersistenceMode(PersistenceMode.InnerDefaultProperty)
        ]
        public virtual ObjectListFieldCollection Fields
        {
            get
            {
                if (_fields == null)
                {
                    _fields = new ObjectListFieldCollection(this);
                    if (IsTrackingViewState)
                    {
                        ((IStateManager)_fields).TrackViewState();
                    }
                }
                return _fields;
            }
        }
 
        /// <include file='doc\ObjectList.uex' path='docs/doc[@for="ObjectList.Items"]/*' />
        [
            Bindable(true),
            Browsable(false),
            DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
        ]
        public virtual ObjectListItemCollection Items
        {
            get
            {
                if (_items == null)
                {
                    _items = new ObjectListItemCollection(this);
                    if (IsTrackingViewState)
                    {
                        ((IStateManager)_items).TrackViewState();
                    }
                }
                return _items;
            }
        }
 
        /// <include file='doc\ObjectList.uex' path='docs/doc[@for="ObjectList.Commands"]/*' />
        [
            Bindable(false),
            Editor(typeof(System.Web.UI.Design.MobileControls.CommandCollectionEditor), typeof(UITypeEditor)),
            MergableProperty(false),
            MobileCategory(SR.Category_Data),
            MobileSysDescription(SR.ObjectList_Commands),
            PersistenceMode(PersistenceMode.InnerDefaultProperty)
        ]
        public virtual ObjectListCommandCollection Commands
        {
            get
            {
                if (_commands == null)
                {
                    _commands = new ObjectListCommandCollection();
                    if (IsTrackingViewState)
                    {
                        ((IStateManager)_commands).TrackViewState();
                    }
                }
                return _commands;
            }
        }
 
        /// <include file='doc\ObjectList.uex' path='docs/doc[@for="ObjectList.LabelField"]/*' />
        [
            DefaultValue(""),
            MobileCategory(SR.Category_Data),
            MobileSysDescription(SR.ObjectList_LabelField),
            TypeConverter(typeof(System.Web.UI.Design.MobileControls.Converters.DataFieldConverter))
        ]
        public String LabelField 
        {
            get 
            {
                String s = (String)ViewState["LabelField"];
                return (s != null) ? s : String.Empty;
            }
            set 
            {
                ViewState["LabelField"] = value;
                InvalidateDisplayFieldIndices();
            }
        }
 
        /// <include file='doc\ObjectList.uex' path='docs/doc[@for="ObjectList.DefaultCommand"]/*' />
        [
            Bindable(true),
            DefaultValue(""),
            MobileCategory(SR.Category_Data),
            MobileSysDescription(SR.ObjectList_DefaultCommand),
            TypeConverter(typeof(System.Web.UI.Design.MobileControls.Converters.DefaultCommandConverter))
        ]
        public String DefaultCommand
        {
            get 
            {
                String s = (String)ViewState["DefaultCommand"];
                return (s != null) ? s : String.Empty;
            }
            set 
            {
                ViewState["DefaultCommand"] = value;
            }
        }
 
        /// <include file='doc\ObjectList.uex' path='docs/doc[@for="ObjectList.TableFields"]/*' />
        [
            Bindable(true),
            DefaultValue(""),
            Editor(typeof(System.Web.UI.Design.MobileControls.TableFieldsEditor),
                typeof(UITypeEditor)),
            MobileCategory(SR.Category_Data),
            MobileSysDescription(SR.ObjectList_TableFields)
        ]
        public String TableFields 
        {
            get 
            {
                String s = (String)ViewState["TableFields"];
                return (s != null) ? s : String.Empty;
            }
            set 
            {
                ViewState["TableFields"] = value;
                InvalidateDisplayFieldIndices();
            }
        }
 
        /// <include file='doc\ObjectList.uex' path='docs/doc[@for="ObjectList.SelectedIndex"]/*' />
        [
            Bindable(false),
            Browsable(false),
            DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
        ]
        public int SelectedIndex
        {
            get 
            {
                return _selectedIndex;
            }
            set 
            {
                if (value == -1 && ViewMode != ObjectListViewMode.List)
                {
                    throw new Exception(
                        SR.GetString(SR.ObjectList_MustBeInListModeToClearSelectedIndex));
                }                
                int itemCount = ItemCount > 0 ? ItemCount : Items.Count;
                if (value < -1)
                {
                    throw new ArgumentOutOfRangeException("SelectedIndex",
                        SR.GetString(SR.ObjectList_SelectedIndexTooSmall, value));
                }
                else if (itemCount > 0 && value >= itemCount)
                {
                    throw new ArgumentOutOfRangeException("SelectedIndex",
                        SR.GetString(SR.ObjectList_SelectedIndexTooBig, value, itemCount));
                }
                // End of guard clauses.
 
                _selectedIndexDirty = (value != _selectedIndex) && IsTrackingViewState;
                DeselectItemIfLoaded(_selectedIndex);
                SelectItemIfLoaded(value);
                _selectedIndex = value;
            }
        }
 
        /// <include file='doc\ObjectList.uex' path='docs/doc[@for="ObjectList.Selection"]/*' />
        [
            Bindable(false),
            Browsable(false),
            DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
        ]
        public ObjectListItem Selection
        {
            get 
            {
                if (SelectedIndex == -1)
                {
                    return null;
                }
                EnsureItemLoaded (SelectedIndex);
                int selectedIndex = TranslateVirtualItemIndex(SelectedIndex);
                if (selectedIndex >= 0 && selectedIndex < Items.Count)
                {
                    Debug.Assert (Items[selectedIndex].Selected);
                    return Items[selectedIndex];
                }
                else
                {
                    return null;
                }
            }
        }
 
        /// <include file='doc\ObjectList.uex' path='docs/doc[@for="ObjectList.AutoGenerateFields"]/*' />
        [
            Bindable(false),
            Browsable(true),
            DefaultValue(true),
            MobileCategory(SR.Category_Behavior),
            MobileSysDescription(SR.ObjectList_AutoGenerateFields)
        ]
        public bool AutoGenerateFields
        {
            get
            {
                Object b = ViewState["AutoGenerateFields"];
                return (b != null) ? (bool)b : true;
            }
            set
            {
                if (value != AutoGenerateFields)
                {
                    ViewState["AutoGenerateFields"] = value;
                    _allFields = null;
                    _autoGeneratedFields = null;
                    _items = null;
                    InvalidateDisplayFieldIndices();
                }
            }
        }
 
        /// <include file='doc\ObjectList.uex' path='docs/doc[@for="ObjectList.AllFields"]/*' />
        [
            Bindable(false),
            Browsable(false),
            DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
        ]
        public IObjectListFieldCollection AllFields
        {
            get
            {
                if (_allFields == null)
                {
                    if (_autoGeneratedFields == null)
                    {
                        if (_fields == null)
                        {
                            return null;
                        }
                        else
                        {
                            _allFields = _fields;
                        }
                    }
                    else 
                    {
                        if (_fields == null)
                        {
                            _allFields = _autoGeneratedFields;
                        }
                        else 
                        {
                            int count = _fields.Count + _autoGeneratedFields.Count;
 
                            ArrayList list = new ArrayList(count);
 
                            foreach (ObjectListField field in _fields)
                            {
                                list.Add(field);
                            }
 
                            foreach (ObjectListField field in _autoGeneratedFields)
                            {
                                list.Add(field);
                            }
 
                            _allFields = new ObjectListFieldCollection(this, list);
                        }
                    }
                }
                return _allFields;
            }
        }
 
        /// <include file='doc\ObjectList.uex' path='docs/doc[@for="ObjectList.LabelFieldIndex"]/*' />
        [
            Bindable(false),
            Browsable(false),
            DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
        ]
        public int LabelFieldIndex
        {
            get
            {
                if (_labelFieldIndex == -1)
                {
                    CalculateDisplayFieldIndices();
                }
                return _labelFieldIndex;
            }
        }
 
        /// <include file='doc\ObjectList.uex' path='docs/doc[@for="ObjectList.Details"]/*' />
        [
            Bindable(false),
            Browsable(false),
            DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
        ]
        public Panel Details
        {
            get
            {
                EnsureChildControls();
                if (ViewMode != ObjectListViewMode.Details)
                {
                    throw new Exception(SR.GetString(SR.ObjectList_MustBeInDetailsModeToGetDetails));
                }
                return Selection;
            }
        }
 
        /// <include file='doc\ObjectList.uex' path='docs/doc[@for="ObjectList.TableFieldIndices"]/*' />
        [
            Bindable(false),
            Browsable(false),
            DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
        ]
        public int[] TableFieldIndices
        {
            get
            {
                if (_labelFieldIndex == -1)
                {
                    CalculateDisplayFieldIndices();
                }
                Debug.Assert(_tableFieldIndices != null, "_tableFieldIndices is null.");
                return _tableFieldIndices;
            }
        }
 
        /// <include file='doc\ObjectList.uex' path='docs/doc[@for="ObjectList.EnsureTemplatedUI"]/*' />
        public override void EnsureTemplatedUI()
        {
            EnsureChildControls();
        }
 
        /// <include file='doc\ObjectList.uex' path='docs/doc[@for="ObjectList.EnsureChildControls"]/*' />
        protected override void EnsureChildControls()
        {
            // if EnsureChildControls is called before items are created (e.g., at
            // LoadPrivateViewState), there will be no controls.  Need to build child
            // controls in this case, so we set ChildControlsCreated to false.
            ChildControlsCreated = ChildControlsCreated && Controls.Count > 0;
            base.EnsureChildControls();
        }
 
        private void CalculateDisplayFieldIndices()
        {
            String labelField = LabelField;
            if (labelField.Length > 0)
            {
                _labelFieldIndex = GetFieldIndex(labelField);
            }
            else
            {
                _labelFieldIndex = 0;
            }
 
            String tableFields = TableFields;
            int length = tableFields.Length;
 
            // Populate the _tableFieldIndices array.
            ArrayList tableFieldIndexList = new ArrayList();
            for (int pos = 0; pos < length; )
            {
                int nextSemicolon = tableFields.IndexOf(';', pos);
                String fieldName = (nextSemicolon == -1) ? 
                    tableFields.Substring(pos) :
                    tableFields.Substring(pos, nextSemicolon - pos);
                tableFieldIndexList.Add(GetFieldIndex(fieldName));
                pos = nextSemicolon == -1 ? length : nextSemicolon + 1;
            }
            _tableFieldIndices = (int[])tableFieldIndexList.ToArray(typeof(int));
        }
 
        internal void InvalidateDisplayFieldIndices()
        {
            _labelFieldIndex = -1;
            _tableFieldIndices = null;
        }
 
        private int GetFieldIndex(String field)
        {
            int index = AllFields.IndexOf(field);
            if (index == -1)
            {
                throw new ArgumentException(SR.GetString(
                                        SR.ObjectList_FieldNotFound, field));
            }
            return index;
        }
 
        /// <include file='doc\ObjectList.uex' path='docs/doc[@for="ObjectList.AddParsedSubObject"]/*' />
        protected override void AddParsedSubObject(Object obj)
        {
            if (!(obj is LiteralControl))
            {
                if (obj is ObjectListField)
                {
                    Fields.Add((ObjectListField)obj);
                } 
                else if (obj is ObjectListCommand)
                {
                    Commands.Add((ObjectListCommand)obj);
                }
                else
                {
                    base.AddParsedSubObject(obj);
                }
            }
        }
 
        private IEnumerable ResolvedDataSource
        {
            get
            {
                if (_resolvedDataSource == null)
                {
                    _resolvedDataSource = 
                        DataSourceHelper.GetResolvedDataSource(DataSource, DataMember);
                }
                return _resolvedDataSource;
            }
        }
 
        /// <include file='doc\ObjectList.uex' path='docs/doc[@for="ObjectList.CreateChildControls"]/*' />
        protected override void CreateChildControls() 
        {
            Controls.Clear();
            if (ViewState[_itemCountViewStateKey] != null)
            {
                CreateChildControls(false);
            }
        }
 
        private void CreateChildControls(bool doDataBind) 
        {
            if (IsTemplated)
            {
                Controls.Clear();
                CreateTemplatedUI(doDataBind);
                ChildControlsCreated = true;
            }
        }
 
        /// <include file='doc\ObjectList.uex' path='docs/doc[@for="ObjectList.DataBind"]/*' />
        public override void DataBind() 
        {
            // Do our own databinding
            OnDataBinding(EventArgs.Empty);
 
            // Contained items will be databound after they have been created,
            // so we don't want to walk the hierarchy here.
        }
        
        /// <include file='doc\ObjectList.uex' path='docs/doc[@for="ObjectList.OnDataBinding"]/*' />
        protected override void OnDataBinding(EventArgs e) 
        {
            base.OnDataBinding(e);
 
            if (ResolvedDataSource == null)
            {
                ClearItemsAndControls();
                return;
            }
 
            if (AutoGenerateFields)
            {
                CreateAutoGeneratedFields(ResolvedDataSource);
            }
 
            if ((AllFields == null || AllFields.Count == 0) 
                && MobilePage != null 
                && !MobilePage.DesignMode)
            {
                throw new Exception(
                    SR.GetString(SR.ObjectList_MustHaveOneOrMoreFields));
            }
            
            CreateItems(ResolvedDataSource);
            
            if (!_loadingItems)
            {
                // To clear the selected index, the ViewMode must be list.
                ViewMode = ObjectListViewMode.List;
                SelectedIndex = -1;    
            }
            else
            {
                SelectItemIfLoaded(SelectedIndex);
            }
            
            CreateChildControls(true);
        }
 
        /// <include file='doc\ObjectList.uex' path='docs/doc[@for="ObjectList.CreateTemplatedItemsList"]/*' />
        public void CreateTemplatedItemsList(bool doDataBind)
        {
            ObjectListItemCollection items = Items;
 
            ITemplate headerTemplate = GetTemplate(Constants.HeaderTemplateTag);
            ITemplate footerTemplate = GetTemplate(Constants.FooterTemplateTag);
            ITemplate itemTemplate = GetTemplate(Constants.ItemTemplateTag);
            ITemplate separatorTemplate = GetTemplate(Constants.SeparatorTemplateTag);
            ITemplate alternatingItemTemplate = GetTemplate(Constants.AlternatingItemTemplateTag);
            if (alternatingItemTemplate == null)
            {
                alternatingItemTemplate = itemTemplate;
            }
 
            CreateControlItem(MobileListItemType.HeaderItem, headerTemplate, doDataBind);
            for (int i = 0; i < items.Count; i++)
            {
                if (i > 0)
                {
                    CreateControlItem(MobileListItemType.SeparatorItem, 
                                      separatorTemplate, 
                                      doDataBind);
                }
                AddItemAsControl(i, items[i], 
                                   ((i & 1) == 1) ? alternatingItemTemplate : itemTemplate, 
                                   doDataBind);
            }
            CreateControlItem(MobileListItemType.FooterItem, footerTemplate, doDataBind);
        }
 
        private void AddItemAsControl(
            int itemIndex,
            MobileListItem item,
            ITemplate itemTemplate, 
            bool doDataBind)
        {
            if (itemTemplate != null)
            {
                // No need to do it again, since CreateItems already does it.
                item.Controls.Clear();                
                item.ID = null;
                Controls.Add(item);
                CheckedInstantiateTemplate (itemTemplate, item, this);
                if (doDataBind)
                {
                    item.DataBind();
                }
            }
        }
 
        private void CreateControlItem(
            MobileListItemType itemType, 
            ITemplate itemTemplate, 
            bool doDataBind)
        {
            if (itemTemplate != null)
            {
                MobileListItem item = new MobileListItem(itemType);
                AddItemAsControl(-1, item, itemTemplate, doDataBind);
            }
        }
 
        /// <include file='doc\ObjectList.uex' path='docs/doc[@for="ObjectList.CreateTemplatedItemDetails"]/*' />
        public void CreateTemplatedItemDetails(bool doDataBind)
        {
            ITemplate detailsTemplate = GetTemplate(Constants.ItemDetailsTemplateTag);
            Selection.Controls.Clear();
 
            // Selection ID must be the same on each request.  AUI 8007.
            Selection.ID = "_ctl";
 
            if (detailsTemplate != null)
            {
                if (!Controls.Contains (Selection))
                {
                    Controls.Add (Selection);
                }
                CheckedInstantiateTemplate (detailsTemplate, Selection, this);
                if (doDataBind)
                {
                    Selection.DataBind();
                }
            }
        }
 
        /// <include file='doc\ObjectList.uex' path='docs/doc[@for="ObjectList.CreateItems"]/*' />
        protected virtual void CreateItems(IEnumerable dataSource) 
        {
            Debug.Assert (dataSource != null, "dataSource is null");
 
            Items.Clear();
 
            int count = 0;
            IEnumerator enumerator;
 
            if(_storedDataValid  && _firstDataItem != null)
            {
                enumerator = _storedEnumerator;
                ObjectListItem item = CreateItem(_firstDataItem);
                item.SetIndex(count + Items.BaseIndex);
                Items.Add(item);
                count++;
                _storedDataValid = false;
                _firstDataItem = null;
                _storedEnumerator = null;
            }
            else
            {
                enumerator = dataSource.GetEnumerator();
            }
 
            while(enumerator.MoveNext())
            {
                Object dataItem = enumerator.Current;
                ObjectListItem item = CreateItem(dataItem);
                item.SetIndex(count + Items.BaseIndex);
                Items.Add(item);
                count++;
            }
        }
 
        /// <include file='doc\ObjectList.uex' path='docs/doc[@for="ObjectList.CreateItem"]/*' />
        protected virtual ObjectListItem CreateItem(Object dataItem)
        {
            ObjectListItem item = new ObjectListItem(this, dataItem);
            
            // Set fields.
 
            IObjectListFieldCollection allFields = AllFields;
            int fieldIndex = 0;
            foreach (ObjectListField field in AllFields)
            {
                field.DataBindItem(fieldIndex, item);
                fieldIndex++;
            }
 
            // Use delegated data binding, if specified.
 
            if (dataItem != null)
            {
                OnItemDataBind(new ObjectListDataBindEventArgs(item, dataItem));
            }
 
            return item;
        }
 
        /// <include file='doc\ObjectList.uex' path='docs/doc[@for="ObjectList.OnPreRender"]/*' />
        protected override void OnPreRender(EventArgs e)
        {
            base.OnPreRender(e);
 
            if (IsTemplated)
            {
                int firstVisibleItem = FirstVisibleItemIndex;
                int pageSize = VisibleItemCount;
                int lastVisibleItem = firstVisibleItem + pageSize - 1;
                int itemIndex = 0;
                int separatorIndex = 0;
                foreach(Control ctl in Controls)
                {
                    MobileListItem item = ctl as MobileListItem;
                    if (item != null)
                    {
                        if (item.ItemType == MobileListItemType.ListItem)
                        {
                            item.Visible = itemIndex >= firstVisibleItem && itemIndex <= lastVisibleItem;
                            itemIndex++;
                        }
                        else if (item.ItemType == MobileListItemType.SeparatorItem)
                        {
                            item.Visible = separatorIndex >= firstVisibleItem && 
                                                separatorIndex < lastVisibleItem;
                            separatorIndex++;
                        }
                    }
                }
            }
        }
 
        /////////////////////////////////////////////////////////////////////////
        //  FIELD AUTO-GENERATION
        /////////////////////////////////////////////////////////////////////////
 
        /// <include file='doc\ObjectList.uex' path='docs/doc[@for="ObjectList.CreateAutoGeneratedFields"]/*' />
        protected void CreateAutoGeneratedFields(IEnumerable dataSource)
        {
            if (dataSource == null)
            {
                return;
            }
 
            ArrayList list = new ArrayList();
            PropertyDescriptorCollection propertyDescriptors = null;
 
            if (dataSource is ITypedList)
            {
                propertyDescriptors = 
                    ((ITypedList)dataSource).GetItemProperties(new PropertyDescriptor[0]);
            }
 
            if (propertyDescriptors == null)
            {
                IEnumerator enumerator = dataSource.GetEnumerator();
                if (enumerator.MoveNext())
                {
                    Object sampleItem = enumerator.Current;
                    StoreEnumerator(enumerator, sampleItem);
                    if (IsBindableType(sampleItem.GetType()))
                    {
                        list.Add(CreateAutoGeneratedField(
                                        SR.GetString(SR.ObjectList_ItemTitle), 
                                        null));
                    }
                    else
                    {
                        propertyDescriptors = TypeDescriptor.GetProperties(sampleItem);
                    }
                }
            }
 
            if (propertyDescriptors != null && propertyDescriptors.Count > 0)
            {
                foreach (PropertyDescriptor pd in propertyDescriptors)
                {
                    if (IsBindableType(pd.PropertyType))
                    {
                        String title;
                        ObjectListTitleAttribute attr = 
                            (ObjectListTitleAttribute)pd.Attributes[typeof(ObjectListTitleAttribute)];
                        if (attr != null)
                        {
                            title = attr.Title;
                        }
                        else
                        {
                            title = pd.Name;
                        }
                        list.Add(CreateAutoGeneratedField(title, pd.Name));
                    }
                }
            }
 
            _autoGeneratedFields = new ObjectListFieldCollection(this, list);
        }
 
        ///  Caches the fact that we have already consumed the first item from the enumeration
        ///  and must use it first during our item creation.
        internal void StoreEnumerator(IEnumerator enumerator, object firstDataItem) 
        {
            _storedEnumerator = enumerator;
            _firstDataItem = firstDataItem;
            _storedDataValid = true;
        }
 
        private bool IsBindableType(Type type)
        {
            return(type.IsPrimitive ||
                   (type == typeof(String)) ||
                   (type == typeof(DateTime)) ||
                   (type == typeof(Decimal)));
        }
 
        private ObjectListField CreateAutoGeneratedField(String title, String dataField)
        {
            ObjectListField field = new ObjectListField();
            ((IStateManager)field).TrackViewState();
            field.Title = title;
            if (dataField != null)
            {
                field.DataField = dataField;
            }
            else
            {
                field.SelfReference = true;
            }
            return field;
        }
 
        Object SaveAutoFieldsState()
        {
            int autoGeneratedCount = (_autoGeneratedFields != null) ? _autoGeneratedFields.Count : 0;
            if (autoGeneratedCount != 0)
            {
                Object[] fieldStates = new Object[autoGeneratedCount];
                for (int i = 0; i < autoGeneratedCount; i++)
                {
                    fieldStates[i] = ((IStateManager)_autoGeneratedFields[i]).SaveViewState();
                }
                return fieldStates;
            }
            else
            {
                return null;
            }
        }
 
        void LoadAutoFieldsState(Object state)
        {
            if (state != null)
            {
                Object[] fieldStates = (Object[])state;
                int count = fieldStates.Length;
                ArrayList list = new ArrayList(count);
                foreach (Object fieldState in fieldStates)
                {
                    ObjectListField field = new ObjectListField();
                    ((IStateManager)field).TrackViewState();
                    ((IStateManager)field).LoadViewState (fieldState);
                    list.Add(field);
                }
                _autoGeneratedFields = new ObjectListFieldCollection(this, list);
            }
        }
 
        internal void OnFieldChanged(bool fieldAddedOrRemoved)
        {
            // Called when a field is added, removed, etc.
            if (IsTrackingViewState)
            {
                _items = null;
                // avoid view state being out of sync with fields.
                _ignoreFieldsItemsViewModeViewState = true;
                if (fieldAddedOrRemoved)
                {
                    _allFields = null;
                }
                InvalidateDisplayFieldIndices();
            }
        }
 
        /// <include file='doc\ObjectList.uex' path='docs/doc[@for="ObjectList.InternalItemCount"]/*' />
        protected override int InternalItemCount
        {
            get
            {
                if (_items != null && Items.Count > 0)
                {
                    return Items.Count;
                }
                else
                {
                    return 0;
                }
            }
            
        }
 
        /////////////////////////////////////////////////////////////////////////
        //  EVENT HANDLING
        /////////////////////////////////////////////////////////////////////////
 
        /// <include file='doc\ObjectList.uex' path='docs/doc[@for="ObjectList.ItemDataBind"]/*' />
        [
            MobileCategory(SR.Category_Action),
            MobileSysDescription(SR.ObjectList_OnItemDataBind)
        ]
        public event ObjectListDataBindEventHandler ItemDataBind 
        {
            add
            {
                Events.AddHandler(EventItemDataBind, value);
            }
            remove 
            {
                Events.RemoveHandler(EventItemDataBind, value);
            }
        }
 
        /// <include file='doc\ObjectList.uex' path='docs/doc[@for="ObjectList.OnItemDataBind"]/*' />
        protected virtual void OnItemDataBind(ObjectListDataBindEventArgs e) 
        {
            ObjectListDataBindEventHandler onItemDataBindHandler = 
                (ObjectListDataBindEventHandler)Events[EventItemDataBind];
            if (onItemDataBindHandler != null)
            {
                onItemDataBindHandler(this, e);
            }
        }
 
        /// <include file='doc\ObjectList.uex' path='docs/doc[@for="ObjectList.ItemCommand"]/*' />
        [
            MobileCategory(SR.Category_Action),
            MobileSysDescription(SR.ObjectList_OnItemCommand)
        ]
        public event ObjectListCommandEventHandler ItemCommand 
        {
            add
            {
                Events.AddHandler(EventItemCommand, value);
            }
            remove 
            {
                Events.RemoveHandler(EventItemCommand, value);
            }
        }
 
        /// <include file='doc\ObjectList.uex' path='docs/doc[@for="ObjectList.OnItemCommand"]/*' />
        protected virtual void OnItemCommand(ObjectListCommandEventArgs e) 
        {
            ObjectListCommandEventHandler onItemCommandHandler = (ObjectListCommandEventHandler)Events[EventItemCommand];
            if (onItemCommandHandler != null)
            {
                onItemCommandHandler(this, e);
            }
        }
 
        /// <include file='doc\ObjectList.uex' path='docs/doc[@for="ObjectList.ItemSelect"]/*' />
        [
            MobileCategory(SR.Category_Action),
            MobileSysDescription(SR.ObjectList_OnItemSelect)
        ]
        public event ObjectListSelectEventHandler ItemSelect 
        {
            add
            {
                Events.AddHandler(EventItemSelect, value);
            }
            remove 
            {
                Events.RemoveHandler(EventItemSelect, value);
            }
        }
 
        /// <include file='doc\ObjectList.uex' path='docs/doc[@for="ObjectList.OnItemSelect"]/*' />
        protected virtual void OnItemSelect(ObjectListSelectEventArgs e) 
        {
            ObjectListSelectEventHandler onItemSelectHandler = (ObjectListSelectEventHandler)Events[EventItemSelect];
            if (onItemSelectHandler != null)
            {
                onItemSelectHandler(this, e);
            }
        }
 
        /// <include file='doc\ObjectList.uex' path='docs/doc[@for="ObjectList.HasItemCommandHandler"]/*' />
        [
            Bindable(false),
            Browsable(false),
            DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
        ]
        public bool HasItemCommandHandler
        {
            get
            {
                return Events[EventItemCommand] != null;
            }
        }
 
        /// <include file='doc\ObjectList.uex' path='docs/doc[@for="ObjectList.ShowItemCommands"]/*' />
        [
            MobileCategory(SR.Category_Action),
            MobileSysDescription(SR.ObjectList_OnShowItemCommands)
        ]
        public event ObjectListShowCommandsEventHandler ShowItemCommands 
        {
            add 
            {
                Events.AddHandler(EventShowItemCommands, value);
            }
            remove
            {
                Events.RemoveHandler(EventShowItemCommands, value);
            }
        }
 
        /// <include file='doc\ObjectList.uex' path='docs/doc[@for="ObjectList.OnShowItemCommands"]/*' />
        protected virtual void OnShowItemCommands(ObjectListShowCommandsEventArgs e) 
        {
            ObjectListShowCommandsEventHandler onShowItemCommandsHandler
                = (ObjectListShowCommandsEventHandler)Events[EventShowItemCommands];
            if (onShowItemCommandsHandler != null)
            {
                onShowItemCommandsHandler(this, e);
            }
        }
 
        /// <include file='doc\ObjectList.uex' path='docs/doc[@for="ObjectList.OnBubbleEvent"]/*' />
        protected override bool OnBubbleEvent(Object sender, EventArgs e) 
        {
            bool handled = false;
 
            if (e is CommandEventArgs)
            {
                if (e is ObjectListCommandEventArgs) 
                {
                    OnItemCommand((ObjectListCommandEventArgs)e);
                    handled = true;
                }
            }
 
            return handled;
        }
 
        /// <internalonly/>
        protected void RaisePostBackEvent(String eventArgument)
        {
            if (!Adapter.HandlePostBackEvent(eventArgument))
            {
                OnItemCommand(new ObjectListCommandEventArgs(Selection, eventArgument));
            }
        }
 
        /// <include file='doc\ObjectList.uex' path='docs/doc[@for="ObjectList.RaiseDefaultItemEvent"]/*' />
        public void RaiseDefaultItemEvent(int itemIndex)
        {
            EnsureItemLoaded (itemIndex);
            int itemCollIndex = TranslateVirtualItemIndex(itemIndex);
            SelectedIndex = itemIndex;
            OnItemCommand(new ObjectListCommandEventArgs(Items[itemCollIndex], DefaultCommand));
        }
 
        /// <include file='doc\ObjectList.uex' path='docs/doc[@for="ObjectList.SelectListItem"]/*' />
        public bool SelectListItem(int itemIndex, bool selectMore)
        {
            EnsureItemLoaded (itemIndex);
            int itemCollIndex = TranslateVirtualItemIndex(itemIndex);
 
            ObjectListSelectEventArgs args = new ObjectListSelectEventArgs(
                                                        Items[itemCollIndex], 
                                                        selectMore);
            SelectedIndex = itemIndex;
            OnItemSelect(args);
            return args.UseDefaultHandling;
        }
 
        /// <include file='doc\ObjectList.uex' path='docs/doc[@for="ObjectList.PreShowItemCommands"]/*' />
        public void PreShowItemCommands(int itemIndex)
        {
            // Called just before commands are shown for a given item. We call 
            // an event handler that can modify (or even replace) the commands collection,
            // but first we mark the collection, and save it off, so that we can 
            // save the state of the collection at this point.
 
            if (_commands != null)
            {
                _globalCommands = _commands;
                _commands.GlobalStateSet();
            }
 
            SelectedIndex = itemIndex;
 
            ObjectListShowCommandsEventArgs eventArgs = 
                new ObjectListShowCommandsEventArgs(Selection, Commands);
            OnShowItemCommands(eventArgs);
            if (eventArgs.Commands != _commands)
            {
                _commands = eventArgs.Commands;
            }
        }
 
        private void EnsureItemLoaded(int virtualIndex)
        {
            Debug.Assert (virtualIndex >= 0, "virtualIndex < 0");
            if (ItemCount > 0 &&
                    (_items == null || 
                    virtualIndex < Items.BaseIndex || 
                    Items.Count <= virtualIndex - Items.BaseIndex))
            {
                OnLoadItems(new LoadItemsEventArgs(virtualIndex, 1));
            }
        }
 
        private int TranslateVirtualItemIndex(int virtualIndex)
        {
            Debug.Assert(ItemCount > 0 || Items.BaseIndex == 0);
 
            // Translate virtual index to true index within collection.            
            // If custom pagination is off, virtual index is the true index.
            return virtualIndex - Items.BaseIndex;
        }
 
        /// <include file='doc\ObjectList.uex' path='docs/doc[@for="ObjectList.OnLoadItems"]/*' />
        protected override void OnLoadItems(LoadItemsEventArgs e)
        {
            // We should only load items if the base index has changed, or if
            // the desired items do not exist in the list. Otherwise, we are making
            // the app reload the same items over and over.
 
            if (e.ItemIndex != Items.BaseIndex || e.ItemCount != Items.Count)
            {
                _loadingItems = true;
                Items.BaseIndex = e.ItemIndex;
                Items.Clear();
                base.OnLoadItems(e);
                _loadingItems = false;
            }
        }
 
        /////////////////////////////////////////////////////////////////////////
        //  STATE MANAGEMENT
        /////////////////////////////////////////////////////////////////////////
 
        /// <include file='doc\ObjectList.uex' path='docs/doc[@for="ObjectList.TrackViewState"]/*' />
        protected override void TrackViewState()
        {
            base.TrackViewState();
            if (_fields != null)
            {
                ((IStateManager)_fields).TrackViewState();
            }
            if (_commands != null)
            {
                ((IStateManager)_commands).TrackViewState();
            }
            if (_items != null)
            {
                ((IStateManager)_items).TrackViewState();
            }
        }
 
        /// <include file='doc\ObjectList.uex' path='docs/doc[@for="ObjectList.SavePrivateViewState"]/*' />
        protected override Object SavePrivateViewState()
        {
            Object baseState = base.SavePrivateViewState();
            if (ViewMode != ObjectListViewMode.List || _selectedIndexDirty)
            {
                return new Triplet(baseState, (int) ViewMode, SelectedIndex);
            }
            else if (baseState != null)
            {
                return baseState;
            }
            return null;
        }
 
        /// <include file='doc\ObjectList.uex' path='docs/doc[@for="ObjectList.LoadPrivateViewState"]/*' />
        protected override void LoadPrivateViewState(Object state)
        {
            if (state != null)
            {
                Triplet stateTriplet = state as Triplet;
                if (stateTriplet != null)
                {
                    base.LoadPrivateViewState (stateTriplet.First);
                    ObjectListViewMode originalViewMode = _viewMode;
                    
                    // use private field because property get calls CreateChildControls.
                    _viewMode = (ObjectListViewMode) stateTriplet.Second;
                    
                    // use property to insure we load items if necessary and set selected index dirty.
                    SelectedIndex = (int)stateTriplet.Third;
                    
                    bool viewModeChanged = (originalViewMode != _viewMode);
                    if (ChildControlsCreated && viewModeChanged)
                    {
                        // This is before items loaded in LVS, so we only do this if
                        // child controls already created in init (e.g., no VS.)
                        CreateChildControls (true);
                    }
                }
                else
                {
                    base.LoadPrivateViewState(state);
                }
            }
        }
 
        /// <include file='doc\ObjectList.uex' path='docs/doc[@for="ObjectList.SaveViewState"]/*' />
        protected override Object SaveViewState() 
        {
            Object baseState, fieldsState, autoFieldsState, commandsState, itemsState;
        
            Debug.Assert (Items != null, "get_Items should initialize Items collection.");
            int count = Items.Count;
            // HasControls implies DataBound on this or a prev req (but not converse).
            if (count > 0 || HasControls()) 
            {
                ViewState[_itemCountViewStateKey] = count;
            }
            baseState = base.SaveViewState();
            fieldsState = (_fields != null) ? ((IStateManager)_fields).SaveViewState() : null;
            autoFieldsState = SaveAutoFieldsState();
            commandsState = (_commands != null) ? ((IStateManager)_commands).SaveViewState() : null;
            itemsState = (_items != null) ? ((IStateManager)_items).SaveViewState() : null;
 
            if (itemsState != null || 
                fieldsState != null || 
                autoFieldsState != null || 
                commandsState != null )
            {
                return new Object[5]
                { 
                    baseState, 
                    fieldsState, 
                    autoFieldsState, 
                    commandsState, 
                    itemsState
                };
            }
            else if (baseState != null)
            {
                return new Object[1] { baseState };
            }
            return null;
        }
 
        /// <include file='doc\ObjectList.uex' path='docs/doc[@for="ObjectList.LoadViewState"]/*' />
        protected override void LoadViewState(Object savedState) 
        {
            // copy locally (can change during Fields.LVS).
            bool ignoreFieldsItemsViewModeViewState = _ignoreFieldsItemsViewModeViewState;
            if (savedState != null) 
            {
                Object[] state = (Object[])savedState;
                base.LoadViewState(state[0]);
 
                if (state.Length > 1)
                {
                    // Always load items after loading fields, because field changes can wipe out
                    // items!
                    if(!ignoreFieldsItemsViewModeViewState)
                    {
                        ((IStateManager)Fields).LoadViewState(state[1]);
                        LoadAutoFieldsState(state[2]);
                    }
                    ((IStateManager)Commands).LoadViewState(state[3]);
                    if(!ignoreFieldsItemsViewModeViewState)
                    {
                        ((IStateManager)Items).LoadViewState(state[4]);
                        SelectItemIfLoaded(SelectedIndex);
                    }
                }
            }
        }
 
        /// <include file='doc\ObjectList.uex' path='docs/doc[@for="ObjectList.ViewMode"]/*' />
        [
            Bindable(false),
            Browsable(false),
            DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
        ]
        public ObjectListViewMode ViewMode
        {
            get
            {
                return _viewMode;
            }
            set
            {   
                if (SelectedIndex == -1 && value != ObjectListViewMode.List)
                {
                    throw new Exception(SR.GetString(
                        SR.ObjectList_CannotSetViewModeWithNoSelectedItem));
                }
                if (value == ObjectListViewMode.List)
                {
                    // Set pagination state changed to refresh pagination.
                    Form.PaginationStateChanged = true;
                }
                _viewMode = value;
                CreateChildControls(true);
            }
        }
 
        /// <include file='doc\ObjectList.uex' path='docs/doc[@for="ObjectList.DetailsCommandText"]/*' />
        [
            Bindable(true),
            Browsable(true),
            DefaultValue(""),
            MobileCategory(SR.Category_Appearance),
            MobileSysDescription(SR.ObjectList_DetailsCommandText),
        ]
        public String DetailsCommandText
        {
            get
            {
                String detailsCommandText = (String)ViewState["DetailsCommandText"];
                return detailsCommandText != null ? detailsCommandText : String.Empty;
            }
            set
            {
                ViewState["DetailsCommandText"] = value;
            }
        }
 
        /// <include file='doc\ObjectList.uex' path='docs/doc[@for="ObjectList.BackCommandText"]/*' />
        [
            Bindable(true),
            Browsable(true),
            DefaultValue(""),
            MobileCategory(SR.Category_Appearance),
            MobileSysDescription(SR.ObjectList_BackCommandText),
        ]
        public String BackCommandText
        {
            get
            {
                String backCommandText = (String) ViewState["BackCommandText"];
                return backCommandText != null ? backCommandText : String.Empty;
            }
            set
            {
                ViewState["BackCommandText"] = value;
            }
        }
 
        /// <include file='doc\ObjectList.uex' path='docs/doc[@for="ObjectList.MoreText"]/*' />
        [
            Bindable(true),
            Browsable(true),
            DefaultValue(""),
            MobileCategory(SR.Category_Appearance),
            MobileSysDescription(SR.ObjectList_MoreText)
        ]
        public String MoreText
        {
            get
            {
                String moreText = (String)ViewState["MoreText"];
                return moreText != null ? moreText : String.Empty;
            }
            set
            {
                ViewState["MoreText"] = value;
            }
        }
 
        /// <include file='doc\ObjectList.uex' path='docs/doc[@for="ObjectList.CommandStyle"]/*' />
        [
            DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
            MobileCategory(SR.Category_Style),
            MobileSysDescription(SR.ObjectList_CommandStyle),
            NotifyParentProperty(true)
        ]
        public Style CommandStyle
        {
            get
            {
                if (_commandStyle == null)
                {
                    _commandStyle = new Style();
                    _commandStyle.SetControl(this);
                    _commandStyle.StyleReference = "subcommand";
                }
                return _commandStyle;
            }
            set
            {
                _commandStyle = value;
            }
        }
 
        /// <include file='doc\ObjectList.uex' path='docs/doc[@for="ObjectList.LabelStyle"]/*' />
        [
            DefaultValue(null),
            DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
            MobileCategory(SR.Category_Style),
            MobileSysDescription(SR.ObjectList_LabelStyle),
            NotifyParentProperty(true)
        ]
        public Style LabelStyle
        {
            get
            {
                if (_labelStyle == null)
                {
                    _labelStyle = new Style();
                    _labelStyle.SetControl(this);
                    _labelStyle.StyleReference = "title";
                }
                return _labelStyle;
            }
            set
            {
                _labelStyle = value;
            }
        }
 
        internal override void InvalidateParentStyles()
        {
            if (_commandStyle != null)
            {
                _commandStyle.InvalidateParentStyle();
            }
            if (_labelStyle != null)
            {
                _labelStyle.InvalidateParentStyle();
            }
            base.InvalidateParentStyles();
        }
 
        internal override void InternalItemCountChangedHandler(int newItemCount)
        {
            if (newItemCount == 0)
            {
                ClearItemsAndControls();
            }
        }
 
        private void ClearItemsAndControls()
        {
            ViewMode = ObjectListViewMode.List;
            SelectedIndex = -1;
            Items.Clear();
            Controls.Clear();
        }
 
        // Wrapper to increase intelligibility at point of call.
        private void SelectItemIfLoaded(int virtualIndex)
        {
            SetItemSelectedPropertyIfItemLoaded(virtualIndex, true);
        }
 
        // Wrapper to increase intelligibility at point of call.
        private void DeselectItemIfLoaded(int virtualIndex)
        {
            SetItemSelectedPropertyIfItemLoaded(virtualIndex, false);
        }
 
        private void SetItemSelectedPropertyIfItemLoaded(int virtualIndex, bool newValue)
        {
            if (virtualIndex < 0)
            {
                return;
            }
            int physicalIndex = TranslateVirtualItemIndex(virtualIndex);
            if (physicalIndex >= 0 && physicalIndex < Items.Count)
            {
                Items[physicalIndex].Selected = newValue;
            }
        }
 
        #region IPostBackEventHandler implementation
        void IPostBackEventHandler.RaisePostBackEvent(String eventArgument) {
            RaisePostBackEvent(eventArgument);
        }
        #endregion 
    }
 
 
    /*
     * Control builder for object lists.
     *
     * Copyright (c) 2000 Microsoft Corporation
     */
 
    /// <include file='doc\ObjectList.uex' path='docs/doc[@for="ObjectListControlBuilder"]/*' />
    [AspNetHostingPermission(SecurityAction.LinkDemand, Level=AspNetHostingPermissionLevel.Minimal)]
    [AspNetHostingPermission(SecurityAction.InheritanceDemand, Level=AspNetHostingPermissionLevel.Minimal)]
    [Obsolete("The System.Web.Mobile.dll assembly has been deprecated and should no longer be used. For information about how to develop ASP.NET mobile applications, see http://go.microsoft.com/fwlink/?LinkId=157231.")]
    public class ObjectListControlBuilder : MobileControlBuilder
    {
        /// <include file='doc\ObjectList.uex' path='docs/doc[@for="ObjectListControlBuilder.GetChildControlType"]/*' />
        public override Type GetChildControlType(String tagName, IDictionary attributes) 
        {
            if (String.Compare(tagName, "Field", StringComparison.OrdinalIgnoreCase) == 0) 
            {
                return typeof(ObjectListField);
            }
            else if (String.Compare(tagName, "Command", StringComparison.OrdinalIgnoreCase) == 0) 
            {
                return typeof(ObjectListCommand);
            }
            else
            {
                return base.GetChildControlType(tagName, attributes);
            }
        }
    }
}