1 namespace System.Web.DynamicData {
3 using System.Collections;
4 using System.Collections.Generic;
5 using System.ComponentModel.DataAnnotations;
6 using System.Diagnostics.CodeAnalysis;
7 using System.Data.Linq;
8 using System.Diagnostics;
9 using System.Globalization;
10 using System.Linq.Expressions;
12 using System.Web.DynamicData.Util;
13 using System.Web.Resources;
15 using System.Web.UI.WebControls;
17 /// Extension methods used by DynamicData
19 public static class DynamicDataExtensions {
20 public static void SetMetaTable(this INamingContainer control, MetaTable table) {
21 SetMetaTableInternal(control, table, null/* defaultValues*/, new HttpContextWrapper(HttpContext.Current));
24 public static void SetMetaTable(this INamingContainer control, MetaTable table, IDictionary<string, object> defaultValues) {
25 if (defaultValues == null) {
26 throw new ArgumentNullException("defaultValues");
28 SetMetaTableInternal(control, table, defaultValues, new HttpContextWrapper(HttpContext.Current));
31 public static void SetMetaTable(this INamingContainer control, MetaTable table, object defaultValues) {
32 if (defaultValues == null) {
33 throw new ArgumentNullException("defaultValues");
35 SetMetaTableInternal(control, table, Misc.ConvertObjectToDictionary(defaultValues), new HttpContextWrapper(HttpContext.Current));
38 public static IDictionary<string, object> GetDefaultValues(this IDataSource dataSource) {
39 return GetDefaultValues(dataSource, new HttpContextWrapper(HttpContext.Current));
42 public static IDictionary<string, object> GetDefaultValues(this INamingContainer control) {
43 return GetDefaultValues(control, new HttpContextWrapper(HttpContext.Current));
46 public static MetaTable GetMetaTable(this IDataSource dataSource) {
47 return GetMetaTable(dataSource, new HttpContextWrapper(HttpContext.Current));
50 public static bool TryGetMetaTable(this IDataSource dataSource, out MetaTable table) {
51 return TryGetMetaTable(dataSource, new HttpContextWrapper(HttpContext.Current), out table);
54 public static MetaTable GetMetaTable(this INamingContainer control) {
55 return GetMetaTable(control, new HttpContextWrapper(HttpContext.Current));
58 public static bool TryGetMetaTable(this INamingContainer control, out MetaTable table) {
59 return TryGetMetaTable(control, new HttpContextWrapper(HttpContext.Current), out table);
62 internal static void ApplyFieldGenerator(INamingContainer control, MetaTable table) {
63 GridView gridView = control as GridView;
64 if (gridView != null && gridView.AutoGenerateColumns && gridView.ColumnsGenerator == null) {
65 gridView.ColumnsGenerator = new DefaultAutoFieldGenerator(table);
68 DetailsView detailsView = control as DetailsView;
69 if (detailsView != null && detailsView.AutoGenerateRows && detailsView.RowsGenerator == null) {
70 detailsView.RowsGenerator = new DefaultAutoFieldGenerator(table);
75 internal static DefaultValueMapping GetDefaultValueMapping(object control, HttpContextBase context) {
76 IDictionary<object, MappingInfo> mapping = MetaTableHelper.GetMapping(context);
77 MappingInfo mappingInfo;
78 if (mapping.TryGetValue(control, out mappingInfo)) {
79 return mappingInfo.DefaultValueMapping;
84 internal static IDictionary<string, object> GetDefaultValues(object control, HttpContextBase context) {
85 DefaultValueMapping mapping = GetDefaultValueMapping(control, context);
86 if (mapping != null) {
87 return mapping.Values;
92 internal static MetaTable GetMetaTable(IDataSource dataSource, HttpContextBase context) {
94 if (TryGetMetaTable(dataSource, context, out table)) {
97 throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, DynamicDataResources.MetaTable_CannotGetTableFromDataSource));
100 internal static bool TryGetMetaTable(IDataSource dataSource, HttpContextBase context, out MetaTable table) {
101 if (dataSource == null) {
102 throw new ArgumentNullException("dataSource");
105 Debug.Assert(context != null);
107 table = MetaTableHelper.GetTableFromMapping(context, dataSource);
109 var dynamicDataSource = dataSource as IDynamicDataSource;
110 if (dynamicDataSource != null) {
111 table = MetaTableHelper.GetTableFromDynamicDataSource(dynamicDataSource);
114 return table != null;
117 internal static MetaTable GetMetaTable(INamingContainer control, HttpContextBase context) {
119 if (!TryGetMetaTable(control, context, out table)) {
120 throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, DynamicDataResources.MetaTable_CannotGetTableFromControl));
125 internal static bool TryGetMetaTable(INamingContainer control, HttpContextBase context, out MetaTable table) {
126 if (control == null) {
127 throw new ArgumentNullException("control");
130 table = MetaTableHelper.GetTableFromMapping(context, control);
131 return table != null;
134 internal static void SetMetaTableInternal(INamingContainer control, MetaTable table, IDictionary<string, object> defaultValues, HttpContextBase context) {
135 if (control == null) {
136 throw new ArgumentNullException("control");
139 throw new ArgumentNullException("table");
141 IDataBoundControl dataBoundControl = control as IDataBoundControl;
142 IDataSource dataSource = null;
143 if (dataBoundControl != null) {
144 dataSource = dataBoundControl.DataSourceObject;
146 MetaTableHelper.SetTableInMapping(context, control, table, defaultValues);
147 if (dataSource != null) {
148 // If the control being mapped is a databound control then register its datasource
149 MetaTableHelper.SetTableInMapping(context, dataSource, table, defaultValues);
154 /// Return the MetaTable association with a datasource
156 [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "This is a legacy API and cannot be changed")]
157 public static MetaTable GetTable(this IDynamicDataSource dataSource) {
158 return MetaTableHelper.GetTableWithFullFallback(dataSource, HttpContext.Current.ToWrapper());
162 /// Expand Dynamic where parameter (e.g. DynamicControlParameter, DynamicQueryStringParameter) into
163 /// 'regular' parameters that the datasource can understand
165 /// <param name="dataSource">The datasource which Where parameters need to be expanded</param>
166 public static void ExpandDynamicWhereParameters(this IDynamicDataSource dataSource) {
168 ParameterCollection whereParameters = dataSource.WhereParameters;
170 // First, check if any parameters need to be expanded
171 bool needProcessing = false;
172 foreach (Parameter parameter in whereParameters) {
173 if (parameter is IWhereParametersProvider) {
174 needProcessing = true;
179 // If not, don't do anything
183 // Make a copy of the parameters, and clear the collection
184 var whereParametersCopy = new Parameter[whereParameters.Count];
185 whereParameters.CopyTo(whereParametersCopy, 0);
186 whereParameters.Clear();
188 // Go through all the parameters and expand them
189 foreach (Parameter parameter in whereParametersCopy) {
190 ExpandWhereParameter(dataSource, parameter);
194 private static void ExpandWhereParameter(IDynamicDataSource dataSource, Parameter parameter) {
195 var provider = parameter as IWhereParametersProvider;
196 if (provider == null) {
197 // If it's a standard parameter, just add it
198 dataSource.WhereParameters.Add(parameter);
201 // Get the list of sub-parameters and expand them recursively
202 IEnumerable<Parameter> newParameters = provider.GetWhereParameters(dataSource);
203 foreach (Parameter newParameter in newParameters) {
204 ExpandWhereParameter(dataSource, newParameter);
210 /// Find the containing data control, and return the data source it points to
212 public static IDynamicDataSource FindDataSourceControl(this Control current) {
213 return DataControlHelper.FindDataSourceControl(current);
217 /// Find the containing data control, and return the MetaTable associated with it, if any
219 public static MetaTable FindMetaTable(this Control current) {
220 return MetaTableHelper.FindMetaTable(current, HttpContext.Current.ToWrapper());
224 /// Find the field template for a column within the current naming container
226 public static Control FindFieldTemplate(this Control control, string columnName) {
227 return control.FindControl(DynamicControl.GetControlIDFromColumnName(columnName));
231 /// Make the SelectedIndex sync up with the PersistedSelection. Concretely, what it means is that
232 /// if you select a row and then page away (or sort), the selection remains on that row
233 /// even if it's not currently visible.
235 [Obsolete("Use the EnablePersistedSelection property on a databound control such as GridView or ListView.")]
236 public static void EnablePersistedSelection(this BaseDataBoundControl dataBoundControl) {
237 EnablePersistedSelectionInternal(dataBoundControl);
240 internal static void EnablePersistedSelectionInternal(BaseDataBoundControl dataBoundControl) {
241 IDataBoundListControl dataBoundListControl = dataBoundControl as IDataBoundListControl;
242 if (dataBoundListControl != null) {
243 dataBoundListControl.EnablePersistedSelection = true;
246 if (dataBoundListControl.SelectedIndex < 0) {
247 // Force the first item to be selected
248 dataBoundListControl.SelectedIndex = 0;
254 /// Set the DataLoadOptions on a Linq To Sql datasource to force all the FK entities
255 /// to be directly loaded.
257 /// <param name="dataSource">The data source for which we want to preload FKs</param>
258 /// <param name="rowType">The type of the entities returned by the data source</param>
259 public static void LoadWithForeignKeys(this LinqDataSource dataSource, Type rowType) {
260 dataSource.ContextCreated += delegate(object sender, LinqDataSourceStatusEventArgs e) {
261 // This only applies to a DLinq data context
262 var context = e.Result as DataContext;
266 DataLoadOptions loadOptions = null;
267 ParameterExpression tableParameter = null;
268 System.Data.Linq.Mapping.MetaTable metaTable = context.Mapping.GetTable(rowType);
269 foreach (System.Data.Linq.Mapping.MetaDataMember member in metaTable.RowType.DataMembers) {
270 if (member.IsAssociation && !member.Association.IsMany) {
271 if (member.Type.Equals(rowType)) continue;
272 if (loadOptions == null) {
273 loadOptions = new DataLoadOptions();
274 tableParameter = Expression.Parameter(rowType, "e");
276 var memberExpression = Expression.Property(tableParameter, member.Name);
277 loadOptions.LoadWith(Expression.Lambda(memberExpression, tableParameter));
281 if (loadOptions != null) {
282 context.LoadOptions = loadOptions;
287 public static void LoadWith<TEntity>(this LinqDataSource dataSource) {
288 LoadWithForeignKeys(dataSource, typeof(TEntity));
292 /// Apply potential HTML encoding and formatting to a string that needs to be displayed
293 /// This logic is mostly copied from BoundField.FormatDataValue, but omits the old Whidbey behavior path
295 /// <param name="fieldValue">The value that should be formatted</param>
296 /// <param name="formattingOptions">The IFieldFormattingOptions to use. This is useful when using options different from the column's</param>
297 /// <returns>the formatted value</returns>
298 public static string FormatValue(this IFieldFormattingOptions formattingOptions, object fieldValue) {
300 string formattedValue = String.Empty;
302 if (fieldValue != null) {
303 string dataValueString = fieldValue.ToString();
304 string formatting = formattingOptions.DataFormatString;
305 int dataValueStringLength = dataValueString.Length;
307 // If the result is still empty and ConvertEmptyStringToNull=true, replace the value with the NullDisplayText
308 if (dataValueStringLength == 0 && formattingOptions.ConvertEmptyStringToNull) {
309 dataValueString = formattingOptions.NullDisplayText;
312 // If there's a format string, apply it to the raw data value
313 // If there's no format string, then dataValueString already has the right value
314 if (!String.IsNullOrEmpty(formatting)) {
315 dataValueString = String.Format(CultureInfo.CurrentCulture, formatting, fieldValue);
318 // Optionally HTML encode the value (including the format string, if any was applied)
319 if (!String.IsNullOrEmpty(dataValueString) && formattingOptions.HtmlEncode) {
320 dataValueString = HttpUtility.HtmlEncode(dataValueString);
324 formattedValue = dataValueString;
327 formattedValue = formattingOptions.NullDisplayText;
330 return formattedValue;
334 /// Similar to FormatValue, but the string is to be used when the field is in edit mode
336 public static string FormatEditValue(this IFieldFormattingOptions formattingOptions, object fieldValue) {
339 // Apply the format string to it if that flag is set. Otherwise use it as is.
340 if (formattingOptions.ApplyFormatInEditMode) {
341 valueString = formattingOptions.FormatValue(fieldValue);
344 valueString = (fieldValue != null) ? fieldValue.ToString() : String.Empty;
347 // Trim any trailing spaces as they cause unwanted behavior (since we limit the input length and the
348 // spaces cause the limit to be reach prematurely)
349 valueString = valueString.TrimEnd();
355 /// Return either the input value or null based on ConvertEmptyStringToNull and NullDisplayText
357 /// <param name="formattingOptions">the formatting options object</param>
358 /// <param name="value">The input value</param>
359 /// <returns>The converted value</returns>
360 public static object ConvertEditedValue(this IFieldFormattingOptions formattingOptions, string value) {
361 // If it's an empty string and ConvertEmptyStringToNull is set, make it null
362 if (String.IsNullOrEmpty(value) && formattingOptions.ConvertEmptyStringToNull) {
366 // If it's the NullDisplayText, return null
367 string nullDisplayText = formattingOptions.NullDisplayText;
368 if (value == nullDisplayText && !String.IsNullOrEmpty(nullDisplayText)) {
372 // Otherwise, return it unchanged
377 /// If this column represents an enumeration type, this method returns that type. The caloumn can represent
378 /// an enumeration type if the underlying type is an enum, or if it is decoareted with EnumDataTypeAttribute.
379 /// If this column does not represent an enum, this method returns null.
381 /// <param name="column"></param>
382 /// <returns></returns>
383 [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "The interface is internal")]
384 public static Type GetEnumType(this MetaColumn column) {
385 return GetEnumType((IMetaColumn)column);
388 internal static Type GetEnumType(this IMetaColumn column) {
389 return column.Attributes.GetAttributePropertyValue<EnumDataTypeAttribute, Type>(a => a.EnumType, null) ??
390 (column.ColumnType.IsEnum ? column.ColumnType : null);
393 internal static bool IsEnumType(this MetaColumn column, out Type enumType) {
394 enumType = column.GetEnumType();
395 return enumType != null;