refactoring
[mono.git] / mcs / class / System.Web / System.Web.UI.WebControls / BaseDataList.cs
1 //
2 // System.Web.UI.WebControls.BaseDataList.cs
3 //
4 // Author:
5 //      Sebastien Pouliot  <sebastien@ximian.com>
6 //
7 // Copyright (C) 2005 Novell, Inc (http://www.novell.com)
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 // 
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 // 
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28
29 using System.Collections;
30 using System.ComponentModel;
31 using System.Security.Permissions;
32
33 namespace System.Web.UI.WebControls {
34
35         // CAS
36         [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
37         [AspNetHostingPermission (SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
38         // attributes
39         [DefaultEvent ("SelectedIndexChanged")]
40         [DefaultProperty ("DataSource")]
41         [Designer ("System.Web.UI.Design.WebControls.BaseDataListDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
42         public abstract class BaseDataList : WebControl {
43
44                 private static readonly object selectedIndexChangedEvent = new object ();
45
46                 private DataKeyCollection keycoll;
47                 private object source;
48 #if NET_2_0
49                 //private string dataSourceId;
50                 IDataSource boundDataSource = null;
51                 private bool initialized;
52                 private bool requiresDataBinding;
53                 private DataSourceSelectArguments selectArguments;
54                 private IEnumerable data;
55 #endif
56
57                 protected BaseDataList ()
58                 {
59                 }
60
61
62                 [DefaultValue ("")]
63 #if NET_2_0
64                 [Localizable (true)]
65 #endif
66                 [WebSysDescription ("")]
67                 [WebCategory ("Accessibility")]
68                 public virtual string Caption {
69                         get { return ViewState.GetString ("Caption", ""); }
70                         set {
71                                 if (value == null)
72                                         ViewState.Remove ("Caption");
73                                 else
74                                         ViewState ["Caption"] = value;
75                         }
76                 }
77
78                 [DefaultValue (TableCaptionAlign.NotSet)]
79                 public virtual TableCaptionAlign CaptionAlign {
80                         get { return (TableCaptionAlign) ViewState.GetInt ("CaptionAlign", (int)TableCaptionAlign.NotSet); }
81                         set {
82                                 if ((value < TableCaptionAlign.NotSet) || (value > TableCaptionAlign.Right))
83                                         throw new ArgumentOutOfRangeException (Locale.GetText ("Invalid TableCaptionAlign value."));
84
85                                 ViewState ["CaptionAlign"] = value;
86                         }
87                 }
88
89 #if ONLY_1_1
90                 [Bindable (true)]
91 #endif
92                 [DefaultValue (-1)]
93                 [WebSysDescription("")]
94                 [WebCategory("Layout")]
95                 public virtual int CellPadding {
96                         get {
97                                 if (!ControlStyleCreated)
98                                         return -1; // default value
99                                 return TableStyle.CellPadding;
100                         }
101                         set { TableStyle.CellPadding = value; }
102                 }
103
104 #if ONLY_1_1
105                 [Bindable (true)]
106 #endif
107                 [DefaultValue (0)]
108                 [WebSysDescription("")]
109                 [WebCategory("Layout")]
110                 public virtual int CellSpacing {
111                         get {
112                                 if (!ControlStyleCreated)
113                                         return 0; // default value
114                                 return TableStyle.CellSpacing;
115                         }
116                         set { TableStyle.CellSpacing = value; }
117                 }
118
119                 public override ControlCollection Controls {
120                         get {
121                                 EnsureChildControls ();
122                                 return base.Controls;
123                         }
124                 }
125
126                 [DefaultValue ("")]
127 #if NET_2_0
128                 [Themeable (false)]
129 #endif
130                 [MonoTODO ("incomplete")]
131                 [WebSysDescription("")]
132                 [WebCategory("Data")]
133                 public virtual string DataKeyField {
134                         get { return ViewState.GetString ("DataKeyField", ""); }
135                         set {
136                                 if (value == null)
137                                         ViewState.Remove ("DataKeyField");
138                                 else
139                                         ViewState ["DataKeyField"] = value;
140                         }
141                 }
142
143                 [Browsable (false)]
144                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
145                 [WebSysDescription("")]
146                 [WebCategory("Data")]
147                 public DataKeyCollection DataKeys {
148                         get {
149                                 if (keycoll == null)
150                                         keycoll = new DataKeyCollection (DataKeysArray);
151                                 return keycoll;
152                         }
153                 }
154
155                 protected ArrayList DataKeysArray {
156                         get {
157                                 ArrayList keys = (ArrayList) ViewState ["DataKeys"];
158                                 if (keys == null) {
159                                         keys = new ArrayList ();
160                                         ViewState ["DataKeys"] = keys;
161                                 }
162                                 return keys;
163                         }
164                 }
165
166                 [DefaultValue ("")]
167 #if NET_2_0
168                 [Themeable (false)]
169 #endif
170                 [WebSysDescription("")]
171                 [WebCategory("Data")]
172                 public string DataMember {
173                         get { return ViewState.GetString ("DataMember", ""); }
174                         set {
175                                 if (value == null)
176                                         ViewState.Remove ("DataMember");
177                                 else
178                                         ViewState ["DataMember"] = value;
179 #if NET_2_0
180                                 if (!Initialized)
181                                         OnDataPropertyChanged ();
182 #endif
183                         }
184                 }
185
186                 [Bindable (true)]
187                 [DefaultValue (null)]
188                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
189 #if NET_2_0
190                 [Themeable (false)]
191 #endif
192                 [WebSysDescription("")]
193                 [WebCategory("Data")]
194                 public virtual object DataSource {
195                         get { return source; }
196                         set {
197                                 if ((value == null) || (value is IEnumerable) || (value is IListSource)) {
198 #if NET_2_0
199 // FIXME - can't duplicate in a test case ? LAMESPEC ?
200 // can't duplicate in a test case
201 //                                      if ((dataSourceId != null) && (dataSourceId.Length != 0))
202 //                                              throw new HttpException (Locale.GetText ("DataSourceID is already set."));
203
204                                         source = value;
205
206                                         if (!Initialized)
207                                                 OnDataPropertyChanged ();
208 #else
209                                         source = value;
210 #endif
211                                 } else {
212                                         string msg = Locale.GetText ("Invalid data source. This requires an object implementing {0} or {1}.",
213                                                 "IEnumerable", "IListSource");
214                                         throw new ArgumentException (msg);
215                                 }
216                         }
217                 }
218
219 #if ONLY_1_1
220                 [Bindable (true)]
221 #endif
222                 [DefaultValue (GridLines.Both)]
223                 [WebSysDescription("")]
224                 [WebCategory("Appearance")]
225                 public virtual GridLines GridLines {
226                         get {
227                                 if (!ControlStyleCreated)
228                                         return GridLines.Both; // default value
229                                 return TableStyle.GridLines;
230                         }
231                         set { TableStyle.GridLines = value; }
232                 }
233
234 #if ONLY_1_1
235                 [Bindable (true)]
236 #endif
237                 [Category ("Layout")]
238                 [DefaultValue (HorizontalAlign.NotSet)]
239                 [WebSysDescription("")]
240                 public virtual HorizontalAlign HorizontalAlign {
241                         get {
242                                 if (!ControlStyleCreated)
243                                         return HorizontalAlign.NotSet; // default value
244                                 return TableStyle.HorizontalAlign;
245                         }
246                         set { TableStyle.HorizontalAlign = value; }
247                 }
248
249                 [DefaultValue (false)]
250                 public virtual bool UseAccessibleHeader {
251                         get { return ViewState.GetBool ("UseAccessibleHeader", false); }
252                         set { ViewState ["UseAccessibleHeader"] = value; }
253                 }
254 #if NET_2_0
255                 [DefaultValue ("")]
256                 [IDReferenceProperty (typeof (DataSourceControl))]
257                 [Themeable (false)]
258                 public virtual string DataSourceID {
259                         get { return ViewState.GetString ("DataSourceID", ""); }
260                         set {
261                                 // LAMESPEC ? this is documented as an HttpException in beta2
262                                 if (source != null)
263                                         throw new InvalidOperationException (Locale.GetText ("DataSource is already set."));
264
265                                 ViewState ["DataSourceID"] = value;
266
267                                 if (!Initialized)
268                                         OnDataPropertyChanged ();
269                         }
270                 }
271
272                 protected bool Initialized {
273                         get { return initialized; }
274                 }
275
276                 // as documented in BaseDataBoundControl
277                 protected bool IsBoundUsingDataSourceID {
278                         get { return (DataSourceID.Length != 0); }
279                 }
280
281                 // doc says ?automatically? called by ASP.NET
282                 protected bool RequiresDataBinding {
283                         get { return requiresDataBinding; }
284                         set { requiresDataBinding = value; }
285                 }
286
287                 protected DataSourceSelectArguments SelectArguments {
288                         get {
289                                 if (selectArguments == null)
290                                         selectArguments = CreateDataSourceSelectArguments ();
291                                 return selectArguments;
292                         }
293                 }
294 #endif
295                 private TableStyle TableStyle {
296                         // this will throw an InvalidCasException just like we need
297                         get { return (TableStyle) ControlStyle; }
298                 }
299
300
301                 protected override void AddParsedSubObject (object obj)
302                 {
303                         // don't accept controls
304                 }
305
306                 // see Kothari, page 435
307 #if NET_2_0
308                 protected internal
309 #else           
310                 protected
311 #endif          
312                 override void CreateChildControls ()
313                 {
314                         // We are recreating the children from viewstate
315                         if (HasControls ())
316                                 Controls.Clear();
317
318                         // If presents, build the children from the viewstate
319                         if (ViewState ["Items"] != null)
320                                 CreateControlHierarchy (false);
321                 }
322
323                 protected abstract void CreateControlHierarchy (bool useDataSource);
324
325                 // see Kothari, page 434
326                 // see also: Control.DataBind on Fx 2.0 beta2 documentation
327                 public override void DataBind ()
328                 {
329                         // unlike most samples we don't call base.OnDataBinding
330                         // because we override it in this class
331                         OnDataBinding (EventArgs.Empty);
332
333                         // Clear, if required, then recreate the control hierarchy
334                         if (HasControls ())
335                                 Controls.Clear ();
336                         if (HasChildViewState)
337                                 ClearChildViewState ();
338                         if (!IsTrackingViewState)
339                                 TrackViewState ();
340                         CreateControlHierarchy (true);
341
342                         // Indicate that child controls have been created, preventing
343                         // CreateChildControls from getting called.
344                         ChildControlsCreated = true;    
345                 }
346 #if NET_2_0
347                 protected virtual DataSourceSelectArguments CreateDataSourceSelectArguments ()
348                 {
349                         return DataSourceSelectArguments.Empty;
350                 }
351
352                 // best documentation is (again) in BaseDataBoundControl
353                 protected void EnsureDataBound ()
354                 {
355                         if (IsBoundUsingDataSourceID && RequiresDataBinding)
356                                 DataBind ();
357                 }
358
359                 private void SelectCallback (IEnumerable data)
360                 {
361                         this.data = data;
362                 }
363
364                 protected virtual IEnumerable GetData ()
365                 {
366                         if (DataSourceID.Length == 0)
367                                 return null;
368
369                         if (boundDataSource == null)
370                                 ConnectToDataSource ();
371
372                         DataSourceView dsv = boundDataSource.GetView (String.Empty);
373                         dsv.Select (SelectArguments, new DataSourceViewSelectCallback (SelectCallback));
374                         return data;
375                 }
376 #endif
377
378                 protected override void OnDataBinding (EventArgs e)
379                 {
380                         base.OnDataBinding (e);
381                 }
382 #if NET_2_0
383                 protected virtual void OnDataPropertyChanged ()
384                 {
385                 }
386
387                 protected virtual void OnDataSourceViewChanged (object sender, EventArgs e)
388                 {
389                         RequiresDataBinding = true;
390                 }
391
392                 protected internal override void OnInit (EventArgs e)
393                 {
394                         base.OnInit (e);
395                 }
396
397                 protected internal override void OnLoad (EventArgs e)
398                 {
399                         if ((Page != null) && !Page.IsPostBack)
400                                 RequiresDataBinding = true;
401
402                         if (IsBoundUsingDataSourceID)
403                                 ConnectToDataSource ();
404
405                         initialized = true;
406                         base.OnLoad (e);
407                 }
408
409                 protected internal override void OnPreRender (EventArgs e)
410                 {
411                         EnsureDataBound ();
412                         base.OnPreRender (e);
413                 }
414 #endif
415                 protected virtual void OnSelectedIndexChanged (EventArgs e)
416                 {
417                         EventHandler selectedIndexChanged = (EventHandler) Events [selectedIndexChangedEvent];
418                         if (selectedIndexChanged != null)
419                                 selectedIndexChanged (this, e);
420                 }
421
422                 protected abstract void PrepareControlHierarchy ();
423
424 #if NET_2_0
425                 protected internal
426 #else           
427                 protected
428 #endif          
429                 override void Render (HtmlTextWriter writer)
430                 {
431                         PrepareControlHierarchy ();
432                         // don't call base class or RenderBegin|EndTag
433                         // or we'll get an extra <span></span>
434                         RenderContents (writer);
435                 }
436
437
438                 [WebSysDescription("")]
439                 [WebCategory("Action")]
440                 public event EventHandler SelectedIndexChanged {
441                         add { Events.AddHandler (selectedIndexChangedEvent, value); }
442                         remove { Events.RemoveHandler (selectedIndexChangedEvent, value); }
443                 }
444
445
446                 static public bool IsBindableType (Type type)
447                 {
448                         // I can't believe how many NRE are possible in System.Web
449                         if (type == null) // Type.GetTypeCode no longer throws when a null is passed.
450                                 throw new NullReferenceException ();
451
452                         switch (Type.GetTypeCode (type)) {
453                         case TypeCode.Boolean:
454                         case TypeCode.Byte:
455                         case TypeCode.SByte:
456                         case TypeCode.Int16:
457                         case TypeCode.UInt16:
458                         case TypeCode.Int32:
459                         case TypeCode.UInt32:
460                         case TypeCode.Int64:
461                         case TypeCode.UInt64:
462                         case TypeCode.Char:
463                         case TypeCode.Double:
464                         case TypeCode.Single:
465                         case TypeCode.DateTime:
466                         case TypeCode.Decimal:
467                         case TypeCode.String:
468                                 return true;
469                         default:
470                                 return false;
471                         }
472                 }
473
474 #if NET_2_0
475                 void ConnectToDataSource ()
476                 {
477                         if (NamingContainer != null)
478                                 boundDataSource = (NamingContainer.FindControl (DataSourceID) as IDataSource);
479
480                         if (boundDataSource == null) {
481                                 if (Parent != null)
482                                         boundDataSource = (Parent.FindControl (DataSourceID) as IDataSource);
483
484                                 if (boundDataSource == null)
485                                         throw new HttpException (Locale.GetText ("Coulnd't find a DataSource named '{0}'.", DataSourceID));
486                         }
487                         DataSourceView dsv = boundDataSource.GetView (String.Empty);
488                         dsv.DataSourceViewChanged += new EventHandler (OnDataSourceViewChanged);
489                 }
490 #endif
491         }
492 }