1 using System.Collections.Generic; 2 using System.ComponentModel; 3 using System.Diagnostics; 4 using System.Diagnostics.CodeAnalysis; 5 using System.Drawing; 6 using System.Globalization; 7 using System.Security.Permissions; 8 using System.Web.Compilation; 9 using System.Web.Resources; 10 using System.Web.Routing; 11 using System.Web.UI; 12 using System.Web.UI.WebControls; 13 using System.Web.DynamicData.Util; 14 15 namespace System.Web.DynamicData { 16 /// <summary> 17 /// <para>A control that displays links to table actions based on routing rules. It will not generate links for actions that are not 18 /// allowed by the routing rules. It can work in 3 modes: explicit, databinding to MetaTable, or databinding to a data row.</para> 19 /// <para>Databinding to MetaTable allows for creating links to actions for a collection of MetaTable objects (such as in the Default.aspx 20 /// page in the project templates)</para> 21 /// <para>Databinding to a data row allows for creating links to actions for data rows retrieved from a database. These are usually used with 22 /// Edit and Details actions.</para> 23 /// <para>Explicit mode allows for links to non-item-specific actions (like List and Insert) and is achieved by properly setting 24 /// ContextTypeName, Table, and Action properties. This is done in the PreRender phase if the NavigateUrl property is null (i.e. it has not 25 /// been set explicitly or did not get set in one of the databinding scenarios.)</para> 26 /// <para>Extra route parameters can be provided by declaring expando attributes on the controls markup.</para> 27 /// </summary> 28 [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "HyperLink", Justification="It's an extension of the HyperLink class")] 29 [DefaultProperty("Action")] 30 [ToolboxBitmap(typeof(DynamicHyperLink), "DynamicHyperLink.bmp")] 31 public class DynamicHyperLink : HyperLink, IAttributeAccessor { 32 private HttpContextBase _context; 33 private bool _dataBound; 34 private object _dataItem; 35 private Dictionary<string, string> _extraRouteParams = new Dictionary<string, string>(); 36 37 /// <summary> 38 /// The name of the action 39 /// </summary> 40 [TypeConverter(typeof(ActionConverter))] 41 [DefaultValue("")] 42 [Category("Navigation")] 43 [ResourceDescription("DynamicHyperLink_Action")] 44 public string Action { 45 get { 46 object o = ViewState["Action"]; 47 return (o == null ? String.Empty: (string)o); 48 } 49 set { 50 ViewState["Action"] = value; 51 } 52 } 53 54 internal new HttpContextBase Context { 55 get { 56 return _context ?? new HttpContextWrapper(base.Context); 57 } 58 set { 59 _context = value; 60 } 61 } 62 63 /// <summary> 64 /// The name of the context type 65 /// </summary> 66 [DefaultValue("")] 67 [Category("Navigation")] 68 [ResourceDescription("DynamicHyperLink_ContextTypeName")] 69 public string ContextTypeName { 70 get { 71 object o = ViewState["ContextTypeName"]; 72 return ((o == null) ? String.Empty : (string)o); 73 } 74 set { 75 ViewState["ContextTypeName"] = value; 76 } 77 } 78 79 /// <summary> 80 /// The name of the column whose value will be used to populate the Text 81 /// property if it is not already set in data binding scenarios. 82 /// </summary> 83 [DefaultValue("")] 84 [Category("Navigation")] 85 [ResourceDescription("DynamicHyperLink_DataField")] 86 public string DataField { 87 get { 88 object o = ViewState["DataField"]; 89 return ((o == null) ? String.Empty : (string)o); 90 } 91 set { 92 ViewState["DataField"] = value; 93 } 94 } 95 96 // for unit testing purposes 97 internal object Page_DataItem { 98 get { 99 return _dataItem ?? Page.GetDataItem(); 100 } 101 set { 102 _dataItem = value; 103 } 104 } 105 106 /// <summary> 107 /// The name of the table 108 /// </summary> 109 [DefaultValue("")] 110 [Category("Navigation")] 111 [ResourceDescription("DynamicHyperLink_TableName")] 112 public string TableName { 113 get { 114 object o = ViewState["TableName"]; 115 return ((o == null) ? String.Empty : (string)o); 116 } 117 set { 118 ViewState["TableName"] = value; 119 } 120 } 121 122 [SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers", MessageId = "0#")] OnDataBinding(EventArgs e)123 protected override void OnDataBinding(EventArgs e) { 124 base.OnDataBinding(e); 125 126 if (DesignMode) { 127 return; 128 } 129 130 if (!String.IsNullOrEmpty(NavigateUrl)) { 131 // stop processing if there already is a URL 132 return; 133 } 134 135 if (!String.IsNullOrEmpty(TableName) || !String.IsNullOrEmpty(ContextTypeName)) { 136 throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, 137 DynamicDataResources.DynamicHyperLink_CannotSetTableAndContextWhenDatabinding, this.ID)); 138 } 139 140 object dataItem = Page_DataItem; 141 if (dataItem == null) { 142 throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, 143 DynamicDataResources.DynamicHyperLink_CannotBindToNull, this.ID)); 144 } 145 146 MetaTable table = dataItem as MetaTable; 147 if (table != null) { 148 BindToMetaTable(table); 149 } else { 150 BindToDataItem(dataItem); 151 } 152 153 _dataBound = true; 154 } 155 BindToMetaTable(MetaTable table)156 private void BindToMetaTable(MetaTable table) { 157 string action = GetActionOrDefaultTo(PageAction.List); 158 NavigateUrl = table.GetActionPath(action, GetRouteValues()); 159 if (String.IsNullOrEmpty(Text)) { 160 Text = table.DisplayName; 161 } 162 } 163 BindToDataItem(object dataItem)164 private void BindToDataItem(object dataItem) { 165 dataItem = Misc.GetRealDataItem(dataItem); 166 Debug.Assert(dataItem != null, "DataItem is null"); 167 // Try to get the MetaTable from the type and if we can't find it then ---- up. 168 MetaTable table = Misc.GetTableFromTypeHierarchy(dataItem.GetType()); 169 if (table == null) { 170 throw new InvalidOperationException(String.Format( 171 CultureInfo.CurrentCulture, 172 DynamicDataResources.MetaModel_EntityTypeDoesNotBelongToModel, 173 dataItem.GetType().FullName)); 174 } 175 176 string action = GetActionOrDefaultTo(PageAction.Details); 177 NavigateUrl = table.GetActionPath(action, GetRouteValues(table, dataItem)); 178 179 if (String.IsNullOrEmpty(Text)) { 180 if (!String.IsNullOrEmpty(DataField)) { 181 Text = DataBinder.GetPropertyValue(dataItem, DataField).ToString(); 182 } else { 183 Text = table.GetDisplayString(dataItem); 184 } 185 } 186 } 187 188 [SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers", MessageId = "0#")] OnPreRender(EventArgs e)189 protected override void OnPreRender(EventArgs e) { 190 base.OnPreRender(e); 191 192 if (DesignMode) { 193 if (!String.IsNullOrEmpty(NavigateUrl)) { 194 NavigateUrl = "DesignTimeUrl"; 195 } 196 return; 197 } 198 199 // check both _dataBound and NavigateUrl cause NavigateUrl might be empty if routing/scaffolding 200 // does not allow a particular action 201 if (!_dataBound && String.IsNullOrEmpty(NavigateUrl)) { 202 MetaTable table; 203 try { 204 table = GetTable(); 205 } catch (Exception exception) { 206 throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, DynamicDataResources.DynamicHyperLink_CannotDetermineTable, this.ID), exception); 207 } 208 209 if (table == null) { 210 throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, DynamicDataResources.DynamicHyperLink_CannotDetermineTable, this.ID)); 211 } 212 213 var action = GetActionOrDefaultTo(PageAction.List); 214 NavigateUrl = table.GetActionPath(action, GetRouteValues()); 215 } 216 } 217 GetRouteValues()218 private RouteValueDictionary GetRouteValues() { 219 var routeValues = new RouteValueDictionary(); 220 foreach (var entry in _extraRouteParams) { 221 string key = entry.Key; 222 routeValues[key] = entry.Value; 223 } 224 return routeValues; 225 } 226 GetRouteValues(MetaTable table, object row)227 private RouteValueDictionary GetRouteValues(MetaTable table, object row) { 228 RouteValueDictionary routeValues = GetRouteValues(); 229 foreach (var pk in table.GetPrimaryKeyDictionary(row)) { 230 routeValues[pk.Key] = pk.Value; 231 } 232 return routeValues; 233 } 234 GetActionOrDefaultTo(string defaultAction)235 private string GetActionOrDefaultTo(string defaultAction) { 236 return String.IsNullOrEmpty(Action) ? defaultAction : Action; 237 } 238 239 // internal for unit testing GetTable()240 internal virtual MetaTable GetTable() { 241 MetaTable table; 242 if (!String.IsNullOrEmpty(TableName)) { 243 table = GetTableFromTableName(); 244 } else { 245 table = DynamicDataRouteHandler.GetRequestMetaTable(Context); 246 } 247 return table; 248 } 249 GetTableFromTableName()250 private MetaTable GetTableFromTableName() { 251 var tableName = TableName; 252 var contextTypeName = ContextTypeName; 253 Debug.Assert(!String.IsNullOrEmpty(tableName)); 254 255 if (!String.IsNullOrEmpty(contextTypeName)) { 256 // context type allows to disambiguate table names 257 Type contextType = BuildManager.GetType(contextTypeName, /* throwOnError */ true, /* ignoreCase */ true); 258 MetaModel model = MetaModel.GetModel(contextType); 259 MetaTable table = model.GetTable(tableName, contextType); 260 return table; 261 } else { 262 var table = DynamicDataRouteHandler.GetRequestMetaTable(Context); 263 if (table == null) { 264 return null; 265 } 266 return table.Model.GetTable(tableName); 267 } 268 } 269 270 #region IAttributeAccessor Members 271 IAttributeAccessor.GetAttribute(string key)272 string IAttributeAccessor.GetAttribute(string key) { 273 return (string)_extraRouteParams[key]; 274 } 275 IAttributeAccessor.SetAttribute(string key, string value)276 void IAttributeAccessor.SetAttribute(string key, string value) { 277 _extraRouteParams[key] = value; 278 } 279 280 #endregion 281 } 282 } 283