Imported Upstream version 4.0.0~alpha1

Former-commit-id: 806294f5ded97629b74c85c09952f2a74fe182d9
This commit is contained in:
Jo Shields
2015-04-07 09:35:12 +01:00
parent 283343f570
commit 3c1f479b9d
22469 changed files with 2931443 additions and 869343 deletions

View File

@ -0,0 +1,16 @@
namespace System.Web.DynamicData {
/// <summary>
/// A data control container type
/// </summary>
public enum ContainerType {
/// <summary>
/// A list container, such as ListView, GridView, Repeater (or a control implementing IDataBoundListControl)
/// </summary>
List,
/// <summary>
/// An item container, such as DetailsView, FormView (or a control implementing IDataBoundItemControl)
/// </summary>
Item
}
}

View File

@ -0,0 +1,31 @@
using System.Collections.Generic;
using System.Security.Permissions;
using System.Diagnostics.CodeAnalysis;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel;
using System.Web.DynamicData.ModelProviders;
namespace System.Web.DynamicData {
/// <summary>
/// Allows for providing extra config information to a context
/// </summary>
public class ContextConfiguration {
/// <summary>
/// An optional factory for obtaining a metadata source for a given entity type
/// </summary>
public Func<Type, TypeDescriptionProvider> MetadataProviderFactory { get; set; }
/// <summary>
/// scaffold all tables
/// </summary>
public bool ScaffoldAllTables { get; set; }
/// <summary>
/// ctor
/// </summary>
[SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "This can be set by the user to support inline dictionary intializers")]
public ContextConfiguration() {
MetadataProviderFactory = type => new AssociatedMetadataTypeTypeDescriptionProvider(type);
}
}
}

View File

@ -0,0 +1,160 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
using System.Web.Resources;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.Expressions;
namespace System.Web.DynamicData {
/// <summary>
/// A Dynamic Data-specific implementation of DataSourceExpression that modifies an IQueryable based on a data key (selected row)
/// in a data bound controls such as GridView, ListView, DetailsView, or FormView.
/// If the Column property is left empty, the control treats the data key as the primary key of current table (this is useful
/// in a List-Details scenario where the databound control and the datasource are displaying items of the same type). If the
/// Column property is not empty, this control treats the data key as a foreign key (this is useful in a Parent-Children scenario,
/// where the databound control is displaying a list of Categories, and the data source is to be filtered to only display the
/// Products that belong to the selected Category).
/// </summary>
public class ControlFilterExpression : DataSourceExpression {
private PropertyExpression _propertyExpression;
/// <summary>
/// The ID of a data-bound control such as a GridView, ListView, DetailsView, or FormView whose data key will be used to build
/// the expression that gets used in a QueryExtender.
/// </summary>
[SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "ID", Justification = "The property refers to the ID property of a Control")]
public string ControlID { get; set; }
/// <summary>
/// Optional property which when set indicates that the data key should be treated as a foreign key.
/// </summary>
public string Column { get; set; }
private PropertyExpression Expression {
get {
if (_propertyExpression == null) {
_propertyExpression = new PropertyExpression();
}
return _propertyExpression;
}
}
public override void SetContext(Control owner, HttpContext context, IQueryableDataSource dataSource) {
base.SetContext(owner, context, dataSource);
Owner.Page.InitComplete += new EventHandler(Page_InitComplete);
Owner.Page.LoadComplete += new EventHandler(Page_LoadComplete);
}
private void Page_InitComplete(object sender, EventArgs e) {
if (!Owner.Page.IsPostBack) {
// Do not reconfigure the Expression on postback. It's values should be preserved via ViewState.
Control control = FindTargetControl();
MetaTable table = DataSource.GetMetaTable();
if (String.IsNullOrEmpty(Column)) {
foreach (var param in GetPrimaryKeyControlParameters(control, table)) {
Expression.Parameters.Add(param);
}
} else {
MetaForeignKeyColumn column = (MetaForeignKeyColumn)table.GetColumn(Column);
foreach (var param in GetForeignKeyControlParameters(control, column)) {
Expression.Parameters.Add(param);
}
}
}
Expression.SetContext(Owner, Context, DataSource);
}
private Control FindTargetControl() {
Control control = Misc.FindControl(Owner, ControlID);
if (control == null) {
throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture,
DynamicDataResources.ControlFilterExpression_CouldNotFindControlID,
Owner.ID,
ControlID));
}
return control;
}
private void Page_LoadComplete(object sender, EventArgs e) {
Expression.Parameters.UpdateValues(Context, Owner);
}
protected override object SaveViewState() {
Pair p = new Pair();
p.First = base.SaveViewState();
p.Second = ((IStateManager)Expression.Parameters).SaveViewState();
return p;
}
protected override void LoadViewState(object savedState) {
Pair p = (Pair)savedState;
base.LoadViewState(p.First);
if (p.Second != null) {
((IStateManager)Expression.Parameters).LoadViewState(p.Second);
}
}
protected override void TrackViewState() {
base.TrackViewState();
((IStateManager)Expression.Parameters).TrackViewState();
}
private IEnumerable<Parameter> GetPrimaryKeyControlParameters(Control control, MetaTable table) {
// For each PK column in the table, we need to create a ControlParameter
var nameColumnMapping = table.PrimaryKeyColumns.ToDictionary(c => c.Name);
return GetControlParameters(control, nameColumnMapping);
}
private IEnumerable<Parameter> GetForeignKeyControlParameters(Control control, MetaForeignKeyColumn column) {
// For each underlying FK, we need to create a ControlParameter
MetaTable otherTable = column.ParentTable;
Dictionary<string, MetaColumn> nameColumnMapping = CreateColumnMapping(column, otherTable.PrimaryKeyColumns);
return GetControlParameters(control, nameColumnMapping);
}
private static Dictionary<string, MetaColumn> CreateColumnMapping(MetaForeignKeyColumn column, IList<MetaColumn> columns) {
var names = column.ForeignKeyNames;
Debug.Assert(names.Count == columns.Count);
Dictionary<string, MetaColumn> nameColumnMapping = new Dictionary<string, MetaColumn>();
for (int i = 0; i < names.Count; i++) {
// Get the filter expression for this foreign key name
string filterExpression = column.GetFilterExpression(names[i]);
nameColumnMapping[filterExpression] = columns[i];
}
return nameColumnMapping;
}
internal static IEnumerable<Parameter> GetControlParameters(Control control, IDictionary<string, MetaColumn> nameColumnMapping) {
IControlParameterTarget target = null;
target = DynamicDataManager.GetControlParameterTarget(control);
Debug.Assert(target != null);
foreach (var entry in nameColumnMapping) {
string parameterName = entry.Key;
MetaColumn column = entry.Value;
ControlParameter controlParameter = new ControlParameter() {
Name = parameterName,
ControlID = control.UniqueID
};
if (target != null) {
// this means the relationship consists of more than one key and we need to expand the property name
controlParameter.PropertyName = target.GetPropertyNameExpression(column.Name);
}
DataSourceUtil.SetParameterTypeCodeAndDbType(controlParameter, column);
yield return controlParameter;
}
}
public override IQueryable GetQueryable(IQueryable source) {
return Expression.GetQueryable(source);
}
}
}

View File

@ -0,0 +1,40 @@
namespace System.Web.DynamicData {
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Globalization;
internal class DataBoundControlParameterTarget : IControlParameterTarget {
private Control _control;
public DataBoundControlParameterTarget(Control control) {
if (control == null) {
throw new ArgumentNullException("control");
}
_control = control;
}
public MetaTable Table {
get {
return _control.FindMetaTable();
}
}
public MetaColumn FilteredColumn {
get {
return null;
}
}
public string GetPropertyNameExpression(string columnName) {
// Get the DataKeyPropertyAttribute and use that as the to get the correct property name expression
DataKeyPropertyAttribute attribute = _control.GetType().GetCustomAttributes(true).OfType<DataKeyPropertyAttribute>().FirstOrDefault();
if ((attribute != null) && !String.IsNullOrEmpty(attribute.Name)) {
return attribute.Name + String.Format(CultureInfo.InvariantCulture, "['{0}']", columnName);
}
//
return String.Empty;
}
}
}

View File

@ -0,0 +1,48 @@
namespace System.Web.DynamicData {
using System;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Drawing.Design;
using System.Security.Permissions;
using System.Web.UI;
/// <summary>
/// Registers a DataControl for use with Dynamic Data
/// </summary>
public class DataControlReference {
/// <summary>
/// Dynamic data manager registering the data control
/// </summary>
[Browsable(false)]
public DynamicDataManager Owner {
get;
internal set;
}
/// <summary>
/// ControlID of the DataControl
/// </summary>
[
Category("Behavior"),
DefaultValue(""),
IDReferenceProperty(),
ResourceDescription("DataControlReference_ControlID"),
TypeConverter("System.Web.DynamicData.Design.DataControlReferenceIDConverter, " + AssemblyRef.SystemWebDynamicDataDesign),
SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "ID")
]
public string ControlID {
get;
set;
}
[SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase")]
public override string ToString() {
if (String.IsNullOrEmpty(ControlID)) {
return "DataControl";
}
else {
return "DataControl: " + ControlID;
}
}
}
}

View File

@ -0,0 +1,50 @@
namespace System.Web.DynamicData {
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Security.Permissions;
/// <summary>
/// Represents a collectin of DataControlReferences
/// </summary>
public class DataControlReferenceCollection : Collection<DataControlReference> {
public DataControlReferenceCollection(DynamicDataManager owner) {
if (owner == null) {
throw new ArgumentNullException("owner");
}
Owner = owner;
}
public DynamicDataManager Owner {
get;
private set;
}
internal void Initialize() {
foreach (DataControlReference reference in this) {
reference.Owner = Owner;
}
}
[SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase")]
protected override void SetItem(int index, DataControlReference item) {
item.Owner = Owner;
base.SetItem(index, item);
}
[SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase")]
protected override void InsertItem(int index, DataControlReference item) {
item.Owner = Owner;
base.InsertItem(index, item);
}
[SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase")]
protected override void RemoveItem(int index) {
this[index].Owner = null;
base.RemoveItem(index);
}
}
}

View File

@ -0,0 +1,70 @@
namespace System.Web.DynamicData {
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Web.Resources;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Collections;
using System.Data;
static class DataSourceUtil {
private static object s_lock = new object();
private static Dictionary<Type, TypeCode> s_typeToTypeCodeMap;
internal static TypeCode TypeCodeFromType(Type type) {
if (s_typeToTypeCodeMap == null) {
lock (s_lock) {
if (s_typeToTypeCodeMap == null) {
//
Dictionary<Type, TypeCode> typeNameToTypeCode = new Dictionary<Type, TypeCode>();
typeNameToTypeCode[typeof(Boolean)] = TypeCode.Boolean;
typeNameToTypeCode[typeof(String)] = TypeCode.String;
typeNameToTypeCode[typeof(Byte)] = TypeCode.Byte;
typeNameToTypeCode[typeof(Int16)] = TypeCode.Int16;
typeNameToTypeCode[typeof(Int32)] = TypeCode.Int32;
typeNameToTypeCode[typeof(Int64)] = TypeCode.Int64;
typeNameToTypeCode[typeof(Single)] = TypeCode.Single;
typeNameToTypeCode[typeof(Double)] = TypeCode.Double;
typeNameToTypeCode[typeof(Decimal)] = TypeCode.Decimal;
typeNameToTypeCode[typeof(DateTime)] = TypeCode.DateTime;
typeNameToTypeCode[typeof(Char)] = TypeCode.Char;
// We don't support columns of type 'sqlvariant', which show up as Object
//
typeNameToTypeCode[typeof(Object)] = TypeCode.DBNull;
// We don't support byte arrays. This include columns of type 'timestamp'
typeNameToTypeCode[typeof(Byte[])] = TypeCode.DBNull;
// Use Object for Guid's (though we need to do some special processing)
typeNameToTypeCode[typeof(Guid)] = TypeCode.Object;
s_typeToTypeCodeMap = typeNameToTypeCode;
}
}
}
// If it's an Nullable<T>, work with T instead
type = Misc.RemoveNullableFromType(type);
TypeCode typeCode;
if (s_typeToTypeCodeMap.TryGetValue(type, out typeCode))
return typeCode;
return TypeCode.Object;
}
internal static void SetParameterTypeCodeAndDbType(Parameter parameter, MetaColumn column) {
// If it's a Guid, use a DbType, since TypeCode doesn't support it. For everything else, use TypeCode
if (column.ColumnType == typeof(Guid)) {
parameter.DbType = DbType.Guid;
}
else {
parameter.Type = column.TypeCode;
}
}
}
}

View File

@ -0,0 +1,68 @@
namespace System.Web.DynamicData {
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Web.DynamicData.Util;
using System.Web.UI;
using System.Web.UI.WebControls;
public class DefaultAutoFieldGenerator : IAutoFieldGenerator {
private IMetaTable _metaTable;
public DefaultAutoFieldGenerator(MetaTable table)
: this((IMetaTable)table) {
}
internal DefaultAutoFieldGenerator(IMetaTable table) {
if (table == null) {
throw new ArgumentNullException("table");
}
_metaTable = table;
}
public ICollection GenerateFields(Control control) {
DataBoundControlMode mode = GetMode(control);
ContainerType containerType = GetControlContainerType(control);
// Auto-generate fields from metadata.
List<DynamicField> fields = new List<DynamicField>();
foreach (MetaColumn column in _metaTable.GetScaffoldColumns(mode, containerType)) {
fields.Add(CreateField(column, containerType, mode));
}
return fields;
}
protected virtual DynamicField CreateField(MetaColumn column, ContainerType containerType, DataBoundControlMode mode) {
string headerText = (containerType == ContainerType.List ? column.ShortDisplayName : column.DisplayName);
var field = new DynamicField() {
DataField = column.Name,
HeaderText = headerText
};
// Turn wrapping off by default so that error messages don't show up on the next line.
field.ItemStyle.Wrap = false;
return field;
}
internal static ContainerType GetControlContainerType(Control control) {
if (control is IDataBoundListControl || control is Repeater) {
return ContainerType.List;
} else if (control is IDataBoundItemControl) {
return ContainerType.Item;
}
return ContainerType.List;
}
internal static DataBoundControlMode GetMode(Control control) {
// Only item controls have distinct modes
IDataBoundItemControl itemControl = control as IDataBoundItemControl;
if (itemControl != null && GetControlContainerType(control) != ContainerType.List) {
return itemControl.Mode;
}
return DataBoundControlMode.ReadOnly;
}
}
}

View File

@ -0,0 +1,435 @@
namespace System.Web.DynamicData {
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Web.Resources;
using System.Web.UI;
using System.Collections.Generic;
using System.Reflection;
using System.Web.UI.WebControls;
using System.Drawing;
using System.Diagnostics;
/// <summary>
/// Control used to render Dynamic Data driven UI
/// </summary>
[ToolboxBitmap(typeof(DynamicControl), "DynamicControl.bmp")]
public class DynamicControl : Control, IAttributeAccessor, IFieldTemplateHost, IFieldFormattingOptions {
private bool _customConvertEmptyStringToNullSet;
private bool _customApplyFormatInEditModeSet;
private MetaTable _table;
private IMetaTable _iMetaTable;
private IDictionary<string, string> _attributes;
private IMetaColumn _iMetaColumn;
private MetaColumn _column;
/// <summary>
/// </summary>
public DynamicControl() {
}
internal DynamicControl(IMetaTable table) {
_iMetaTable = table;
}
internal DynamicControl(IMetaColumn column) {
_iMetaColumn = column;
}
/// <param name="mode">The mode that the associated field template should be in (readonly, edit, insert)</param>
public DynamicControl(DataBoundControlMode mode) {
Mode = mode;
}
/// <summary>
/// The name of the column that this control handles
/// </summary>
[
Category("Data"),
DefaultValue(""),
ResourceDescription("DynamicControlFieldCommon_DataField")
]
public string DataField {
get {
object o = ViewState["DataField"];
return ((o == null) ? String.Empty : (string)o);
}
set {
if (!String.Equals(value, ViewState["DataField"])) {
ViewState["DataField"] = value;
}
}
}
/// <summary>
/// The MetaColumn that this control is working with
/// </summary>
[Browsable(false)]
public MetaColumn Column {
get {
return _column;
}
set {
_column = value;
_iMetaColumn = value;
}
}
/// <summary>
/// The UIHint used by this control to locate the proper field template
/// </summary>
[
Category("Behavior"),
DefaultValue(""),
ResourceDescription("DynamicControlFieldCommon_UIHint")
]
public virtual string UIHint {
get {
object o = ViewState["UIHint"];
return ((o == null) ? String.Empty : (string)o);
}
set {
ViewState["UIHint"] = value;
}
}
/// <summary>
/// Gets or sets the CSS class property of the DynamicControl
/// </summary>
[
Category("Appearance"),
DefaultValue(""),
CssClassProperty()
]
public virtual string CssClass {
get {
object o = ViewState["CssClass"];
return ((o == null) ? String.Empty : (string)o);
}
set {
ViewState["CssClass"] = value;
}
}
/// <summary>
/// The MetaTable that this control is associated with
/// </summary>
[Browsable(false)]
public virtual MetaTable Table {
get {
if (_table == null) {
_table = this.FindMetaTable();
if (_table == null) {
throw new Exception(String.Format(CultureInfo.CurrentCulture,
DynamicDataResources.DynamicControl_ControlNeedsToExistInADataControlUsingDynamicDataSource));
}
}
return _table;
}
}
private IMetaTable IMetaTable {
get {
return _iMetaTable ?? Table;
}
}
private IMetaColumn IMetaColumn {
get {
return _iMetaColumn;
}
set {
_iMetaColumn = value;
_column = value as MetaColumn;
}
}
/// <summary>
/// The rendering mode: readonly, edit or insert
/// </summary>
[
DefaultValue(DataBoundControlMode.ReadOnly),
Category("Behavior"),
ResourceDescription("DynamicField_Mode")
]
public DataBoundControlMode Mode {
get {
object o = ViewState["Mode"];
return ((o == null) ? DataBoundControlMode.ReadOnly : (DataBoundControlMode)o);
}
set {
ViewState["Mode"] = value;
}
}
/// <summary>
/// The validation group that the field template need to be in
/// </summary>
[
Category("Behavior"),
DefaultValue(""),
Themeable(false),
ResourceDescription("DynamicControlFieldCommon_ValidationGroup")
]
public virtual string ValidationGroup {
get {
object o = ViewState["ValidationGroup"];
return ((o == null) ? String.Empty : (string)o);
}
set {
ViewState["ValidationGroup"] = value;
}
}
internal Control CreateControl() {
Debug.Assert(FieldTemplate == null);
FieldTemplate = (Control)IMetaColumn.Model.FieldTemplateFactory.CreateFieldTemplate(Column, Mode, UIHint);
if (FieldTemplate != null) {
((IFieldTemplate)FieldTemplate).SetHost(this);
// If we got some extra attributes declared on the tag, assign them to the user control if it has matching properties
if (_attributes != null) {
var ucType = FieldTemplate.GetType();
foreach (var entry in _attributes) {
// Look for a public property by that name on th user control
var propInfo = ucType.GetProperty(entry.Key, BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
if (propInfo != null) {
// Convert the value to the type of the property and set it
var value = PropertyConverter.ObjectFromString(propInfo.PropertyType, propInfo, entry.Value);
propInfo.SetValue(FieldTemplate, value, null);
}
}
}
// Give it the column name as its ID, unless there is already a control by that name
string id = GetControlIDFromColumnName(IMetaColumn.Name);
if (FindControl(id) == null)
FieldTemplate.ID = id;
}
return FieldTemplate;
}
/// <summary>
/// See base class documentation
/// </summary>
[SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers")]
protected override void OnInit(EventArgs e) {
base.OnInit(e);
// Don't do anything in Design mode
if (DesignMode)
return;
ResolveColumn();
FieldTemplate = CreateControl();
// Add it to the tree
if (FieldTemplate != null) {
Controls.Add(FieldTemplate);
}
}
/// <summary>
/// Renders the underlying field template control. If this DynamicControl's CssClass tag is defined,
/// the underlying control's output will be wrapped in a span tag with that css class applied.
/// </summary>
/// <param name="writer"></param>
protected override void Render(HtmlTextWriter writer) {
// In Design mode, simply render the string Databound. Ideally, we'd do something fancier
// that takes into account the type of the column. But at lesat, rendering *something* makes
// the design layout look reasonable
if (DesignMode) {
writer.Write(DynamicDataResources.DynamicControlDesignRender);
return;
}
// If there is a CSS class, output it in a span tag. Otherwise, don't use any extra tag.
if (!String.IsNullOrEmpty(CssClass)) {
writer.AddAttribute(HtmlTextWriterAttribute.Class, CssClass);
writer.RenderBeginTag(HtmlTextWriterTag.Span);
base.Render(writer);
writer.RenderEndTag();
}
else {
base.Render(writer);
}
}
internal void ResolveColumn() {
if (IMetaColumn == null) {
if (String.IsNullOrEmpty(DataField)) {
throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture,
DynamicDataResources.DynamicControl_ControlMustHaveDateFieldAttribute, GetType().Name, ID));
}
IMetaColumn = IMetaTable.GetColumn(DataField);
// Try to get them various settings from the model, unless they were explicitely
// specified on the control
if (String.IsNullOrEmpty(UIHint)) {
UIHint = IMetaColumn.UIHint;
}
if (String.IsNullOrEmpty(DataFormatString)) {
DataFormatString = IMetaColumn.DataFormatString;
}
if (String.IsNullOrEmpty(NullDisplayText)) {
NullDisplayText = IMetaColumn.NullDisplayText;
}
if (!_customConvertEmptyStringToNullSet) {
ConvertEmptyStringToNull = IMetaColumn.ConvertEmptyStringToNull;
}
if (!_customApplyFormatInEditModeSet) {
ApplyFormatInEditMode = IMetaColumn.ApplyFormatInEditMode;
}
if (ViewState["HtmlEncode"] == null) {
HtmlEncode = IMetaColumn.HtmlEncode;
}
}
}
/// <summary>
/// The Field Template that was created for this control
/// </summary>
[Browsable(false)]
public Control FieldTemplate { get; private set; }
internal static string GetControlIDFromColumnName(string columnName) {
return "__" + columnName;
}
internal void SetAttributes(IDictionary<string, string> attributes) {
_attributes = attributes;
}
#region IAttributeAccessor Members
/// <summary>
/// See IAttributeAccessor
/// </summary>
public string GetAttribute(string key) {
if (_attributes == null)
return String.Empty;
return _attributes[key];
}
/// <summary>
/// See IAttributeAccessor
/// </summary>
public void SetAttribute(string key, string value) {
if (_attributes == null) {
_attributes = new Dictionary<string, string>();
}
_attributes[key] = value;
}
#endregion
#region IFieldTemplateHost Members
IFieldFormattingOptions IFieldTemplateHost.FormattingOptions {
get { return this; }
}
#endregion
#region IFieldFormattingOptions Members
/// <summary>
/// See IFieldFormattingOptions
/// </summary>
[
Category("Behavior"),
DefaultValue(false),
ResourceDescription("DynamicControlFieldCommon_ConvertEmptyStringToNull")
]
public bool ConvertEmptyStringToNull {
get {
object o = ViewState["ConvertEmptyStringToNull"];
return (o == null ? false : (bool)o);
}
set {
_customConvertEmptyStringToNullSet = true;
ViewState["ConvertEmptyStringToNull"] = value;
}
}
/// <summary>
/// See IFieldFormattingOptions
/// </summary>
[
Category("Behavior"),
DefaultValue(false),
ResourceDescription("DynamicControlFieldCommon_ApplyFormatInEditMode")
]
public bool ApplyFormatInEditMode {
get {
object o = ViewState["ApplyFormatInEditMode"];
return (o == null ? false : (bool)o);
}
set {
_customApplyFormatInEditModeSet = true;
ViewState["ApplyFormatInEditMode"] = value;
}
}
/// <summary>
/// See IFieldFormattingOptions
/// </summary>
[
Category("Data"),
DefaultValue(""),
ResourceDescription("DynamicControlFieldCommon_DataFormatString")
]
public string DataFormatString {
get {
object o = ViewState["DataFormatString"];
return (o == null ? String.Empty : (string)o);
}
set {
ViewState["DataFormatString"] = value;
}
}
/// <summary>
/// See IFieldFormattingOptions
/// </summary>
[
Category("Behavior"),
DefaultValue(true),
ResourceDescription("DynamicControlFieldCommon_HtmlEncode")
]
public bool HtmlEncode {
get {
object o = ViewState["HtmlEncode"];
return (o == null ? true : (bool)o);
}
set {
ViewState["HtmlEncode"] = value;
}
}
/// <summary>
/// See IFieldFormattingOptions
/// </summary>
[
Category("Behavior"),
DefaultValue(""),
ResourceDescription("DynamicControlFieldCommon_NullDisplayText")
]
public string NullDisplayText {
get {
object o = ViewState["NullDisplayText"];
return (o == null ? String.Empty : (string)o);
}
set {
ViewState["NullDisplayText"] = value;
}
}
#endregion
}
}

View File

@ -0,0 +1,166 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Web.DynamicData.Util;
using System.Web.Resources;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace System.Web.DynamicData {
/// <summary>
/// DynamicControlParameter is similar to ControlParameter, but understainds higher level concepts. e.g. in a
/// master-details scenario using a GridView and DetailsView, you only need to point the DetailsView's datasource
/// to the GridView (using a DynamicControlParameter), and it does the right thing. This works even for
/// multi-part primary keys
/// </summary>
public class DynamicControlParameter : Parameter, IWhereParametersProvider {
/// <summary>
/// </summary>
public DynamicControlParameter() { }
/// <summary>
/// </summary>
public DynamicControlParameter(string controlId) { ControlId = controlId; }
/// <summary>
/// The ID of the control from which the parameter gets its data
/// </summary>
public string ControlId { get; set; }
/// <summary>
/// See IWhereParametersProvider.GetWhereParameters
/// </summary>
public virtual IEnumerable<Parameter> GetWhereParameters(IDynamicDataSource dataSource) {
Debug.Assert(dataSource != null);
// Find the control that the ControlParameter uses
Control control = Misc.FindControl((Control)dataSource, ControlId);
if (control == null) {
throw new InvalidOperationException(String.Format(
CultureInfo.CurrentCulture, DynamicDataResources.DynamicControlParameter_DynamicDataSourceControlNotFound, ControlId));
}
// If the control is itself a parameter provider, delegate to it
var whereParametersProvider = control as IWhereParametersProvider;
if (whereParametersProvider != null) {
return whereParametersProvider.GetWhereParameters(dataSource);
}
IControlParameterTarget paramTarget = DynamicDataManager.GetControlParameterTarget(control);
if (paramTarget == null) {
throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture,
DynamicDataResources.DynamicControlParameter_DynamicDataSourceControlCannotBeUsedAsParent, ControlId));
}
string columnName = Name;
MetaColumn column = null;
MetaTable table = MetaTableHelper.GetTableWithFullFallback(dataSource, HttpContext.Current.ToWrapper());
if (!String.IsNullOrEmpty(columnName)) {
column = table.GetColumn(columnName);
}
else {
// There was no Name attribute telling us what field to filter, but maybe
// the control given us data has that info
column = paramTarget.FilteredColumn;
}
if (column == null) {
// If there is no specific column, we're setting the primary key
if (paramTarget.Table != table) {
throw new Exception(String.Format(CultureInfo.CurrentCulture,
DynamicDataResources.DynamicControlParameter_InvalidPK,
ControlId, paramTarget.Table, table.Name));
}
return GetPrimaryKeyControlWhereParameters(control, paramTarget);
}
else if (column is MetaForeignKeyColumn) {
return GetForeignKeyControlWhereParameters(control, paramTarget, (MetaForeignKeyColumn)column);
}
return GetPropertyControlWhereParameters(control, paramTarget, column);
}
private IEnumerable<Parameter> GetPropertyControlWhereParameters(Control control,
IControlParameterTarget paramTarget, MetaColumn column) {
ControlParameter controlParameter = new ControlParameter() {
Name = column.Name,
ControlID = control.UniqueID,
PropertyName = paramTarget.GetPropertyNameExpression(column.Name)
};
DataSourceUtil.SetParameterTypeCodeAndDbType(controlParameter, column);
yield return controlParameter;
}
private IEnumerable<Parameter> GetPrimaryKeyControlWhereParameters(Control control,
IControlParameterTarget paramTarget) {
MetaTable parentTable = paramTarget.Table;
if (parentTable != null) {
// For each PK column in the table, we need to create a ControlParameter
foreach (var keyColumn in parentTable.PrimaryKeyColumns) {
var controlParameter = new ControlParameter() {
Name = keyColumn.Name,
ControlID = control.UniqueID,
PropertyName = paramTarget.GetPropertyNameExpression(keyColumn.Name)
};
DataSourceUtil.SetParameterTypeCodeAndDbType(controlParameter, keyColumn);
yield return controlParameter;
}
}
}
private IEnumerable<Parameter> GetForeignKeyControlWhereParameters(Control control,
IControlParameterTarget paramTarget, MetaForeignKeyColumn column) {
MetaTable parentTable = paramTarget.Table;
if (parentTable != null) {
string namePrefix = String.Empty;
// Make sure the data types match
if (column.ColumnType != parentTable.EntityType) {
throw new Exception(String.Format(CultureInfo.CurrentCulture,
DynamicDataResources.DynamicControlParameter_DynamicDataSourceColumnNotCompatibleWithTable,
column.DisplayName, parentTable.Name));
}
// For each underlying FK, we need to create a ControlParameter
Debug.Assert(column.ForeignKeyNames.Count == parentTable.PrimaryKeyColumns.Count);
int index = 0;
foreach (var fkName in column.ForeignKeyNames) {
MetaColumn parentTablePKColumn = parentTable.PrimaryKeyColumns[index++];
var controlParameter = new ControlParameter() {
Name = fkName,
ControlID = control.UniqueID,
PropertyName = paramTarget.GetPropertyNameExpression(parentTablePKColumn.Name)
};
DataSourceUtil.SetParameterTypeCodeAndDbType(controlParameter, parentTablePKColumn);
yield return controlParameter;
}
}
}
/// <summary>
/// same as base
/// </summary>
/// <param name="context"></param>
/// <param name="control"></param>
/// <returns></returns>
protected override object Evaluate(HttpContext context, Control control) {
// If this gets called, it means we never had a chance to expand the parameter. Give an error
// telling the user to use a DynamicDataManager
throw new InvalidOperationException(String.Format(
CultureInfo.CurrentCulture, DynamicDataResources.DynamicParameter_NeedExpansion, typeof(DynamicControlParameter).Name));
}
}
}

View File

@ -0,0 +1,398 @@
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;
/// <summary>
/// Extension methods used by DynamicData
/// </summary>
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<string, object> 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<string, object> GetDefaultValues(this IDataSource dataSource) {
return GetDefaultValues(dataSource, new HttpContextWrapper(HttpContext.Current));
}
public static IDictionary<string, object> 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<object, MappingInfo> mapping = MetaTableHelper.GetMapping(context);
MappingInfo mappingInfo;
if (mapping.TryGetValue(control, out mappingInfo)) {
return mappingInfo.DefaultValueMapping;
}
return null;
}
internal static IDictionary<string, object> 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<string, object> 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);
}
}
/// <summary>
/// Return the MetaTable association with a datasource
/// </summary>
[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());
}
/// <summary>
/// Expand Dynamic where parameter (e.g. DynamicControlParameter, DynamicQueryStringParameter) into
/// 'regular' parameters that the datasource can understand
/// </summary>
/// <param name="dataSource">The datasource which Where parameters need to be expanded</param>
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<Parameter> newParameters = provider.GetWhereParameters(dataSource);
foreach (Parameter newParameter in newParameters) {
ExpandWhereParameter(dataSource, newParameter);
}
}
}
/// <summary>
/// Find the containing data control, and return the data source it points to
/// </summary>
public static IDynamicDataSource FindDataSourceControl(this Control current) {
return DataControlHelper.FindDataSourceControl(current);
}
/// <summary>
/// Find the containing data control, and return the MetaTable associated with it, if any
/// </summary>
public static MetaTable FindMetaTable(this Control current) {
return MetaTableHelper.FindMetaTable(current, HttpContext.Current.ToWrapper());
}
/// <summary>
/// Find the field template for a column within the current naming container
/// </summary>
public static Control FindFieldTemplate(this Control control, string columnName) {
return control.FindControl(DynamicControl.GetControlIDFromColumnName(columnName));
}
/// <summary>
/// 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.
/// </summary>
[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;
}
}
}
/// <summary>
/// Set the DataLoadOptions on a Linq To Sql datasource to force all the FK entities
/// to be directly loaded.
/// </summary>
/// <param name="dataSource">The data source for which we want to preload FKs</param>
/// <param name="rowType">The type of the entities returned by the data source</param>
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<TEntity>(this LinqDataSource dataSource) {
LoadWithForeignKeys(dataSource, typeof(TEntity));
}
/// <summary>
/// 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
/// </summary>
/// <param name="fieldValue">The value that should be formatted</param>
/// <param name="formattingOptions">The IFieldFormattingOptions to use. This is useful when using options different from the column's</param>
/// <returns>the formatted value</returns>
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;
}
/// <summary>
/// Similar to FormatValue, but the string is to be used when the field is in edit mode
/// </summary>
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;
}
/// <summary>
/// Return either the input value or null based on ConvertEmptyStringToNull and NullDisplayText
/// </summary>
/// <param name="formattingOptions">the formatting options object</param>
/// <param name="value">The input value</param>
/// <returns>The converted value</returns>
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;
}
/// <summary>
/// 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.
/// </summary>
/// <param name="column"></param>
/// <returns></returns>
[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<EnumDataTypeAttribute, Type>(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;
}
}
}

View File

@ -0,0 +1,261 @@
namespace System.Web.DynamicData {
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using System.Globalization;
using System.Security.Permissions;
using System.Web.Resources;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.DynamicData.Util;
using System.Data.Objects;
using IDataBoundControlInterface = System.Web.UI.WebControls.IDataBoundControl;
/// <summary>
/// Adds behavior to certain control to make them work with Dynamic Data
/// </summary>
[NonVisualControl()]
[ParseChildren(true)]
[PersistChildren(false)]
[ToolboxBitmap(typeof(DynamicDataManager), "DynamicDataManager.bmp")]
[Designer("System.Web.DynamicData.Design.DynamicDataManagerDesigner, " + AssemblyRef.SystemWebDynamicDataDesign)]
public class DynamicDataManager : Control {
private DataControlReferenceCollection _dataControls;
// Key is used as the set of registered data source controls. Value is ignored.
private Dictionary<IDynamicDataSource, object> _dataSources = new Dictionary<IDynamicDataSource, object>();
/// <summary>
/// Causes foreign entities to be loaded as well setting the proper DataLoadOptions.
/// Only works with Linq To Sql.
/// </summary>
[
Category("Behavior"),
DefaultValue(false),
ResourceDescription("DynamicDataManager_AutoLoadForeignKeys")
]
public bool AutoLoadForeignKeys {
get;
set;
}
[
Browsable(false),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
EditorBrowsable(EditorBrowsableState.Never)
]
public override string ClientID {
get {
return base.ClientID;
}
}
[
Browsable(false),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
EditorBrowsable(EditorBrowsableState.Never)
]
public override ClientIDMode ClientIDMode {
get {
return base.ClientIDMode;
}
set {
throw new NotImplementedException();
}
}
[
Category("Behavior"),
DefaultValue(null),
PersistenceMode(PersistenceMode.InnerProperty),
MergableProperty(false),
]
public DataControlReferenceCollection DataControls {
get {
if (_dataControls == null) {
_dataControls = new DataControlReferenceCollection(this);
}
return _dataControls;
}
}
/// <summary>
/// See base class documentation
/// </summary>
[
Browsable(false),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
EditorBrowsable(EditorBrowsableState.Never)
]
public override bool Visible {
get {
return base.Visible;
}
set {
throw new NotImplementedException();
}
}
/// <summary>
/// See base class documentation
/// </summary>
[SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers")]
protected override void OnInit(EventArgs e) {
base.OnInit(e);
// Initialize the collection
DataControls.Initialize();
// Subscribe to the Page's Init to register the controls set in the DataControls collection
Page.Init += OnPageInit;
}
private void OnPageInit(object sender, EventArgs e) {
foreach (DataControlReference controlReference in DataControls) {
Control targetControl = Misc.FindControl(this, controlReference.ControlID);
if (targetControl == null) {
throw new InvalidOperationException(
String.Format(CultureInfo.CurrentCulture,
DynamicDataResources.DynamicDataManager_ControlNotFound,
controlReference.ControlID));
}
RegisterControl(targetControl);
}
}
/// <summary>
/// See base class documentation
/// </summary>
[SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers")]
protected override void OnLoad(EventArgs e) {
base.OnLoad(e);
// Go through all the registered data sources
foreach (IDynamicDataSource dataSource in _dataSources.Keys) {
// Expand any dynamic where parameters that they may use
dataSource.ExpandDynamicWhereParameters();
}
}
/// <summary>
/// Register a data control to give it Dynamic Data behavior
/// </summary>
/// <param name="control"></param>
public void RegisterControl(Control control) {
RegisterControl(control, false);
}
/// <summary>
/// Register a data control to give it Dynamic Data behavior
/// </summary>
/// <param name="setSelectionFromUrl">When true, if a primary key is found in the route values
/// (typically on the query string), it will get be set as the selected item. This only applies
/// to list controls.</param>
public void RegisterControl(Control control, bool setSelectionFromUrl) {
//
if (DesignMode) {
return;
}
IDataBoundControlInterface dataBoundControl = DataControlHelper.GetDataBoundControl(control, true /*failIfNotFound*/);
// If we can't get an associated IDynamicDataSource, don't do anything
IDynamicDataSource dataSource = dataBoundControl.DataSourceObject as IDynamicDataSource;
if (dataSource == null) {
return;
}
// If we can't get a MetaTable from the data source, don't do anything
MetaTable table = MetaTableHelper.GetTableWithFullFallback(dataSource, Context.ToWrapper());
// Save the datasource so we can process its parameters in OnLoad. The value we set is irrelevant
_dataSources[dataSource] = null;
((INamingContainer)control).SetMetaTable(table);
BaseDataBoundControl baseDataBoundControl = control as BaseDataBoundControl;
if (baseDataBoundControl != null) {
EnablePersistedSelection(baseDataBoundControl, table);
}
RegisterControlInternal(dataBoundControl, dataSource, table, setSelectionFromUrl, Page.IsPostBack);
}
internal static void EnablePersistedSelection(BaseDataBoundControl baseDataBoundControl, IMetaTable table) {
Debug.Assert(baseDataBoundControl != null, "NULL!");
// Make the persisted selection [....] up with the selected index if possible
if (!table.IsReadOnly) {
DynamicDataExtensions.EnablePersistedSelectionInternal(baseDataBoundControl);
}
}
internal void RegisterControlInternal(IDataBoundControlInterface dataBoundControl, IDynamicDataSource dataSource, IMetaTable table, bool setSelectionFromUrl, bool isPostBack) {
// Set the auto field generator (for controls that support it - GridView and DetailsView)
IFieldControl fieldControl = dataBoundControl as IFieldControl;
if (fieldControl != null) {
fieldControl.FieldsGenerator = new DefaultAutoFieldGenerator(table);
}
var linqDataSource = dataSource as LinqDataSource;
var entityDataSource = dataSource as EntityDataSource;
// If the context type is not set, we need to set it
if (dataSource.ContextType == null) {
dataSource.ContextType = table.DataContextType;
// If it's a LinqDataSurce, register for ContextCreating so the context gets created using the correct ctor
// Ideally, this would work with other datasource, but we just don't have the right abstraction
if (linqDataSource != null) {
linqDataSource.ContextCreating += delegate(object sender, LinqDataSourceContextEventArgs e) {
e.ObjectInstance = table.CreateContext();
};
}
if (entityDataSource != null) {
entityDataSource.ContextCreating += delegate(object sender, EntityDataSourceContextCreatingEventArgs e) {
e.Context = (ObjectContext)table.CreateContext();
};
}
}
// If the datasource doesn't have an EntitySetName (aka TableName), set it from the meta table
if (String.IsNullOrEmpty(dataSource.EntitySetName)) {
dataSource.EntitySetName = table.DataContextPropertyName;
}
// If there is no Where clause, turn on auto generate
if (String.IsNullOrEmpty(dataSource.Where)) {
dataSource.AutoGenerateWhereClause = true;
}
// If it's a LinqDataSource and the flag is set, pre load the foreign keys
if (AutoLoadForeignKeys && linqDataSource != null) {
linqDataSource.LoadWithForeignKeys(table.EntityType);
}
if (!isPostBack) {
if (table.HasPrimaryKey) {
dataBoundControl.DataKeyNames = table.PrimaryKeyNames;
// Set the virtual selection from the URL if needed
var dataKeySelector = dataBoundControl as IPersistedSelector;
if (dataKeySelector != null && setSelectionFromUrl) {
DataKey dataKey = table.GetDataKeyFromRoute();
if (dataKey != null) {
dataKeySelector.DataKey = dataKey;
}
}
}
}
}
internal static IControlParameterTarget GetControlParameterTarget(Control control) {
return (control as IControlParameterTarget) ?? new DataBoundControlParameterTarget(control);
}
}
}

View File

@ -0,0 +1,200 @@
using System.Web.Routing;
using System.Diagnostics;
namespace System.Web.DynamicData {
/// <summary>
/// Route used by Dynamic Data
/// </summary>
public class DynamicDataRoute : Route {
internal const string ActionToken = "Action";
internal const string TableToken = "Table";
internal const string ModelToken = "__Model";
private MetaModel _model;
private volatile bool _initialized;
private object _initializationLock = new object();
/// <summary>
/// Construct a DynamicDataRoute
/// </summary>
/// <param name="url">url passed to the base ctor</param>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings",
Justification = "This is a URL template with special characters, not just a regular valid URL.")]
public DynamicDataRoute(string url)
: base(url, new DynamicDataRouteHandler()) {
}
/// <summary>
/// Name of the table that this route applies to. Can be omitted.
/// </summary>
public string Table { get; set; }
/// <summary>
/// Action that this route applies to. Can be omitted.
/// </summary>
public string Action { get; set; }
/// <summary>
/// The ViewName is the name of the page used to handle the request. If omitted, it defaults to the Action name.
/// </summary>
public string ViewName { get; set; }
/// <summary>
/// The MetaModel that this route applies to
/// </summary>
public MetaModel Model {
get { return _model ?? MetaModel.Default; }
set { _model = value; }
}
// Make sure that if the Table or Action properties were used, they get added to
// the Defaults dictionary
private void EnsureRouteInitialize() {
if (!_initialized) {
lock (_initializationLock) {
if (!_initialized) {
// Give the model to the handler
Debug.Assert(Model != null);
RouteHandler.Model = Model;
// If neither was specified, we don't need to do anything
if (Table == null && Action == null)
return;
// If we don't already have a Defaults dictionary, create one
if (Defaults == null)
Defaults = new RouteValueDictionary();
if (Table != null) {
// Try to get the table just to cause a failure if it doesn't exist
var metaTable = Model.GetTable(Table);
Defaults[TableToken] = Table;
}
if (Action != null)
Defaults[ActionToken] = Action;
_initialized = true;
}
}
}
}
/// <summary>
/// See base class documentation
/// </summary>
public override RouteData GetRouteData(HttpContextBase httpContext) {
EnsureRouteInitialize();
// Try to get the route data for this route
RouteData routeData = base.GetRouteData(httpContext);
// If not, we're done
if (routeData == null) {
return null;
}
// Add all the query string values to the RouteData
//
AddQueryStringParamsToRouteData(httpContext, routeData);
// Check if the route values match an existing table and if they can be served by a scaffolded or custom page
if (!VerifyRouteValues(routeData.Values))
return null;
return routeData;
}
internal static void AddQueryStringParamsToRouteData(HttpContextBase httpContext, RouteData routeData) {
foreach (string key in httpContext.Request.QueryString) {
// Don't overwrite existing items
if (!routeData.Values.ContainsKey(key)) {
routeData.Values[key] = httpContext.Request.QueryString[key];
}
}
}
/// <summary>
/// See base class documentation
/// </summary>
public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values) {
EnsureRouteInitialize();
// Check if the route values include a MetaModel
object modelObject;
if (values.TryGetValue(ModelToken, out modelObject)) {
var model = modelObject as MetaModel;
if (model != null) {
// If it's different from the one for this route, fail the route matching
if (modelObject != Model)
return null;
// It has the right model, so we want to continue. But we first need to
// remove this token so it doesn't affect the path
values.Remove(ModelToken);
}
}
// Call the base to try to generate a path from this route
VirtualPathData virtualPathData = base.GetVirtualPath(requestContext, values);
// If not, we're done
if (virtualPathData == null)
return null;
// Check if the route values match an existing table and if they can be served by a scaffolded or custom page
if (VerifyRouteValues(values)) {
return virtualPathData;
}
else {
return null;
}
}
private bool VerifyRouteValues(RouteValueDictionary values) {
// Get the MetaTable and action. If either is missing, return false to skip this route
object tableNameObject, actionObject;
if (!values.TryGetValue(TableToken, out tableNameObject) || !values.TryGetValue(ActionToken, out actionObject)) {
return false;
}
MetaTable table;
// If no table by such name is available, return false to move on to next route.
if (!Model.TryGetTable((string)tableNameObject, out table)) {
return false;
}
// Check if n Page can be accessed for the table/action (either scaffold or custom).
// If not, return false so that this route is not used and the search goes on.
return RouteHandler.CreateHandler(this, table, (string)actionObject) != null;
}
/// <summary>
/// Extract the MetaTable from the RouteData. Fails if it can't find it
/// </summary>
/// <param name="routeData">The route data</param>
/// <returns>The found MetaTable</returns>
public MetaTable GetTableFromRouteData(RouteData routeData) {
string tableName = routeData.GetRequiredString(TableToken);
return Model.GetTable(tableName);
}
/// <summary>
/// Extract the Action from the RouteData. Fails if it can't find it
/// </summary>
/// <param name="routeData">The route data</param>
/// <returns>The found Action</returns>
public string GetActionFromRouteData(RouteData routeData) {
return routeData.GetRequiredString(ActionToken);
}
/// <summary>
/// Strongly typed version of Route.RouteHandler for convenience
/// </summary>
public new DynamicDataRouteHandler RouteHandler {
get { return (DynamicDataRouteHandler)base.RouteHandler; }
set { base.RouteHandler = value; }
}
}
}

View File

@ -0,0 +1,226 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Web.Compilation;
using System.Web.Hosting;
using System.Web.Routing;
using System.Web.UI;
namespace System.Web.DynamicData {
/// <summary>
/// Route handler used for Dynamic Data
/// </summary>
public class DynamicDataRouteHandler : IRouteHandler {
private static object s_requestContextKey = new object();
private static object s_metaTableKey = new object();
private object _requestItemsKey = new object();
/// <summary>
/// ctor
/// </summary>
public DynamicDataRouteHandler() {
VirtualPathProvider = HostingEnvironment.VirtualPathProvider;
CreateHandlerCallback = delegate(string s) {
return (Page)BuildManager.CreateInstanceFromVirtualPath(s, typeof(Page));
};
}
/// <summary>
/// The MetaModel that the handler is associated with
/// </summary>
public MetaModel Model { get; internal set; }
// the following properties are for mocking purposes
internal VirtualPathProvider VirtualPathProvider { get; set; }
private HttpContextBase _context;
internal HttpContextBase HttpContext {
get {
return _context ?? new HttpContextWrapper(System.Web.HttpContext.Current);
}
set { _context = value; }
}
internal Func<string, IHttpHandler> CreateHandlerCallback { get; set; }
/// <summary>
/// Create a handler to process a Dynamic Data request
/// </summary>
/// <param name="route">The Route that was matched</param>
/// <param name="table">The MetaTable found in the route</param>
/// <param name="action">The Action found in the route</param>
/// <returns></returns>
public virtual IHttpHandler CreateHandler(DynamicDataRoute route, MetaTable table, string action) {
// First, get the path to the page (could be custom, shared, or null)
string virtualPath = GetPageVirtualPath(route, table, action);
if (virtualPath != null) {
// Gets called only for custom pages that we know exist or templates that may or may not
// exist. This method will throw if virtualPath does not exist, which is fine for templates
// but is not fine for custom pages.
return CreateHandlerCallback(virtualPath);
} else {
// This should only occur in the event that scaffolding is disabled and the custom page
// virtual path does not exist.
return null;
}
}
private string GetPageVirtualPath(DynamicDataRoute route, MetaTable table, string action) {
long cacheKey = Misc.CombineHashCodes(table, route.ViewName ?? action);
Dictionary<long, string> virtualPathCache = GetVirtualPathCache();
string virtualPath;
if (!virtualPathCache.TryGetValue(cacheKey, out virtualPath)) {
virtualPath = GetPageVirtualPathNoCache(route, table, action);
lock (virtualPathCache) {
virtualPathCache[cacheKey] = virtualPath;
}
}
return virtualPath;
}
private Dictionary<long, string> GetVirtualPathCache() {
var httpContext = HttpContext;
Dictionary<long, string> virtualPathCache = (Dictionary<long, string>)httpContext.Items[_requestItemsKey];
if (virtualPathCache == null) {
virtualPathCache = new Dictionary<long, string>();
httpContext.Items[_requestItemsKey] = virtualPathCache;
}
return virtualPathCache;
}
private string GetPageVirtualPathNoCache(DynamicDataRoute route, MetaTable table, string action) {
// The view name defaults to the action
string viewName = route.ViewName ?? action;
// First, get the path to the custom page
string customPageVirtualPath = GetCustomPageVirtualPath(table, viewName);
if (VirtualPathProvider.FileExists(customPageVirtualPath)) {
return customPageVirtualPath;
} else {
if (table.Scaffold) {
// If it doesn't exist, try the scaffolded page, but only if scaffolding is enabled on this table
return GetScaffoldPageVirtualPath(table, viewName);
} else {
// If scaffolding is disabled, null the path so BuildManager doesn't get called.
return null;
}
}
}
/// <summary>
/// Build the path to a custom page. By default, it looks like ~/DynamicData/CustomPages/[tablename]/[viewname].aspx
/// </summary>
/// <param name="table">The MetaTable that the page is for</param>
/// <param name="viewName">The view name</param>
/// <returns></returns>
protected virtual string GetCustomPageVirtualPath(MetaTable table, string viewName) {
string pathPattern = "{0}CustomPages/{1}/{2}.aspx";
return String.Format(CultureInfo.InvariantCulture, pathPattern, Model.DynamicDataFolderVirtualPath, table.Name, viewName);
}
/// <summary>
/// Build the path to a page template. By default, it looks like ~/DynamicData/PageTemplates/[tablename]/[viewname].aspx
/// </summary>
/// <param name="table">The MetaTable that the page is for</param>
/// <param name="viewName">The view name</param>
/// <returns></returns>
protected virtual string GetScaffoldPageVirtualPath(MetaTable table, string viewName) {
string pathPattern = "{0}PageTemplates/{1}.aspx";
return String.Format(CultureInfo.InvariantCulture, pathPattern, Model.DynamicDataFolderVirtualPath, viewName);
}
/// <summary>
/// Return the RequestContext for this request. A new one is created if needed (can happen if the current request
/// is not a Dynamic Data request)
/// </summary>
/// <param name="httpContext">The current HttpContext</param>
/// <returns>The RequestContext</returns>
public static RequestContext GetRequestContext(HttpContext httpContext) {
if (httpContext == null) {
throw new ArgumentNullException("httpContext");
}
return GetRequestContext(new HttpContextWrapper(httpContext));
}
internal static RequestContext GetRequestContext(HttpContextBase httpContext) {
Debug.Assert(httpContext != null);
// Look for the RequestContext in the HttpContext
var requestContext = httpContext.Items[s_requestContextKey] as RequestContext;
// If the current request didn't go through the routing engine (e.g. normal page),
// there won't be a RequestContext. If so, create a new one and save it
if (requestContext == null) {
var routeData = new RouteData();
requestContext = new RequestContext(httpContext, routeData);
// Add the query string params to the route data. This allows non routed pages to support filtering.
DynamicDataRoute.AddQueryStringParamsToRouteData(httpContext, routeData);
httpContext.Items[s_requestContextKey] = requestContext;
}
return requestContext;
}
/// <summary>
/// The MetaTable associated with the current HttpRequest. Can be null for non-Dynamic Data requests.
/// </summary>
/// <param name="httpContext">The current HttpContext</param>
public static MetaTable GetRequestMetaTable(HttpContext httpContext) {
if (httpContext == null) {
throw new ArgumentNullException("httpContext");
}
return GetRequestMetaTable(new HttpContextWrapper(httpContext));
}
internal static MetaTable GetRequestMetaTable(HttpContextBase httpContext) {
Debug.Assert(httpContext != null);
return (MetaTable)httpContext.Items[s_metaTableKey];
}
/// <summary>
/// Set the MetaTable associated with the current HttpRequest. Normally, this is set automatically from the
/// route, but this method is useful to set the table when used outside of routing.
/// </summary>
public static void SetRequestMetaTable(HttpContext httpContext, MetaTable table) {
SetRequestMetaTable(new HttpContextWrapper(httpContext), table);
}
internal static void SetRequestMetaTable(HttpContextBase httpContext, MetaTable table) {
Debug.Assert(httpContext != null);
httpContext.Items[s_metaTableKey] = table;
}
#region IRouteHandler Members
IHttpHandler IRouteHandler.GetHttpHandler(RequestContext requestContext) {
// Save the RequestContext
Debug.Assert(requestContext.HttpContext.Items[s_requestContextKey] == null);
requestContext.HttpContext.Items[s_requestContextKey] = requestContext;
// Get the dynamic route
var route = (DynamicDataRoute)requestContext.RouteData.Route;
// Get the Model from the route
MetaModel model = route.Model;
// Get the MetaTable and save it in the HttpContext
MetaTable table = route.GetTableFromRouteData(requestContext.RouteData);
requestContext.HttpContext.Items[s_metaTableKey] = table;
// Get the action from the request context
string action = route.GetActionFromRouteData(requestContext.RouteData);
return CreateHandler(route, table, action);
}
#endregion
}
}

View File

@ -0,0 +1,113 @@
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using System.Globalization;
using System.Web.DynamicData.Util;
using System.Web.Resources;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace System.Web.DynamicData {
[ToolboxBitmap(typeof(DynamicEntity), "DynamicEntity.bmp")]
public class DynamicEntity : Control {
private HttpContextBase _context;
[
DefaultValue(DataBoundControlMode.ReadOnly),
Category("Behavior"),
ResourceDescription("DynamicEntity_Mode")
]
public DataBoundControlMode Mode {
get {
var value = ViewState["Mode"];
return value != null ? (DataBoundControlMode)value : DataBoundControlMode.ReadOnly;
}
set {
ViewState["Mode"] = value;
}
}
[
DefaultValue(""),
Category("Behavior"),
ResourceDescription("DynamicControlFieldCommon_UIHint")
]
public string UIHint {
get {
return (string)ViewState["UIHint"] ?? String.Empty;
}
set {
ViewState["UIHint"] = value;
}
}
[
Category("Behavior"),
DefaultValue(""),
Themeable(false),
ResourceDescription("DynamicControlFieldCommon_ValidationGroup")
]
public string ValidationGroup {
get {
return (string)ViewState["ValidationGroup"] ?? String.Empty;
}
set {
ViewState["ValidationGroup"] = value;
}
}
private new HttpContextBase Context {
get {
return _context ?? new HttpContextWrapper(HttpContext.Current);
}
}
public DynamicEntity() {
}
// for unit testing
internal DynamicEntity(HttpContextBase context)
: this() {
_context = context;
}
[SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers", MessageId = "0#")]
protected override void OnLoad(EventArgs e) {
base.OnLoad(e);
if (DesignMode) {
return;
}
MetaTable table = MetaTableHelper.FindMetaTable(this, Context);
if (table == null) {
throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture,
DynamicDataResources.DynamicEntity_ControlNeedsToExistInAContextSupportingDynamicData,
this.ID));
}
EntityTemplateFactory entityTemplateFactory = table.Model.EntityTemplateFactory;
EntityTemplateUserControl entityTemplateControl = entityTemplateFactory.CreateEntityTemplate(table, Mode, UIHint);
if (entityTemplateControl == null) {
throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture,
DynamicDataResources.DynamicEntity_CantFindTemplate,
table.Name,
entityTemplateFactory.TemplateFolderVirtualPath));
}
entityTemplateControl.Mode = Mode;
entityTemplateControl.ValidationGroup = ValidationGroup;
entityTemplateControl.Table = table;
Controls.Add(entityTemplateControl);
}
protected override void Render(HtmlTextWriter writer) {
if (DesignMode) {
writer.Write("[" + GetType().Name + "]");
}
else {
base.Render(writer);
}
}
}
}

View File

@ -0,0 +1,412 @@
namespace System.Web.DynamicData {
using System;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Collections.Generic;
using System.Globalization;
using System.Web.Resources;
using System.Diagnostics;
/// <summary>
/// Field type that can display DynamicData UI
/// </summary>
[Designer("System.Web.DynamicData.Design.DynamicFieldDesigner, " + AssemblyRef.SystemWebDynamicDataDesign)]
public class DynamicField : DataControlField, IAttributeAccessor, IFieldFormattingOptions {
private bool _customConvertEmptyStringToNullSet;
private bool _customApplyFormatInEditModeSet;
private MetaColumn _column;
private IDictionary<string, string> _attributes;
/// <summary>
/// same as base. uses column's display name if possible
/// </summary>
public override string HeaderText {
get {
object o = ViewState["HeaderText"];
if (o != null)
return (string)o;
// Default to the Column's DisplayName
if (Column != null)
return Column.DisplayName;
// If we couldn't get it, use the name if the data field
return DataField;
}
set {
base.HeaderText = value;
}
}
/// <summary>
/// same as base. uses column's SortExpression property, if possible.
/// </summary>
public override string SortExpression {
get {
object o = ViewState["SortExpression"];
if (o != null)
return (string)o;
// Default to the Column's SortExpression
if (Column != null)
return Column.SortExpression;
return String.Empty;
}
set {
base.SortExpression = value;
}
}
/// <summary>
/// Determines whether the control validates client input or not, defaults to inherit from parent.
/// </summary>
[
Category("Behavior"),
ResourceDescription("DynamicField_ValidateRequestMode"),
DefaultValue(ValidateRequestMode.Inherit)
]
public new ValidateRequestMode ValidateRequestMode {
get {
return base.ValidateRequestMode;
}
set {
base.ValidateRequestMode = value;
}
}
[
SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "ReadOnly", Justification="Matches DataBoundControlMode value"),
DefaultValue(false),
Category("Behavior"),
ResourceDescription("DynamicField_ReadOnly"),
]
/// <summary>
/// Forces this DynamicField to always load a ReadOnly template
/// </summary>
public virtual bool ReadOnly {
get {
object o = ViewState["ReadOnly"];
return (o == null ? false : (bool)o);
}
set {
ViewState["ReadOnly"] = value;
}
}
/// <summary>
/// The name of the column that this field handles
/// </summary>
[
Category("Data"),
DefaultValue(""),
ResourceDescription("DynamicControlFieldCommon_DataField")
]
public virtual string DataField {
get {
object o = ViewState["DataField"];
return ((o == null) ? String.Empty : (string)o);
}
set {
if (!String.Equals(value, ViewState["DataField"])) {
ViewState["DataField"] = value;
OnFieldChanged();
}
}
}
/// <summary>
/// The MetaColumn that this fiedl is working with
/// </summary>
protected MetaColumn Column {
get {
// Don't do anything in Design mode. In some cases in the Designer (in the Edit field dialog),
// DesignMode actually returns true, so checking for a null Control provides an additional check.
if (DesignMode || Control == null)
return null;
if (_column == null) {
MetaTable table = Control.FindMetaTable();
if (table == null) {
throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, DynamicDataResources.DynamicControl_ControlNeedsToExistInADataControlUsingDynamicDataSource));
}
_column = table.GetColumn(DataField);
}
return _column;
}
}
/// <summary>
/// An optional UIHint specified on the field
/// </summary>
[
Category("Behavior"),
DefaultValue(""),
ResourceDescription("DynamicControlFieldCommon_UIHint")
]
public virtual string UIHint {
get {
object o = ViewState["UIHint"];
return ((o == null) ? String.Empty : (string)o);
}
set {
if (!String.Equals(value, ViewState["UIHint"])) {
ViewState["UIHint"] = value;
OnFieldChanged();
}
}
}
/// <summary>
/// The validation group that the field template needs to be in
/// </summary>
[
Category("Behavior"),
DefaultValue(""),
ResourceDescription("DynamicControlFieldCommon_ValidationGroup")
]
public virtual string ValidationGroup {
get {
object o = ViewState["ValidationGroup"];
return ((o == null) ? String.Empty : (string)o);
}
set {
if (!String.Equals(value, ViewState["ValidationGroup"])) {
ViewState["ValidationGroup"] = value;
OnFieldChanged();
}
}
}
/// <summary>
/// See base class documentation
/// </summary>
protected override DataControlField CreateField() {
return new DynamicField();
}
/// <summary>
/// See base class documentation
/// </summary>
public override void InitializeCell(DataControlFieldCell cell, DataControlCellType cellType,
DataControlRowState rowState, int rowIndex) {
base.InitializeCell(cell, cellType, rowState, rowIndex);
if (cellType == DataControlCellType.DataCell) {
DynamicControl control = CreateDynamicControl();
control.DataField = DataField;
control.Mode = DetermineControlMode(rowState);
// Copy various properties into the control
if (_customApplyFormatInEditModeSet) {
control.ApplyFormatInEditMode = ApplyFormatInEditMode;
}
if (_customConvertEmptyStringToNullSet) {
control.ConvertEmptyStringToNull = ConvertEmptyStringToNull;
}
control.DataFormatString = DataFormatString;
if (ViewState["HtmlEncode"] == null) {
// There is no Column in Design Mode
if (!DesignMode) {
control.HtmlEncode = Column.HtmlEncode;
}
}
else {
control.HtmlEncode = HtmlEncode;
}
control.NullDisplayText = NullDisplayText;
control.UIHint = UIHint;
control.ValidationGroup = ValidationGroup;
// Pass it all the extra declarative attributes that we got
control.SetAttributes(_attributes);
ConfigureDynamicControl(control);
cell.Controls.Add(control);
}
}
/// <summary>
/// Provides a way for classes deriving from DynamicField to override how DynamicControl gets created.
/// </summary>
/// <returns></returns>
protected virtual DynamicControl CreateDynamicControl() {
return new DynamicControl();
}
/// <summary>
/// Provides a hook to further modify a DynamicControl that was created by the InitializeCell method
/// </summary>
/// <param name="control"></param>
protected virtual void ConfigureDynamicControl(DynamicControl control) {
Debug.Assert(control != null);
}
private DataBoundControlMode DetermineControlMode(DataControlRowState rowState) {
if (ReadOnly) {
return DataBoundControlMode.ReadOnly;
}
bool edit = (rowState & DataControlRowState.Edit) != 0;
bool insert = (rowState & DataControlRowState.Insert) != 0;
if (edit) {
return DataBoundControlMode.Edit;
} else if (insert) {
return DataBoundControlMode.Insert;
} else {
return DataBoundControlMode.ReadOnly;
}
}
/// <summary>
/// See base class documentation
/// </summary>
public override void ExtractValuesFromCell(IOrderedDictionary dictionary, DataControlFieldCell cell,
DataControlRowState rowState, bool includeReadOnly) {
Misc.ExtractValuesFromBindableControls(dictionary, cell);
}
/// <summary>
/// See base class documentation
/// </summary>
protected override void CopyProperties(DataControlField newField) {
base.CopyProperties(newField);
DynamicField field = ((DynamicField)newField);
field.DataField = DataField;
field.ApplyFormatInEditMode = ApplyFormatInEditMode;
field.ConvertEmptyStringToNull = ConvertEmptyStringToNull;
field.HtmlEncode = HtmlEncode;
field.ReadOnly = ReadOnly;
field.NullDisplayText = NullDisplayText;
field.UIHint = UIHint;
field.ValidationGroup = ValidationGroup;
field.DataFormatString = DataFormatString;
}
#region IAttributeAccessor Members
/// <summary>
/// See IAttributeAccessor
/// </summary>
public string GetAttribute(string key) {
if (_attributes == null)
return String.Empty;
return _attributes[key];
}
/// <summary>
/// See IAttributeAccessor
/// </summary>
public void SetAttribute(string key, string value) {
if (_attributes == null) {
_attributes = new Dictionary<string, string>();
}
_attributes[key] = value;
}
#endregion
#region IFieldFormattingOptions Members
/// <summary>
/// See IFieldFormattingOptions
/// </summary>
[
Category("Behavior"),
DefaultValue(false),
ResourceDescription("DynamicControlFieldCommon_ConvertEmptyStringToNull")
]
public bool ConvertEmptyStringToNull {
get {
object o = ViewState["ConvertEmptyStringToNull"];
return (o == null ? false : (bool)o);
}
set {
_customConvertEmptyStringToNullSet = true;
ViewState["ConvertEmptyStringToNull"] = value;
}
}
/// <summary>
/// See IFieldFormattingOptions
/// </summary>
[
Category("Behavior"),
DefaultValue(false),
ResourceDescription("DynamicControlFieldCommon_ApplyFormatInEditMode")
]
public bool ApplyFormatInEditMode {
get {
object o = ViewState["ApplyFormatInEditMode"];
return (o == null ? false : (bool)o);
}
set {
_customApplyFormatInEditModeSet = true;
ViewState["ApplyFormatInEditMode"] = value;
}
}
/// <summary>
/// See IFieldFormattingOptions
/// </summary>
[
Category("Data"),
DefaultValue(""),
ResourceDescription("DynamicControlFieldCommon_DataFormatString")
]
public string DataFormatString {
get {
object o = ViewState["DataFormatString"];
return (o == null ? String.Empty : (string)o);
}
set {
ViewState["DataFormatString"] = value;
}
}
/// <summary>
/// See IFieldFormattingOptions
/// </summary>
[
Category("Behavior"),
DefaultValue(true),
ResourceDescription("DynamicControlFieldCommon_HtmlEncode")
]
public bool HtmlEncode {
get {
object o = ViewState["HtmlEncode"];
return (o == null ? true : (bool)o);
}
set {
ViewState["HtmlEncode"] = value;
}
}
/// <summary>
/// See IFieldFormattingOptions
/// </summary>
[
Category("Behavior"),
DefaultValue(""),
ResourceDescription("DynamicControlFieldCommon_NullDisplayText")
]
public string NullDisplayText {
get {
object o = ViewState["NullDisplayText"];
return (o == null ? String.Empty : (string)o);
}
set {
ViewState["NullDisplayText"] = value;
}
}
#endregion
}
}

View File

@ -0,0 +1,152 @@
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Web.Compilation;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.Expressions;
using System.Web.Resources;
namespace System.Web.DynamicData {
public class DynamicFilter : Control, IFilterExpressionProvider {
private HttpContextBase _context;
private IQueryableDataSource _dataSource;
private Func<MetaColumn, string, QueryableFilterUserControl> _filterLoader;
private QueryableFilterUserControl _filterUserControl;
protected internal MetaColumn Column {
get;
private set;
}
/// <summary>
/// The name of the column that this control handles
/// </summary>
[
Category("Data"),
DefaultValue(""),
ResourceDescription("DynamicFilter_DataField")
]
public string DataField {
get {
object o = ViewState["DataField"];
return (o == null) ? String.Empty : (string)o;
}
set {
ViewState["DataField"] = value;
}
}
/// <summary>
/// An optional property that can be used to override the column's default default filter UI hint.
/// </summary>
[
Category("Behavior"),
DefaultValue(""),
ResourceDescription("DynamicFilter_FilterUIHint")
]
public string FilterUIHint {
get {
object o = ViewState["FilterUIHint"];
return (o == null) ? String.Empty : (string)o;
}
set {
ViewState["FilterUIHint"] = value;
}
}
public DynamicFilter()
: this(CreateUserControl) {
}
// internal for unit testing
internal DynamicFilter(Func<MetaColumn, string, QueryableFilterUserControl> filterLoader) {
_filterLoader = filterLoader;
}
// internal for unit testing
internal static QueryableFilterUserControl CreateUserControl(MetaColumn column, string filterUiHint) {
return column.Model.FilterFactory.CreateFilterControl(column, filterUiHint);
}
internal new HttpContextBase Context {
get {
return _context ?? new HttpContextWrapper(HttpContext.Current);
}
set {
_context = value;
}
}
public event EventHandler FilterChanged;
/// <summary>
/// Returns the filter template that was created for this control.
/// </summary>
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public Control FilterTemplate {
get {
return _filterUserControl;
}
}
protected override void Render(HtmlTextWriter writer) {
if (DesignMode) {
writer.Write("[" + GetType().Name + "]");
}
else {
base.Render(writer);
}
}
private void EnsureInit(IQueryableDataSource dataSource) {
if (_filterUserControl == null) {
MetaTable table = DynamicDataExtensions.GetMetaTable(dataSource, Context);
Column = table.GetColumn(DataField);
_filterUserControl = _filterLoader(Column, FilterUIHint);
_filterUserControl.Initialize(Column, dataSource, Context);
_filterUserControl.FilterChanged += new EventHandler(Child_FilterChanged);
Controls.Add(_filterUserControl);
}
}
private void Child_FilterChanged(object sender, EventArgs e) {
EventHandler eventHandler = FilterChanged;
if (eventHandler != null) {
eventHandler(sender, e);
}
}
internal void Initialize(IQueryableDataSource dataSource) {
Debug.Assert(dataSource != null);
EnsureInit(dataSource);
}
#region IFilterExpressionProvider Members
void IFilterExpressionProvider.Initialize(IQueryableDataSource dataSource) {
if (dataSource == null) {
throw new ArgumentNullException("dataSource");
}
_dataSource = dataSource;
Page.InitComplete += new EventHandler(Page_InitComplete);
}
void Page_InitComplete(object sender, EventArgs e) {
Debug.Assert(_dataSource != null);
EnsureInit(_dataSource);
}
IQueryable IFilterExpressionProvider.GetQueryable(IQueryable source) {
return _filterUserControl.GetQueryable(source);
}
#endregion
}
}

View File

@ -0,0 +1,47 @@
using System.Linq.Expressions;
using System.Web.UI;
using System.Globalization;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Web.UI.WebControls.Expressions;
using System.Web.UI.WebControls;
namespace System.Web.DynamicData {
/// <summary>
/// This is a Dynamic Data-specific extension of DataSourceExpression that works by forwarding the processing of an IQueryable to
/// a specialized control such as QueryableFilterRepeater or DynamicFilter.
/// </summary>
public class DynamicFilterExpression : DataSourceExpression {
/// <summary>
/// References the ID of a QueryableFilterRepeater or DynamicFilter control on the page.
/// </summary>
[SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "ID", Justification = "This refers to a Control ID")]
public string ControlID { get; set; }
private IFilterExpressionProvider FilterExpressionProvider { get; set; }
public override void SetContext(Control owner, HttpContext context, IQueryableDataSource dataSource) {
base.SetContext(owner, context, dataSource);
FilterExpressionProvider = FindControl(Owner);
FilterExpressionProvider.Initialize(dataSource);
}
private IFilterExpressionProvider FindControl(Control control) {
var result = Misc.FindControl(control, ControlID) as IFilterExpressionProvider;
if (result == null) {
throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, "The control '{0}' could not be found.", ControlID));
}
return result;
}
/// <summary>
/// Delegates the processing of the source queryable to the control referenced by ControlID.
/// </summary>
/// <param name="source"></param>
/// <returns></returns>
public override IQueryable GetQueryable(IQueryable source) {
IQueryable result = FilterExpressionProvider.GetQueryable(source);
return result;
}
}
}

View File

@ -0,0 +1,282 @@
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using System.Globalization;
using System.Security.Permissions;
using System.Web.Compilation;
using System.Web.Resources;
using System.Web.Routing;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.DynamicData.Util;
namespace System.Web.DynamicData {
/// <summary>
/// <para>A control that displays links to table actions based on routing rules. It will not generate links for actions that are not
/// allowed by the routing rules. It can work in 3 modes: explicit, databinding to MetaTable, or databinding to a data row.</para>
/// <para>Databinding to MetaTable allows for creating links to actions for a collection of MetaTable objects (such as in the Default.aspx
/// page in the project templates)</para>
/// <para>Databinding to a data row allows for creating links to actions for data rows retrieved from a database. These are usually used with
/// Edit and Details actions.</para>
/// <para>Explicit mode allows for links to non-item-specific actions (like List and Insert) and is achieved by properly setting
/// ContextTypeName, Table, and Action properties. This is done in the PreRender phase if the NavigateUrl property is null (i.e. it has not
/// been set explicitly or did not get set in one of the databinding scenarios.)</para>
/// <para>Extra route parameters can be provided by declaring expando attributes on the controls markup.</para>
/// </summary>
[SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "HyperLink", Justification="It's an extension of the HyperLink class")]
[DefaultProperty("Action")]
[ToolboxBitmap(typeof(DynamicHyperLink), "DynamicHyperLink.bmp")]
public class DynamicHyperLink : HyperLink, IAttributeAccessor {
private HttpContextBase _context;
private bool _dataBound;
private object _dataItem;
private Dictionary<string, string> _extraRouteParams = new Dictionary<string, string>();
/// <summary>
/// The name of the action
/// </summary>
[TypeConverter(typeof(ActionConverter))]
[DefaultValue("")]
[Category("Navigation")]
[ResourceDescription("DynamicHyperLink_Action")]
public string Action {
get {
object o = ViewState["Action"];
return (o == null ? String.Empty: (string)o);
}
set {
ViewState["Action"] = value;
}
}
internal new HttpContextBase Context {
get {
return _context ?? new HttpContextWrapper(base.Context);
}
set {
_context = value;
}
}
/// <summary>
/// The name of the context type
/// </summary>
[DefaultValue("")]
[Category("Navigation")]
[ResourceDescription("DynamicHyperLink_ContextTypeName")]
public string ContextTypeName {
get {
object o = ViewState["ContextTypeName"];
return ((o == null) ? String.Empty : (string)o);
}
set {
ViewState["ContextTypeName"] = value;
}
}
/// <summary>
/// The name of the column whose value will be used to populate the Text
/// property if it is not already set in data binding scenarios.
/// </summary>
[DefaultValue("")]
[Category("Navigation")]
[ResourceDescription("DynamicHyperLink_DataField")]
public string DataField {
get {
object o = ViewState["DataField"];
return ((o == null) ? String.Empty : (string)o);
}
set {
ViewState["DataField"] = value;
}
}
// for unit testing purposes
internal object Page_DataItem {
get {
return _dataItem ?? Page.GetDataItem();
}
set {
_dataItem = value;
}
}
/// <summary>
/// The name of the table
/// </summary>
[DefaultValue("")]
[Category("Navigation")]
[ResourceDescription("DynamicHyperLink_TableName")]
public string TableName {
get {
object o = ViewState["TableName"];
return ((o == null) ? String.Empty : (string)o);
}
set {
ViewState["TableName"] = value;
}
}
[SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers", MessageId = "0#")]
protected override void OnDataBinding(EventArgs e) {
base.OnDataBinding(e);
if (DesignMode) {
return;
}
if (!String.IsNullOrEmpty(NavigateUrl)) {
// stop processing if there already is a URL
return;
}
if (!String.IsNullOrEmpty(TableName) || !String.IsNullOrEmpty(ContextTypeName)) {
throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture,
DynamicDataResources.DynamicHyperLink_CannotSetTableAndContextWhenDatabinding, this.ID));
}
object dataItem = Page_DataItem;
if (dataItem == null) {
throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture,
DynamicDataResources.DynamicHyperLink_CannotBindToNull, this.ID));
}
MetaTable table = dataItem as MetaTable;
if (table != null) {
BindToMetaTable(table);
} else {
BindToDataItem(dataItem);
}
_dataBound = true;
}
private void BindToMetaTable(MetaTable table) {
string action = GetActionOrDefaultTo(PageAction.List);
NavigateUrl = table.GetActionPath(action, GetRouteValues());
if (String.IsNullOrEmpty(Text)) {
Text = table.DisplayName;
}
}
private void BindToDataItem(object dataItem) {
dataItem = Misc.GetRealDataItem(dataItem);
Debug.Assert(dataItem != null, "DataItem is null");
// Try to get the MetaTable from the type and if we can't find it then ---- up.
MetaTable table = Misc.GetTableFromTypeHierarchy(dataItem.GetType());
if (table == null) {
throw new InvalidOperationException(String.Format(
CultureInfo.CurrentCulture,
DynamicDataResources.MetaModel_EntityTypeDoesNotBelongToModel,
dataItem.GetType().FullName));
}
string action = GetActionOrDefaultTo(PageAction.Details);
NavigateUrl = table.GetActionPath(action, GetRouteValues(table, dataItem));
if (String.IsNullOrEmpty(Text)) {
if (!String.IsNullOrEmpty(DataField)) {
Text = DataBinder.GetPropertyValue(dataItem, DataField).ToString();
} else {
Text = table.GetDisplayString(dataItem);
}
}
}
[SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers", MessageId = "0#")]
protected override void OnPreRender(EventArgs e) {
base.OnPreRender(e);
if (DesignMode) {
if (!String.IsNullOrEmpty(NavigateUrl)) {
NavigateUrl = "DesignTimeUrl";
}
return;
}
// check both _dataBound and NavigateUrl cause NavigateUrl might be empty if routing/scaffolding
// does not allow a particular action
if (!_dataBound && String.IsNullOrEmpty(NavigateUrl)) {
MetaTable table;
try {
table = GetTable();
} catch (Exception exception) {
throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, DynamicDataResources.DynamicHyperLink_CannotDetermineTable, this.ID), exception);
}
if (table == null) {
throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, DynamicDataResources.DynamicHyperLink_CannotDetermineTable, this.ID));
}
var action = GetActionOrDefaultTo(PageAction.List);
NavigateUrl = table.GetActionPath(action, GetRouteValues());
}
}
private RouteValueDictionary GetRouteValues() {
var routeValues = new RouteValueDictionary();
foreach (var entry in _extraRouteParams) {
string key = entry.Key;
routeValues[key] = entry.Value;
}
return routeValues;
}
private RouteValueDictionary GetRouteValues(MetaTable table, object row) {
RouteValueDictionary routeValues = GetRouteValues();
foreach (var pk in table.GetPrimaryKeyDictionary(row)) {
routeValues[pk.Key] = pk.Value;
}
return routeValues;
}
private string GetActionOrDefaultTo(string defaultAction) {
return String.IsNullOrEmpty(Action) ? defaultAction : Action;
}
// internal for unit testing
internal virtual MetaTable GetTable() {
MetaTable table;
if (!String.IsNullOrEmpty(TableName)) {
table = GetTableFromTableName();
} else {
table = DynamicDataRouteHandler.GetRequestMetaTable(Context);
}
return table;
}
private MetaTable GetTableFromTableName() {
var tableName = TableName;
var contextTypeName = ContextTypeName;
Debug.Assert(!String.IsNullOrEmpty(tableName));
if (!String.IsNullOrEmpty(contextTypeName)) {
// context type allows to disambiguate table names
Type contextType = BuildManager.GetType(contextTypeName, /* throwOnError */ true, /* ignoreCase */ true);
MetaModel model = MetaModel.GetModel(contextType);
MetaTable table = model.GetTable(tableName, contextType);
return table;
} else {
var table = DynamicDataRouteHandler.GetRequestMetaTable(Context);
if (table == null) {
return null;
}
return table.Model.GetTable(tableName);
}
}
#region IAttributeAccessor Members
string IAttributeAccessor.GetAttribute(string key) {
return (string)_extraRouteParams[key];
}
void IAttributeAccessor.SetAttribute(string key, string value) {
_extraRouteParams[key] = value;
}
#endregion
}
}

View File

@ -0,0 +1,42 @@
using System.Collections.Generic;
using System.Globalization;
using System.Web.DynamicData.Util;
using System.Web.Resources;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace System.Web.DynamicData {
/// <summary>
/// DynamicQueryStringParameter allows a datasource to have its primary key easily fed from the query string.
/// It does not require any attributes, and works even for multi-part primary keys.
/// </summary>
public class DynamicQueryStringParameter : Parameter, IWhereParametersProvider {
/// <summary>
/// See IWhereParametersProvider.GetWhereParameters
/// </summary>
public virtual IEnumerable<Parameter> GetWhereParameters(IDynamicDataSource dataSource) {
var table = MetaTableHelper.GetTableWithFullFallback(dataSource, HttpContext.Current.ToWrapper());
// check makes no sense as the above call will throw
//if (table == null) {
// return new Parameter[0];
//}
return RouteParametersHelper.GetColumnParameters(table, Name);
}
/// <summary>
/// same as base
/// </summary>
/// <param name="context"></param>
/// <param name="control"></param>
/// <returns></returns>
protected override object Evaluate(HttpContext context, Control control) {
// If this gets called, it means we never had a chance to expand the parameter. Give an error
// telling the user to use a DynamicDataManager
throw new InvalidOperationException(String.Format(
CultureInfo.CurrentCulture, DynamicDataResources.DynamicParameter_NeedExpansion, typeof(DynamicQueryStringParameter).Name));
}
}
}

Some files were not shown because too many files have changed in this diff Show More