File: UI\WebControls\GridViewColumnsGenerator.cs
Project: ndp\fx\src\xsp\system\Web\System.Web.csproj (System.Web)
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection;
using System.Web.Util;
 
namespace System.Web.UI.WebControls {
    public class GridViewColumnsGenerator : AutoFieldsGenerator {
 
        public override List<AutoGeneratedField> CreateAutoGeneratedFields(object dataObject, Control control) {
            if (!(control is GridView)) {
                throw new ArgumentException(SR.GetString(SR.InvalidDefaultAutoFieldGenerator, GetType().FullName, typeof(GridView).FullName));
            }
 
            Debug.Assert(dataObject == null || dataObject is PagedDataSource);
 
            PagedDataSource dataSource = dataObject as PagedDataSource;
            GridView gridView = control as GridView;
 
            if (dataSource == null) {
                // note that we're not throwing an exception in this case, and the calling
                // code should be able to handle a null arraylist being returned
                return null;
            }
 
            List<AutoGeneratedField> generatedFields = new List<AutoGeneratedField>();
            PropertyDescriptorCollection propDescs = null;
            bool throwException = true;
 
            // try ITypedList first
            // A PagedDataSource implements this, but returns null, if the underlying data source
            // does not implement it.
            propDescs = ((ITypedList)dataSource).GetItemProperties(new PropertyDescriptor[0]);
 
            if (propDescs == null) {
                Type sampleItemType = null;
                object sampleItem = null;
 
                IEnumerable realDataSource = dataSource.DataSource;
                Debug.Assert(realDataSource != null, "Must have a real data source when calling CreateAutoGeneratedColumns");
 
                Type dataSourceType = realDataSource.GetType();
 
                // try for a typed Row property, which should be present on strongly typed collections
                PropertyInfo itemProp = dataSourceType.GetProperty("Item", BindingFlags.Public | BindingFlags.Instance, null, null, new Type[] { typeof(int) }, null);
                if (itemProp != null) {
                    sampleItemType = itemProp.PropertyType;
                }
 
                if ((sampleItemType == null) || (sampleItemType == typeof(object))) {
                    // last resort... try to get ahold of the first item by beginning the
                    // enumeration
 
                    IEnumerator e = dataSource.GetEnumerator();
 
                    if (e.MoveNext()) {
                        sampleItem = e.Current;
                    }
                    else {
                        // we don't want to throw an exception if we're bound to an IEnumerable
                        // data source with no records... we'll simply bail and not show any data
                        throwException = false;
                    }
                    if (sampleItem != null) {
                        sampleItemType = sampleItem.GetType();
                    }
 
                    // We must store the enumerator regardless of whether we got back an item from it
                    // because we cannot start the enumeration again, in the case of a DataReader.
                    // Code in CreateChildControls must deal appropriately for the case where
                    // there is a stored enumerator, but a null object as the first item.
                    gridView.StoreEnumerator(e, sampleItem);
                }
 
                if ((sampleItem != null) && (sampleItem is ICustomTypeDescriptor)) {
                    // Get the custom properties of the object
                    propDescs = TypeDescriptor.GetProperties(sampleItem);
                }
                else if (sampleItemType != null) {
                    // directly bindable types: strings, ints etc. get treated specially, since we
                    // don't care about their properties, but rather we care about them directly
                    if (ShouldGenerateField(sampleItemType, gridView)) {
                        AutoGeneratedFieldProperties fieldProps = new AutoGeneratedFieldProperties();
                        ((IStateManager)fieldProps).TrackViewState();
 
                        fieldProps.Type = sampleItemType;
                        fieldProps.Name = "Item";
                        fieldProps.DataField = AutoGeneratedField.ThisExpression;
 
                        AutoGeneratedField field = CreateAutoGeneratedFieldFromFieldProperties(fieldProps);
                        if (field != null) {
                            generatedFields.Add(field);
 
                            AutoGeneratedFieldProperties.Add(fieldProps);
                        }
                    }
                    else {
                        // complex type... we get its properties
                        propDescs = TypeDescriptor.GetProperties(sampleItemType);
                    }
                }
            }
            else {
                if (propDescs.Count == 0) {
                    // we don't want to throw an exception if we're bound to an ITypedList
                    // data source with no records... we'll simply bail and not show any data
                    throwException = false;
                }
            }
 
            if ((propDescs != null) && (propDescs.Count != 0)) {
                string[] dataKeyNames = gridView.DataKeyNames;
                int keyNamesLength = dataKeyNames.Length;
                string[] dataKeyNamesCaseInsensitive = new string[keyNamesLength];
                for (int i = 0; i < keyNamesLength; i++) {
                    dataKeyNamesCaseInsensitive[i] = dataKeyNames[i].ToLowerInvariant();
                }
                foreach (PropertyDescriptor pd in propDescs) {
                    Type propertyType = pd.PropertyType;
                    if (ShouldGenerateField(propertyType, gridView)) {
                        string name = pd.Name;
                        bool isKey = ((IList)dataKeyNamesCaseInsensitive).Contains(name.ToLowerInvariant());
                        AutoGeneratedFieldProperties fieldProps = new AutoGeneratedFieldProperties();
                        ((IStateManager)fieldProps).TrackViewState();
                        fieldProps.Name = name;
                        fieldProps.IsReadOnly = isKey;
                        fieldProps.Type = propertyType;
                        fieldProps.DataField = name;
 
                        AutoGeneratedField field = CreateAutoGeneratedFieldFromFieldProperties(fieldProps);
                        if (field != null) {
                            generatedFields.Add(field);
                            AutoGeneratedFieldProperties.Add(fieldProps);
                        }
                    }
                }
            }
 
            if ((generatedFields.Count == 0) && throwException) {
                // this handles the case where we got back something that either had no
                // properties, or all properties were not bindable.
                throw new InvalidOperationException(SR.GetString(SR.GridView_NoAutoGenFields, gridView.ID));
            }
 
            return generatedFields;
        }
        
        private bool ShouldGenerateField(Type propertyType, GridView gridView) {
            if (gridView.RenderingCompatibility < VersionUtil.Framework45 && AutoGenerateEnumFields == null) {
                //This is for backward compatibility. Before 4.5, auto generating fields used to call into this method
                //and if someone has overriden this method to force generation of columns, the scenario should still
                //work.
                return gridView.IsBindableType(propertyType);
            }
            else {
                //If AutoGenerateEnumFileds is null here, the rendering compatibility must be 4.5
                return DataBoundControlHelper.IsBindableType(propertyType, enableEnums: AutoGenerateEnumFields ?? true);
            }
        }
 
    }
}