[asp.net] Fix for bug #646479. Do not call control's OnLoad twice when it is dynamica...
[mono.git] / mcs / class / System.Web / System.Web.UI / Control.cs
index 59c13c3127e485822c8d608abe8f761ab9a21383..74e8fe0efc9efebd5cdbf96378f38ca4b5148487 100644 (file)
@@ -42,9 +42,16 @@ using System.ComponentModel.Design.Serialization;
 using System.Globalization;
 using System.IO;
 using System.Security.Permissions;
+using System.Text;
 using System.Web;
-using System.Web.Util;
+using System.Web.Configuration;
 using System.Web.UI.Adapters;
+using System.Web.UI.WebControls;
+using System.Web.Util;
+
+#if NET_4_0
+using System.Web.Routing;
+#endif
 
 namespace System.Web.UI
 {
@@ -82,6 +89,7 @@ namespace System.Web.UI
                static Dictionary <Type, bool> loadViewStateByIDCache;
                bool? loadViewStateByID;
                string uniqueID;
+               string clientID;
                string _userId;
                ControlCollection _controls;
                Control _namingContainer;
@@ -99,7 +107,13 @@ namespace System.Web.UI
                TemplateControl _templateControl;
                bool _isChildControlStateCleared;
                string _templateSourceDirectory;
-
+#if NET_4_0
+               ViewStateMode viewStateMode;
+               ClientIDMode? clientIDMode;
+               ClientIDMode? effectiveClientIDMode;
+               Version renderingCompatibility;
+               bool? renderingCompatibilityOld;
+#endif
                /*************/
                int stateMask;
                const int ENABLE_VIEWSTATE = 1;
@@ -135,6 +149,9 @@ namespace System.Web.UI
                        stateMask = ENABLE_VIEWSTATE | VISIBLE | AUTOID | BINDING_CONTAINER | AUTO_EVENT_WIREUP;
                        if (this is INamingContainer)
                                stateMask |= IS_NAMING_CONTAINER;
+#if NET_4_0
+                       viewStateMode = ViewStateMode.Inherit;
+#endif
                }
                
                ControlAdapter adapter;
@@ -181,6 +198,9 @@ namespace System.Web.UI
 
                [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
                [EditorBrowsable (EditorBrowsableState.Never), Browsable (false)]
+#if NET_4_0
+               [Bindable (true)]
+#endif
                public Control BindingContainer {
                        get {
                                Control container = NamingContainer;
@@ -196,18 +216,251 @@ namespace System.Web.UI
                [WebSysDescription ("An Identification of the control that is rendered.")]
                public virtual string ClientID {
                        get {
-                               string client = UniqueID;
+                               if (clientID != null)
+                                       return clientID;
+#if NET_4_0
+                               clientID = GetClientID ();
+#else
+                               clientID = UniqueID2ClientID (UniqueID);
+#endif                         
+                               stateMask |= ID_SET;
+                               return clientID;
+                       }
+               }
+#if NET_4_0
+               [Bindable (false)]
+               [Browsable (false)]
+               [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+               public virtual Version RenderingCompatibility {
+                       get {
+                               if (renderingCompatibility == null) {
+                                       var ps = WebConfigurationManager.GetSection ("system.web/pages") as PagesSection;
+                                       renderingCompatibility = ps != null ? ps.ControlRenderingCompatibilityVersion : new Version (4, 0);
+                               }
+
+                               return renderingCompatibility;
+                       }
+                       
+                       set {
+                               renderingCompatibility = value;
+                               renderingCompatibilityOld = null;
+                       }
+               }
+
+               internal bool RenderingCompatibilityLessThan40 {
+                       get {
+                               if (!renderingCompatibilityOld.HasValue)
+                                       renderingCompatibilityOld = RenderingCompatibility < new Version (4, 0);
 
-                               if (client != null)
-                                       client = UniqueID2ClientID (client);
+                               return renderingCompatibilityOld.Value;
+                       }
+               }
+               
+               [Bindable (false)]
+               [Browsable (false)]
+               [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+               [EditorBrowsableAttribute (EditorBrowsableState.Never)]
+               public Control DataItemContainer {
+                       get {
+                               Control container = NamingContainer;
+                               if (container == null)
+                                       return null;
 
-                               stateMask |= ID_SET;
-                               return client;
+                               if (container is IDataItemContainer)
+                                       return container;
+
+                               return container.DataItemContainer;
+                       }
+               }
+
+               [Bindable (false)]
+               [Browsable (false)]
+               [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+               [EditorBrowsableAttribute (EditorBrowsableState.Never)]
+               public Control DataKeysContainer {
+                       get {
+                               Control container = NamingContainer;
+                               if (container == null)
+                                       return null;
+
+                               if (container is IDataKeysControl)
+                                       return container;
+
+                               return container.DataKeysContainer;
                        }
                }
 
+               [Themeable (false)]
+               [DefaultValue (ClientIDMode.Inherit)]
+               public virtual ClientIDMode ClientIDMode {
+                       get {
+                               if (!clientIDMode.HasValue)
+                                       return ClientIDMode.Inherit;
+
+                               return clientIDMode.Value;
+                       }
+                       
+                       set {
+                               if (!clientIDMode.HasValue || clientIDMode.Value != value) {
+                                       ClearCachedClientID ();
+                                       ClearEffectiveClientIDMode ();
+                                       clientIDMode = value;
+                               }
+                       }
+               }
+
+               internal ClientIDMode EffectiveClientIDMode {
+                       get {
+                               if (effectiveClientIDMode.HasValue)
+                                       return effectiveClientIDMode.Value;
+                               
+                               ClientIDMode ret = ClientIDMode;
+                               if (ret != ClientIDMode.Inherit) {
+                                       effectiveClientIDMode = ret;
+                                       return ret;
+                               }
+                               
+                               // not sure about this, but it seems logical as INamingContainer is
+                               // the top of the hierarchy and it should "reset" the mode.
+                               Control container = NamingContainer;
+                               if (container != null) {
+                                       effectiveClientIDMode = container.EffectiveClientIDMode;
+                                       return effectiveClientIDMode.Value;
+                               }
+
+                               var ps = WebConfigurationManager.GetSection ("system.web/pages") as PagesSection;
+                               effectiveClientIDMode = ps.ClientIDMode;
+
+                               return effectiveClientIDMode.Value;
+                       }       
+               }
+
+               protected void ClearCachedClientID ()
+               {
+                       clientID = null;
+                       if (!HasControls ())
+                               return;
+
+                       for (int i = 0; i < _controls.Count; i++)
+                               _controls [i].ClearCachedClientID ();
+               }
+
+               protected void ClearEffectiveClientIDMode ()
+               {
+                       effectiveClientIDMode = null;
+                       if (!HasControls ())
+                               return;
+
+                       for (int i = 0; i < _controls.Count; i++)
+                               _controls [i].ClearEffectiveClientIDMode ();
+               }
+
+               string GetClientID ()
+               {
+                       switch (EffectiveClientIDMode) {
+                               case ClientIDMode.AutoID:
+                                       return UniqueID2ClientID (UniqueID);
+
+                               case ClientIDMode.Predictable:
+                                       EnsureID ();
+                                       return GeneratePredictableClientID ();
+
+                               case ClientIDMode.Static:
+                                       EnsureID ();
+                                       return ID;
+
+                               default:
+                                       throw new InvalidOperationException ("Unsupported ClientIDMode value.");
+                       }
+               }
+               
+               string GeneratePredictableClientID ()
+               {
+                       string myID = ID;
+                       bool haveMyID = !String.IsNullOrEmpty (myID);
+                       char separator = ClientIDSeparator;
+
+                       var sb = new StringBuilder ();
+                       Control container = NamingContainer;
+                       if (this is INamingContainer && !haveMyID) {
+                               if (container != null)
+                                       EnsureIDInternal ();
+                               myID = _userId;
+                       }
+                       
+                       if (container != null && container != Page) {
+                               string containerID = container.ID;
+                               if (!String.IsNullOrEmpty (containerID)) {
+                                       sb.Append (container.GetClientID ());
+                                       sb.Append (separator);
+                               } else {
+                                       sb.Append (container.GeneratePredictableClientID ());
+                                       if (sb.Length > 0)
+                                               sb.Append (separator);
+                               }
+                       }
+
+                       if (!haveMyID) {
+                               if (this is INamingContainer || !AutoID)
+                                       sb.Append (myID);
+                               else {
+                                       int length = sb.Length;
+                                       if (length > 0 && sb [length - 1] == separator)
+                                               sb.Length = length - 1;
+                               }
+                               
+                               return sb.ToString ();
+                       }
+                       
+                       sb.Append (myID);
+                       IDataItemContainer dataItemContainer = DataItemContainer as IDataItemContainer;
+                       if (dataItemContainer == null)
+                               return sb.ToString ();
+                       
+                       IDataKeysControl dataKeysContainer = DataKeysContainer as IDataKeysControl;
+                       GetDataBoundControlFieldValue (sb, separator, dataItemContainer, dataKeysContainer);
+                       
+                       return sb.ToString ();
+               }
+
+               void GetDataBoundControlFieldValue (StringBuilder sb, char separator, IDataItemContainer dataItemContainer, IDataKeysControl dataKeysContainer)
+               {
+                       if (dataItemContainer is IDataBoundItemControl)
+                               return;
+                       
+                       int index = dataItemContainer.DisplayIndex;
+                       if (dataKeysContainer == null) {
+                               if (index >= 0) {
+                                       sb.Append (separator);
+                                       sb.Append (index);
+                               }
+                               return;
+                       }
+                       
+                       string[] suffixes = dataKeysContainer.ClientIDRowSuffix;
+                       DataKeyArray keys = dataKeysContainer.ClientIDRowSuffixDataKeys;
+                       if (keys == null || suffixes == null || suffixes.Length == 0) {
+                               sb.Append (separator);
+                               sb.Append (index);
+                               return;
+                       }
+
+                       object value;
+                       DataKey key = keys [index];
+                       foreach (string suffix in suffixes) {
+                               sb.Append (separator);
+                               value = key != null ? key [suffix] : null;
+                               if (value == null)
+                                       continue;
+                               sb.Append (value.ToString ());
+                       }
+               }
+#endif
                internal string UniqueID2ClientID (string uniqueId)
                {
+                       if (String.IsNullOrEmpty (uniqueId))
+                               return null;
+                       
                        return uniqueId.Replace (IdSeparator, ClientIDSeparator);
                }
 
@@ -271,10 +524,16 @@ namespace System.Web.UI
 
                protected internal bool IsViewStateEnabled {
                        get {
-                               for (Control control = this; control != null; control = control.Parent)
+                               for (Control control = this; control != null; control = control.Parent) {
                                        if (!control.EnableViewState)
                                                return false;
-
+#if NET_4_0
+                                       ViewStateMode vsm = control.ViewStateMode;
+                                       if (vsm != ViewStateMode.Inherit)
+                                               return vsm == ViewStateMode.Enabled;
+#endif
+                               }
+                               
                                return true;
                        }
                }
@@ -286,6 +545,9 @@ namespace System.Web.UI
                [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
                [Browsable (false)]
                [WebSysDescription ("The container that this control is part of. The control's name has to be unique within the container.")]
+#if NET_4_0
+               [Bindable (true)]
+#endif
                public virtual Control NamingContainer {
                        get {
                                if (_namingContainer == null && _parent != null) {
@@ -320,6 +582,9 @@ namespace System.Web.UI
                [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
                [Browsable (false)]
                [WebSysDescription ("The parent control of this control.")]
+#if NET_4_0
+               [Bindable (true)]
+#endif
                public virtual Control Parent { //DIT
                        get { return _parent; }
                }
@@ -334,6 +599,9 @@ namespace System.Web.UI
 
                [Browsable (false)]
                [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+#if NET_4_0
+               [Bindable (true)]
+#endif
                public TemplateControl TemplateControl {
                        get { return TemplateControlInternal; }
 
@@ -397,13 +665,13 @@ namespace System.Web.UI
                                if (uniqueID != null)
                                        return uniqueID;
 
-                               if (NamingContainer == null)
+                               Control container = NamingContainer;
+                               if (container == null)
                                        return _userId;
 
                                EnsureIDInternal ();
-
-                               string prefix = NamingContainer.UniqueID;
-                               if (NamingContainer == Page || prefix == null) {
+                               string prefix = container.UniqueID;
+                               if (container == Page || prefix == null) {
                                        uniqueID = _userId;
 #if TARGET_J2EE
                                        if (getFacesContext () != null)
@@ -551,6 +819,11 @@ namespace System.Web.UI
                void NullifyUniqueID ()
                {
                        uniqueID = null;
+#if NET_4_0
+                       ClearCachedClientID ();
+#else
+                       clientID = null;
+#endif
                        if (!HasControls ())
                                return;
 
@@ -1345,10 +1618,12 @@ namespace System.Web.UI
                                trace.Write ("control", String.Concat ("LoadRecursive ", _userId, " ", type_name));
                        }
 #endif
-                       if (Adapter != null)
-                               Adapter.OnLoad (EventArgs.Empty);
-                       else
-                               OnLoad (EventArgs.Empty);
+                       if ((stateMask & LOADED) == 0) {
+                               if (Adapter != null)
+                                       Adapter.OnLoad (EventArgs.Empty);
+                               else
+                                       OnLoad (EventArgs.Empty);
+                       }
                        int ccount = _controls != null ? _controls.Count : 0;
                        for (int i = 0; i < ccount; i++) {
                                Control c = _controls [i];
@@ -1428,8 +1703,12 @@ namespace System.Web.UI
                        
                        stateMask |= PRERENDERED;
                }
-
-               internal void InitRecursive (Control namingContainer)
+#if NET_4_0
+               internal virtual
+#else
+               internal
+#endif
+               void InitRecursive (Control namingContainer)
                {
 #if MONO_TRACE
                        TraceContext trace = (Context != null && Context.Trace.IsEnabled) ? Context.Trace : null;
@@ -1823,5 +2102,72 @@ namespace System.Web.UI
                                return false;
                        }
                }
+#if NET_4_0
+               [ThemeableAttribute(false)]
+               [DefaultValue ("0")]
+               public virtual ViewStateMode ViewStateMode {
+                       get { return viewStateMode;  }
+                       set {
+                               if (value < ViewStateMode.Inherit || value > ViewStateMode.Disabled)
+                                       throw new ArgumentOutOfRangeException ("An attempt was made to set this property to a value that is not in the ViewStateMode enumeration.");
+
+                               viewStateMode = value;
+                       }
+               }
+
+               public string GetRouteUrl (object routeParameters)
+               {
+                       return GetRouteUrl (null, new RouteValueDictionary (routeParameters));
+               }
+
+               public string GetRouteUrl (RouteValueDictionary routeParameters)
+               {
+                       return GetRouteUrl (null, routeParameters);
+               }
+
+               public string GetRouteUrl (string routeName, object routeParameters)
+               {
+                       return GetRouteUrl (routeName, new RouteValueDictionary (routeParameters));
+               }
+
+               public string GetRouteUrl (string routeName, RouteValueDictionary routeParameters)
+               {
+                       HttpContext ctx = Context ?? HttpContext.Current;
+                       HttpRequest req = ctx != null ? ctx.Request : null;
+
+                       if (req == null)
+                               return null;
+
+                       VirtualPathData vpd = RouteTable.Routes.GetVirtualPath (req.RequestContext, routeName, routeParameters);
+                       if (vpd == null)
+                               return null;
+
+                       return vpd.VirtualPath;
+               }
+
+               public string GetUniqueIDRelativeTo (Control control)
+               {
+                       if (control == null)
+                               throw new ArgumentNullException ("control");
+
+                       Control parent = this;
+                       Control namingContainer = control.NamingContainer;
+                       
+                       if (namingContainer != null)
+                               while (parent != null && parent != namingContainer)
+                                       parent = parent.Parent;
+
+                       if (parent != namingContainer)
+                               throw new InvalidOperationException (
+                                       String.Format ("This control is not a descendant of the NamingContainer of '{0}'", control.UniqueID)
+                               );
+
+                       int idx = control.UniqueID.LastIndexOf (IdSeparator);
+                       if (idx < 0)
+                               return UniqueID;
+
+                       return UniqueID.Substring (idx + 1);
+               }
+#endif
        }
 }