283 lines
11 KiB
C#
283 lines
11 KiB
C#
|
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
|
|||
|
}
|
|||
|
}
|