e79aa3c0ed
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
163 lines
7.9 KiB
C#
163 lines
7.9 KiB
C#
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);
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|