2008-03-13 Marek Habersack <mhabersack@novell.com>
[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                                 OnDataPropertyChanged ();
181 #endif
182                         }
183                 }
184
185                 [Bindable (true)]
186                 [DefaultValue (null)]
187                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
188 #if NET_2_0
189                 [Themeable (false)]
190 #endif
191                 [WebSysDescription("")]
192                 [WebCategory("Data")]
193                 public virtual object DataSource {
194                         get { return source; }
195                         set {
196                                 if ((value == null) || (value is IEnumerable) || (value is IListSource)) {
197 #if NET_2_0
198 // FIXME - can't duplicate in a test case ? LAMESPEC ?
199 // can't duplicate in a test case
200 //                                      if ((dataSourceId != null) && (dataSourceId.Length != 0))
201 //                                              throw new HttpException (Locale.GetText ("DataSourceID is already set."));
202
203                                         source = value;
204
205                                         OnDataPropertyChanged ();
206 #else
207                                         source = value;
208 #endif
209                                 } else {
210                                         string msg = Locale.GetText ("Invalid data source. This requires an object implementing {0} or {1}.",
211                                                 "IEnumerable", "IListSource");
212                                         throw new ArgumentException (msg);
213                                 }
214                         }
215                 }
216
217 #if ONLY_1_1
218                 [Bindable (true)]
219 #endif
220                 [DefaultValue (GridLines.Both)]
221                 [WebSysDescription("")]
222                 [WebCategory("Appearance")]
223                 public virtual GridLines GridLines {
224                         get {
225                                 if (!ControlStyleCreated)
226                                         return GridLines.Both; // default value
227                                 return TableStyle.GridLines;
228                         }
229                         set { TableStyle.GridLines = value; }
230                 }
231
232 #if ONLY_1_1
233                 [Bindable (true)]
234 #endif
235                 [Category ("Layout")]
236                 [DefaultValue (HorizontalAlign.NotSet)]
237                 [WebSysDescription("")]
238                 public virtual HorizontalAlign HorizontalAlign {
239                         get {
240                                 if (!ControlStyleCreated)
241                                         return HorizontalAlign.NotSet; // default value
242                                 return TableStyle.HorizontalAlign;
243                         }
244                         set { TableStyle.HorizontalAlign = value; }
245                 }
246
247                 [DefaultValue (false)]
248                 public virtual bool UseAccessibleHeader {
249                         get { return ViewState.GetBool ("UseAccessibleHeader", false); }
250                         set { ViewState ["UseAccessibleHeader"] = value; }
251                 }
252 #if NET_2_0
253                 [DefaultValue ("")]
254                 [IDReferenceProperty (typeof (DataSourceControl))]
255                 [Themeable (false)]
256                 public virtual string DataSourceID {
257                         get { return ViewState.GetString ("DataSourceID", ""); }
258                         set {
259                                 // LAMESPEC ? this is documented as an HttpException in beta2
260                                 if (source != null)
261                                         throw new InvalidOperationException (Locale.GetText ("DataSource is already set."));
262
263                                 ViewState ["DataSourceID"] = value;
264
265                                 OnDataPropertyChanged ();
266                         }
267                 }
268
269                 protected bool Initialized {
270                         get { return initialized; }
271                 }
272
273                 // as documented in BaseDataBoundControl
274                 protected bool IsBoundUsingDataSourceID {
275                         get { return (DataSourceID.Length != 0); }
276                 }
277
278                 // doc says ?automatically? called by ASP.NET
279                 protected bool RequiresDataBinding {
280                         get { return requiresDataBinding; }
281                         set { requiresDataBinding = value; }
282                 }
283
284                 protected DataSourceSelectArguments SelectArguments {
285                         get {
286                                 if (selectArguments == null)
287                                         selectArguments = CreateDataSourceSelectArguments ();
288                                 return selectArguments;
289                         }
290                 }
291 #endif
292                 private TableStyle TableStyle {
293                         // this will throw an InvalidCasException just like we need
294                         get { return (TableStyle) ControlStyle; }
295                 }
296
297
298                 protected override void AddParsedSubObject (object obj)
299                 {
300                         // don't accept controls
301                 }
302
303                 // see Kothari, page 435
304 #if NET_2_0
305                 protected internal
306 #else           
307                 protected
308 #endif          
309                 override void CreateChildControls ()
310                 {
311                         // We are recreating the children from viewstate
312                         if (HasControls ())
313                                 base.Controls.Clear();
314
315 #if NET_2_0
316                         if (IsDataBound)
317                                 CreateControlHierarchy (false);
318                         else if (RequiresDataBinding)
319                                 EnsureDataBound ();
320 #else
321                         // If presents, build the children from the viewstate
322                         if (ViewState ["Items"] != null)
323                                 CreateControlHierarchy (false);
324 #endif
325                 }
326
327                 protected abstract void CreateControlHierarchy (bool useDataSource);
328
329                 // see Kothari, page 434
330                 // see also: Control.DataBind on Fx 2.0 beta2 documentation
331                 public override void DataBind ()
332                 {
333                         // unlike most samples we don't call base.OnDataBinding
334                         // because we override it in this class
335                         OnDataBinding (EventArgs.Empty);
336
337                         // Clear, if required, then recreate the control hierarchy
338                         if (HasControls ())
339                                 Controls.Clear ();
340                         if (HasChildViewState)
341                                 ClearChildViewState ();
342                         if (!IsTrackingViewState)
343                                 TrackViewState ();
344                         CreateControlHierarchy (true);
345
346                         // Indicate that child controls have been created, preventing
347                         // CreateChildControls from getting called.
348                         ChildControlsCreated = true;    
349 #if NET_2_0
350                         RequiresDataBinding = false;
351                         IsDataBound = true;
352 #endif
353                 }
354 #if NET_2_0
355                 protected virtual DataSourceSelectArguments CreateDataSourceSelectArguments ()
356                 {
357                         return DataSourceSelectArguments.Empty;
358                 }
359
360                 // best documentation is (again) in BaseDataBoundControl
361                 protected void EnsureDataBound ()
362                 {
363                         if (IsBoundUsingDataSourceID && RequiresDataBinding)
364                                 DataBind ();
365                 }
366
367                 private void SelectCallback (IEnumerable data)
368                 {
369                         this.data = data;
370                 }
371
372                 protected virtual IEnumerable GetData ()
373                 {
374                         if (DataSourceID.Length == 0)
375                                 return null;
376
377                         if (boundDataSource == null)
378                                 ConnectToDataSource ();
379
380                         DataSourceView dsv = boundDataSource.GetView (String.Empty);
381                         dsv.Select (SelectArguments, new DataSourceViewSelectCallback (SelectCallback));
382                         return data;
383                 }
384
385                 bool IsDataBound {
386                         get {
387                                 return ViewState.GetBool ("_DataBound", false);
388                         }
389                         set {
390                                 ViewState ["_DataBound"] = value;
391                         }
392                 }
393
394 #endif
395
396                 protected override void OnDataBinding (EventArgs e)
397                 {
398                         base.OnDataBinding (e);
399                 }
400 #if NET_2_0
401                 protected virtual void OnDataPropertyChanged ()
402                 {
403                         if (Initialized)
404                                 RequiresDataBinding = true;
405                 }
406
407                 protected virtual void OnDataSourceViewChanged (object sender, EventArgs e)
408                 {
409                         RequiresDataBinding = true;
410                 }
411
412                 protected internal override void OnInit (EventArgs e)
413                 {
414                         base.OnInit (e);
415                         if (Page != null) {
416                                 Page.PreLoad += new EventHandler (OnPagePreLoad);
417
418                                 if (!IsViewStateEnabled && Page.IsPostBack)
419                                         RequiresDataBinding = true;
420                         }
421                 }
422                 
423                 protected virtual void OnPagePreLoad (object sender, EventArgs e)
424                 {
425                         Initialize ();
426                 }
427                 
428                 protected internal override void OnLoad (EventArgs e)
429                 {
430                         if (!Initialized)
431                                 Initialize ();
432
433                         base.OnLoad (e);
434                 }
435                 
436                 private void Initialize ()
437                 {
438                         if (Page != null) {
439                                 if (!Page.IsPostBack || (IsViewStateEnabled && !IsDataBound))
440                                         RequiresDataBinding = true;
441                         }
442
443                         if (IsBoundUsingDataSourceID)
444                                 ConnectToDataSource ();
445
446                         initialized = true;
447                 }
448
449                 protected internal override void OnPreRender (EventArgs e)
450                 {
451                         EnsureDataBound ();
452                         base.OnPreRender (e);
453                 }
454 #endif
455                 protected virtual void OnSelectedIndexChanged (EventArgs e)
456                 {
457                         EventHandler selectedIndexChanged = (EventHandler) Events [selectedIndexChangedEvent];
458                         if (selectedIndexChanged != null)
459                                 selectedIndexChanged (this, e);
460                 }
461
462                 protected abstract void PrepareControlHierarchy ();
463
464 #if NET_2_0
465                 protected internal
466 #else           
467                 protected
468 #endif          
469                 override void Render (HtmlTextWriter writer)
470                 {
471                         PrepareControlHierarchy ();
472                         // don't call base class or RenderBegin|EndTag
473                         // or we'll get an extra <span></span>
474                         RenderContents (writer);
475                 }
476
477
478                 [WebSysDescription("")]
479                 [WebCategory("Action")]
480                 public event EventHandler SelectedIndexChanged {
481                         add { Events.AddHandler (selectedIndexChangedEvent, value); }
482                         remove { Events.RemoveHandler (selectedIndexChangedEvent, value); }
483                 }
484
485
486                 static public bool IsBindableType (Type type)
487                 {
488                         // I can't believe how many NRE are possible in System.Web
489                         if (type == null) // Type.GetTypeCode no longer throws when a null is passed.
490                                 throw new NullReferenceException ();
491
492                         switch (Type.GetTypeCode (type)) {
493                         case TypeCode.Boolean:
494                         case TypeCode.Byte:
495                         case TypeCode.SByte:
496                         case TypeCode.Int16:
497                         case TypeCode.UInt16:
498                         case TypeCode.Int32:
499                         case TypeCode.UInt32:
500                         case TypeCode.Int64:
501                         case TypeCode.UInt64:
502                         case TypeCode.Char:
503                         case TypeCode.Double:
504                         case TypeCode.Single:
505                         case TypeCode.DateTime:
506                         case TypeCode.Decimal:
507                         case TypeCode.String:
508                                 return true;
509                         default:
510                                 return false;
511                         }
512                 }
513
514 #if NET_2_0
515                 void ConnectToDataSource ()
516                 {
517                         if (NamingContainer != null)
518                                 boundDataSource = (NamingContainer.FindControl (DataSourceID) as IDataSource);
519
520                         if (boundDataSource == null) {
521                                 if (Parent != null)
522                                         boundDataSource = (Parent.FindControl (DataSourceID) as IDataSource);
523
524                                 if (boundDataSource == null)
525                                         throw new HttpException (Locale.GetText ("Coulnd't find a DataSource named '{0}'.", DataSourceID));
526                         }
527                         DataSourceView dsv = boundDataSource.GetView (String.Empty);
528                         dsv.DataSourceViewChanged += new EventHandler (OnDataSourceViewChanged);
529                 }
530 #endif
531         }
532 }