namespace System.Web.DynamicData { using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Diagnostics.CodeAnalysis; using System.Data.Linq; using System.Diagnostics; using System.Globalization; using System.Linq.Expressions; using System.Web; using System.Web.DynamicData.Util; using System.Web.Resources; using System.Web.UI; using System.Web.UI.WebControls; /// /// Extension methods used by DynamicData /// public static class DynamicDataExtensions { public static void SetMetaTable(this INamingContainer control, MetaTable table) { SetMetaTableInternal(control, table, null/* defaultValues*/, new HttpContextWrapper(HttpContext.Current)); } public static void SetMetaTable(this INamingContainer control, MetaTable table, IDictionary defaultValues) { if (defaultValues == null) { throw new ArgumentNullException("defaultValues"); } SetMetaTableInternal(control, table, defaultValues, new HttpContextWrapper(HttpContext.Current)); } public static void SetMetaTable(this INamingContainer control, MetaTable table, object defaultValues) { if (defaultValues == null) { throw new ArgumentNullException("defaultValues"); } SetMetaTableInternal(control, table, Misc.ConvertObjectToDictionary(defaultValues), new HttpContextWrapper(HttpContext.Current)); } public static IDictionary GetDefaultValues(this IDataSource dataSource) { return GetDefaultValues(dataSource, new HttpContextWrapper(HttpContext.Current)); } public static IDictionary GetDefaultValues(this INamingContainer control) { return GetDefaultValues(control, new HttpContextWrapper(HttpContext.Current)); } public static MetaTable GetMetaTable(this IDataSource dataSource) { return GetMetaTable(dataSource, new HttpContextWrapper(HttpContext.Current)); } public static bool TryGetMetaTable(this IDataSource dataSource, out MetaTable table) { return TryGetMetaTable(dataSource, new HttpContextWrapper(HttpContext.Current), out table); } public static MetaTable GetMetaTable(this INamingContainer control) { return GetMetaTable(control, new HttpContextWrapper(HttpContext.Current)); } public static bool TryGetMetaTable(this INamingContainer control, out MetaTable table) { return TryGetMetaTable(control, new HttpContextWrapper(HttpContext.Current), out table); } internal static void ApplyFieldGenerator(INamingContainer control, MetaTable table) { GridView gridView = control as GridView; if (gridView != null && gridView.AutoGenerateColumns && gridView.ColumnsGenerator == null) { gridView.ColumnsGenerator = new DefaultAutoFieldGenerator(table); } else { DetailsView detailsView = control as DetailsView; if (detailsView != null && detailsView.AutoGenerateRows && detailsView.RowsGenerator == null) { detailsView.RowsGenerator = new DefaultAutoFieldGenerator(table); } } } internal static DefaultValueMapping GetDefaultValueMapping(object control, HttpContextBase context) { IDictionary mapping = MetaTableHelper.GetMapping(context); MappingInfo mappingInfo; if (mapping.TryGetValue(control, out mappingInfo)) { return mappingInfo.DefaultValueMapping; } return null; } internal static IDictionary GetDefaultValues(object control, HttpContextBase context) { DefaultValueMapping mapping = GetDefaultValueMapping(control, context); if (mapping != null) { return mapping.Values; } return null; } internal static MetaTable GetMetaTable(IDataSource dataSource, HttpContextBase context) { MetaTable table; if (TryGetMetaTable(dataSource, context, out table)) { return table; } throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, DynamicDataResources.MetaTable_CannotGetTableFromDataSource)); } internal static bool TryGetMetaTable(IDataSource dataSource, HttpContextBase context, out MetaTable table) { if (dataSource == null) { throw new ArgumentNullException("dataSource"); } Debug.Assert(context != null); table = MetaTableHelper.GetTableFromMapping(context, dataSource); if (table == null) { var dynamicDataSource = dataSource as IDynamicDataSource; if (dynamicDataSource != null) { table = MetaTableHelper.GetTableFromDynamicDataSource(dynamicDataSource); } } return table != null; } internal static MetaTable GetMetaTable(INamingContainer control, HttpContextBase context) { MetaTable table; if (!TryGetMetaTable(control, context, out table)) { throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, DynamicDataResources.MetaTable_CannotGetTableFromControl)); } return table; } internal static bool TryGetMetaTable(INamingContainer control, HttpContextBase context, out MetaTable table) { if (control == null) { throw new ArgumentNullException("control"); } table = MetaTableHelper.GetTableFromMapping(context, control); return table != null; } internal static void SetMetaTableInternal(INamingContainer control, MetaTable table, IDictionary defaultValues, HttpContextBase context) { if (control == null) { throw new ArgumentNullException("control"); } if (table == null) { throw new ArgumentNullException("table"); } IDataBoundControl dataBoundControl = control as IDataBoundControl; IDataSource dataSource = null; if (dataBoundControl != null) { dataSource = dataBoundControl.DataSourceObject; } MetaTableHelper.SetTableInMapping(context, control, table, defaultValues); if (dataSource != null) { // If the control being mapped is a databound control then register its datasource MetaTableHelper.SetTableInMapping(context, dataSource, table, defaultValues); } } /// /// Return the MetaTable association with a datasource /// [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "This is a legacy API and cannot be changed")] public static MetaTable GetTable(this IDynamicDataSource dataSource) { return MetaTableHelper.GetTableWithFullFallback(dataSource, HttpContext.Current.ToWrapper()); } /// /// Expand Dynamic where parameter (e.g. DynamicControlParameter, DynamicQueryStringParameter) into /// 'regular' parameters that the datasource can understand /// /// The datasource which Where parameters need to be expanded public static void ExpandDynamicWhereParameters(this IDynamicDataSource dataSource) { ParameterCollection whereParameters = dataSource.WhereParameters; // First, check if any parameters need to be expanded bool needProcessing = false; foreach (Parameter parameter in whereParameters) { if (parameter is IWhereParametersProvider) { needProcessing = true; break; } } // If not, don't do anything if (!needProcessing) return; // Make a copy of the parameters, and clear the collection var whereParametersCopy = new Parameter[whereParameters.Count]; whereParameters.CopyTo(whereParametersCopy, 0); whereParameters.Clear(); // Go through all the parameters and expand them foreach (Parameter parameter in whereParametersCopy) { ExpandWhereParameter(dataSource, parameter); } } private static void ExpandWhereParameter(IDynamicDataSource dataSource, Parameter parameter) { var provider = parameter as IWhereParametersProvider; if (provider == null) { // If it's a standard parameter, just add it dataSource.WhereParameters.Add(parameter); } else { // Get the list of sub-parameters and expand them recursively IEnumerable newParameters = provider.GetWhereParameters(dataSource); foreach (Parameter newParameter in newParameters) { ExpandWhereParameter(dataSource, newParameter); } } } /// /// Find the containing data control, and return the data source it points to /// public static IDynamicDataSource FindDataSourceControl(this Control current) { return DataControlHelper.FindDataSourceControl(current); } /// /// Find the containing data control, and return the MetaTable associated with it, if any /// public static MetaTable FindMetaTable(this Control current) { return MetaTableHelper.FindMetaTable(current, HttpContext.Current.ToWrapper()); } /// /// Find the field template for a column within the current naming container /// public static Control FindFieldTemplate(this Control control, string columnName) { return control.FindControl(DynamicControl.GetControlIDFromColumnName(columnName)); } /// /// Make the SelectedIndex [....] up with the PersistedSelection. Concretely, what it means is that /// if you select a row and then page away (or sort), the selection remains on that row /// even if it's not currently visible. /// [Obsolete("Use the EnablePersistedSelection property on a databound control such as GridView or ListView.")] public static void EnablePersistedSelection(this BaseDataBoundControl dataBoundControl) { EnablePersistedSelectionInternal(dataBoundControl); } internal static void EnablePersistedSelectionInternal(BaseDataBoundControl dataBoundControl) { IDataBoundListControl dataBoundListControl = dataBoundControl as IDataBoundListControl; if (dataBoundListControl != null) { dataBoundListControl.EnablePersistedSelection = true; // if (dataBoundListControl.SelectedIndex < 0) { // Force the first item to be selected dataBoundListControl.SelectedIndex = 0; } } } /// /// Set the DataLoadOptions on a Linq To Sql datasource to force all the FK entities /// to be directly loaded. /// /// The data source for which we want to preload FKs /// The type of the entities returned by the data source public static void LoadWithForeignKeys(this LinqDataSource dataSource, Type rowType) { dataSource.ContextCreated += delegate(object sender, LinqDataSourceStatusEventArgs e) { // This only applies to a DLinq data context var context = e.Result as DataContext; if (context == null) return; DataLoadOptions loadOptions = null; ParameterExpression tableParameter = null; System.Data.Linq.Mapping.MetaTable metaTable = context.Mapping.GetTable(rowType); foreach (System.Data.Linq.Mapping.MetaDataMember member in metaTable.RowType.DataMembers) { if (member.IsAssociation && !member.Association.IsMany) { if (member.Type.Equals(rowType)) continue; if (loadOptions == null) { loadOptions = new DataLoadOptions(); tableParameter = Expression.Parameter(rowType, "e"); } var memberExpression = Expression.Property(tableParameter, member.Name); loadOptions.LoadWith(Expression.Lambda(memberExpression, tableParameter)); } } if (loadOptions != null) { context.LoadOptions = loadOptions; } }; } public static void LoadWith(this LinqDataSource dataSource) { LoadWithForeignKeys(dataSource, typeof(TEntity)); } /// /// Apply potential HTML encoding and formatting to a string that needs to be displayed /// This logic is mostly copied from BoundField.FormatDataValue, but omits the old Whidbey behavior path /// /// The value that should be formatted /// The IFieldFormattingOptions to use. This is useful when using options different from the column's /// the formatted value public static string FormatValue(this IFieldFormattingOptions formattingOptions, object fieldValue) { string formattedValue = String.Empty; if (fieldValue != null) { string dataValueString = fieldValue.ToString(); string formatting = formattingOptions.DataFormatString; int dataValueStringLength = dataValueString.Length; // If the result is still empty and ConvertEmptyStringToNull=true, replace the value with the NullDisplayText if (dataValueStringLength == 0 && formattingOptions.ConvertEmptyStringToNull) { dataValueString = formattingOptions.NullDisplayText; } else { // If there's a format string, apply it to the raw data value // If there's no format string, then dataValueString already has the right value if (!String.IsNullOrEmpty(formatting)) { dataValueString = String.Format(CultureInfo.CurrentCulture, formatting, fieldValue); } // Optionally HTML encode the value (including the format string, if any was applied) if (!String.IsNullOrEmpty(dataValueString) && formattingOptions.HtmlEncode) { dataValueString = HttpUtility.HtmlEncode(dataValueString); } } formattedValue = dataValueString; } else { formattedValue = formattingOptions.NullDisplayText; } return formattedValue; } /// /// Similar to FormatValue, but the string is to be used when the field is in edit mode /// public static string FormatEditValue(this IFieldFormattingOptions formattingOptions, object fieldValue) { string valueString; // Apply the format string to it if that flag is set. Otherwise use it as is. if (formattingOptions.ApplyFormatInEditMode) { valueString = formattingOptions.FormatValue(fieldValue); } else { valueString = (fieldValue != null) ? fieldValue.ToString() : String.Empty; } // Trim any trailing spaces as they cause unwanted behavior (since we limit the input length and the // spaces cause the limit to be reach prematurely) valueString = valueString.TrimEnd(); return valueString; } /// /// Return either the input value or null based on ConvertEmptyStringToNull and NullDisplayText /// /// the formatting options object /// The input value /// The converted value public static object ConvertEditedValue(this IFieldFormattingOptions formattingOptions, string value) { // If it's an empty string and ConvertEmptyStringToNull is set, make it null if (String.IsNullOrEmpty(value) && formattingOptions.ConvertEmptyStringToNull) { return null; } // If it's the NullDisplayText, return null string nullDisplayText = formattingOptions.NullDisplayText; if (value == nullDisplayText && !String.IsNullOrEmpty(nullDisplayText)) { return null; } // Otherwise, return it unchanged return value; } /// /// If this column represents an enumeration type, this method returns that type. The caloumn can represent /// an enumeration type if the underlying type is an enum, or if it is decoareted with EnumDataTypeAttribute. /// If this column does not represent an enum, this method returns null. /// /// /// [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "The interface is internal")] public static Type GetEnumType(this MetaColumn column) { return GetEnumType((IMetaColumn)column); } internal static Type GetEnumType(this IMetaColumn column) { return column.Attributes.GetAttributePropertyValue(a => a.EnumType, null) ?? (column.ColumnType.IsEnum ? column.ColumnType : null); } internal static bool IsEnumType(this MetaColumn column, out Type enumType) { enumType = column.GetEnumType(); return enumType != null; } } }