merge -r 98047:98048
[mono.git] / mcs / class / System.Web / System.Web.UI.WebControls / Repeater.cs
1 //
2 // System.Web.UI.WebControls.Repeater.cs
3 //
4 // Authors:
5 //      Ben Maurer (bmaurer@novell.com)
6 //
7 // (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 // Helpful resources while implementing this class:
30 //
31 // _Developing Microsoft ASP.NET Server Controls and Components_ (Kothari, Datye)
32 //    Chapters 16 and 20 (especially listing 20-3 on page 559)
33 //
34 // "Building DataBound Templated Custom ASP.NET Server Controls" (Mitchell) on MSDN
35 //    Right now, http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnaspp/html/databoundtemplatedcontrols.asp
36 //    works, but with msdn we all know that urls have a very short lifetime :-)
37 //
38
39 using System.Collections;
40 using System.ComponentModel;
41 using System.Security.Permissions;
42 using System.Web.Util;
43
44 namespace System.Web.UI.WebControls {
45
46         // CAS
47         [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
48         [AspNetHostingPermission (SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
49         // attributes
50         [DefaultEvent ("ItemCommand")]
51         [DefaultProperty ("DataSource")]
52         [Designer ("System.Web.UI.Design.WebControls.RepeaterDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
53         [ParseChildren (true)]
54         [PersistChildren (false)]
55         public class Repeater : Control, INamingContainer {
56
57                 object dataSource;
58 #if NET_2_0
59                 IDataSource boundDataSource;
60                 private bool initialized;
61                 private bool preRendered = false;
62                 private bool requiresDataBinding;
63                 private DataSourceSelectArguments selectArguments;
64                 private IEnumerable data;
65 #endif
66
67                 // See Kothari, listing 20-3
68 #if NET_2_0
69                 protected internal
70 #else           
71                 protected
72 #endif          
73                 override void CreateChildControls ()
74                 {
75                         // We are recreating the children from viewstate
76                         Controls.Clear();
77
78                         // Build the children from the viewstate
79                         if (ViewState ["Items"] != null)
80                                 CreateControlHierarchy (false);
81                 }
82                 
83                 // See Kothari, listing 20-3
84                 protected override void OnDataBinding (EventArgs e)
85                 {
86                         base.OnDataBinding (EventArgs.Empty);
87
88                         Controls.Clear ();
89                         ClearChildViewState ();
90                         TrackViewState ();
91
92                         CreateControlHierarchy (true);
93
94                         ChildControlsCreated = true;
95                 }
96
97                 void DoItem (int i, ListItemType t, object d, bool databind)
98                 {
99                         RepeaterItem itm = CreateItem (i, t);
100
101                         if (t == ListItemType.Item || t == ListItemType.AlternatingItem)
102                                 items.Add (itm);
103                         
104                         itm.DataItem = d;
105                         RepeaterItemEventArgs e = new RepeaterItemEventArgs (itm);
106                         InitializeItem (itm);
107                         
108                         //
109                         // It is very important that this be called *before* data
110                         // binding. Otherwise, we won't save our state in the viewstate.
111                         //
112                         Controls.Add (itm);
113                         OnItemCreated (e);
114
115                         if (databind) {
116                                 itm.DataBind ();
117                                 OnItemDataBound (e);
118                         }
119                 }
120                 
121                 protected virtual void CreateControlHierarchy (bool useDataSource)
122                 {
123                         IEnumerable ds;
124                         items = new ArrayList ();
125                         itemscol = null;
126                         
127                         if (useDataSource) {
128                                 ds = GetData ();
129                         }
130                         else {
131                                 // Optimize (shouldn't need all this memory ;-)
132                                 ds = new object [(int) ViewState ["Items"]];
133                         }
134
135                         // If there is no datasource, then we don't show anything. the "Items"
136                         // viewstate won't get set, so on postback, we won't get here
137                         if (ds == null)
138                                 return;
139
140                         if (HeaderTemplate != null)
141                                 DoItem (-1, ListItemType.Header, null, useDataSource);
142
143                         int idx = 0;
144                         foreach (object o in ds) {
145                                 if (idx != 0 && SeparatorTemplate != null)
146                                         DoItem (idx - 1, ListItemType.Separator, null, useDataSource);
147
148                                 DoItem (idx, idx % 2 == 0 ? ListItemType.Item : ListItemType.AlternatingItem, o, useDataSource);
149                                 idx ++;
150                         }
151                         
152                         if (FooterTemplate != null)
153                                 DoItem (-1, ListItemType.Footer, null, useDataSource);
154
155                         ViewState ["Items"] = idx;
156                 }
157                 
158                 // Why does this get overriden?
159                 public override void DataBind ()
160                 {
161                         // In all the examples I've seen online, this does base.OnDataBinding and
162                         // then does all the create child controls stuff. But from stack traces on
163                         // windows, this doesn't seem to be the case here.
164                         OnDataBinding (EventArgs.Empty);
165
166 #if NET_2_0
167                         RequiresDataBinding = false;
168 #endif
169                 }
170                 
171                 protected virtual RepeaterItem CreateItem (int itemIndex, ListItemType itemType)
172                 {
173                         return new RepeaterItem (itemIndex, itemType);
174                 }
175                 
176                 protected virtual void InitializeItem (RepeaterItem item)
177                 {
178                         ITemplate t = null;
179                         
180                         switch (item.ItemType) {
181                         case ListItemType.Header:
182                                 t = HeaderTemplate;
183                                 break;
184                         case ListItemType.Footer:
185                                 t = FooterTemplate;
186                                 break;  
187                         case ListItemType.Item:
188                                 t = ItemTemplate;
189                                 break;
190                         case ListItemType.AlternatingItem:
191                                 t = AlternatingItemTemplate;
192                                 if (t == null)
193                                         t = ItemTemplate;
194                                 break;
195                         case ListItemType.Separator:
196                                 t = SeparatorTemplate;
197                                 break;
198                         }
199
200                         if (t != null)
201                                 t.InstantiateIn (item);                 
202                 }
203                 
204
205                 protected override bool OnBubbleEvent (object sender, EventArgs e)
206                 {
207                         RepeaterCommandEventArgs rcea = e as RepeaterCommandEventArgs;
208                         if (rcea != null) {
209                                 OnItemCommand (rcea);
210                                 return true;
211                         }
212
213                         return false;
214                 }
215
216         
217                 public override ControlCollection Controls {
218                         get {
219                                 EnsureChildControls ();
220                                 return base.Controls;
221                         }
222                         
223                 }
224
225                 RepeaterItemCollection itemscol;
226                 ArrayList items;
227                 [Browsable(false)]
228                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
229                 [WebSysDescription ("")]
230                 public virtual RepeaterItemCollection Items {
231                         get {
232                                 if (itemscol == null) {
233                                         if (items == null)
234                                                 items = new ArrayList ();
235
236                                         itemscol = new RepeaterItemCollection (items);
237                                 }
238                                 return itemscol;
239                         }
240                 }
241                 
242                 [DefaultValue("")]
243                 [WebSysDescription ("")]
244                 [WebCategory ("Data")]
245                 public virtual string DataMember {
246                         get {
247                                 return ViewState.GetString ("DataMember", "");
248                         }
249                         set {
250                                 if (value == null)
251                                         ViewState.Remove ("DataMember");
252                                 else
253                                         ViewState ["DataMember"] = value;
254
255 #if NET_2_0
256                                 if (!Initialized)
257                                         OnDataPropertyChanged ();
258 #endif
259                         }
260                 }
261
262                 [Bindable(true)]
263                 [DefaultValue(null)]
264                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
265                 [WebSysDescription ("")]
266                 [WebCategory ("Data")]
267                 public virtual object DataSource {
268                         get {
269                                 return dataSource;
270                         }
271                         
272                         set {
273                                 if (value == null || value is IListSource || value is IEnumerable) {
274 #if NET_2_0
275 // FIXME - can't duplicate in a test case ? LAMESPEC ?
276 // can't duplicate in a test case
277 //                                      if ((dataSourceId != null) && (dataSourceId.Length != 0))
278 //                                              throw new HttpException (Locale.GetText ("DataSourceID is already set."));
279
280                                         dataSource = value;
281
282                                         if (!Initialized)
283                                                 OnDataPropertyChanged ();
284 #else
285                                         dataSource = value;
286 #endif
287                                 } else
288                                         throw new ArgumentException (String.Format (
289                                             "An invalid data source is being used for {0}. A valid data source must implement either IListSource or IEnumerable",
290                                             ID));
291                         }
292                 }
293
294 #if NET_2_0
295                 [DefaultValue ("")]
296                 [IDReferenceProperty (typeof (DataSourceControl))]
297                 public virtual string DataSourceID
298                 {
299                         get {
300                                 return ViewState.GetString ("DataSourceID", "");
301                         }
302                         set {
303                                 if (dataSource != null)
304                                         throw new HttpException ("Only one of DataSource and DataSourceID can be specified.");
305                                 ViewState ["DataSourceID"] = value;
306
307                                 if (!Initialized)
308                                         OnDataPropertyChanged ();
309                         }
310                 }
311
312                 [Browsable (true)]
313                 [DefaultValue(false)]
314                 public override bool EnableTheming {
315                         get { return base.EnableTheming; }
316                         set { base.EnableTheming = value; }
317                 }
318 #endif          
319
320                 ITemplate alt_itm_tmpl;
321                 [Browsable(false)]
322                 [DefaultValue(null)]
323                 [PersistenceMode(PersistenceMode.InnerProperty)]
324                 [TemplateContainer (typeof (RepeaterItem))]
325                 [WebSysDescription ("")]
326                 public virtual ITemplate AlternatingItemTemplate {
327                         get {
328                                 return alt_itm_tmpl;
329                         }
330                         set {
331                                 alt_itm_tmpl = value;
332                         }
333                 }               
334                 
335                 ITemplate footer_tmpl;
336                 [Browsable(false)]
337                 [DefaultValue(null)]
338                 [PersistenceMode(PersistenceMode.InnerProperty)]
339                 [TemplateContainer (typeof (RepeaterItem))]
340                 [WebSysDescription ("")]
341                 public virtual ITemplate FooterTemplate {
342                         get {
343                                 return footer_tmpl;
344                         }
345                         set {
346                                 footer_tmpl = value;
347                         }
348                 }
349
350                 ITemplate header_tmpl;
351                 [Browsable(false)]
352                 [DefaultValue(null)]
353                 [PersistenceMode(PersistenceMode.InnerProperty)]
354                 [TemplateContainer (typeof (RepeaterItem))]
355                 [WebSysDescription ("")]
356                 public virtual ITemplate HeaderTemplate {
357                         get {
358                                 return header_tmpl;
359                         }
360                         set {
361                                 header_tmpl = value;
362                         }
363                 }
364
365                 ITemplate item_tmpl;
366                 [Browsable(false)]
367                 [DefaultValue(null)]
368                 [PersistenceMode(PersistenceMode.InnerProperty)]
369                 [TemplateContainer (typeof (RepeaterItem))]
370                 [WebSysDescription ("")]
371                 public virtual ITemplate ItemTemplate {
372                         get {
373                                 return item_tmpl;
374                         }
375                         set {
376                                 item_tmpl = value;
377                         }
378                 }
379
380                 ITemplate separator_tmpl;
381                 [Browsable(false)]
382                 [DefaultValue(null)]
383                 [PersistenceMode(PersistenceMode.InnerProperty)]
384                 [TemplateContainer (typeof (RepeaterItem))]
385                 [WebSysDescription ("")]
386                 public virtual ITemplate SeparatorTemplate {
387                         get {
388                                 return separator_tmpl;
389                         }
390                         set {
391                                 separator_tmpl = value;
392                         }
393                 }
394
395                 
396                 protected virtual void OnItemCommand (RepeaterCommandEventArgs e)
397                 {
398                         RepeaterCommandEventHandler h = (RepeaterCommandEventHandler) Events [ItemCommandEvent];
399                         if (h != null)
400                                 h (this, e);
401                 }
402
403                 static readonly object ItemCommandEvent = new object ();
404
405                 [WebSysDescription ("")]
406                 [WebCategory ("Action")]
407                 public event RepeaterCommandEventHandler ItemCommand {
408                         add { Events.AddHandler (ItemCommandEvent, value); }
409                         remove { Events.RemoveHandler (ItemCommandEvent, value); }
410                 }
411
412                 
413                 protected virtual void OnItemCreated (RepeaterItemEventArgs e)
414                 {
415                         RepeaterItemEventHandler h = (RepeaterItemEventHandler) Events [ItemCreatedEvent];
416                         if (h != null)
417                                 h (this, e);
418                 }
419
420                 static readonly object ItemCreatedEvent = new object ();
421
422                 [WebSysDescription ("")]
423                 [WebCategory ("Behavior")]
424                 public event RepeaterItemEventHandler ItemCreated {
425                         add { Events.AddHandler (ItemCreatedEvent, value); }
426                         remove { Events.RemoveHandler (ItemCreatedEvent, value); }
427                 }
428                 
429                 protected virtual void OnItemDataBound (RepeaterItemEventArgs e) 
430                 {
431                         RepeaterItemEventHandler h = (RepeaterItemEventHandler) Events [ItemDataBoundEvent];
432                         if (h != null)
433                                 h (this, e);
434                 }
435                 
436                 static readonly object ItemDataBoundEvent = new object ();
437
438                 [WebSysDescription ("")]
439                 [WebCategory ("Behavior")]
440                 public event RepeaterItemEventHandler ItemDataBound {
441                         add { Events.AddHandler (ItemDataBoundEvent, value); }
442                         remove { Events.RemoveHandler (ItemDataBoundEvent, value); }
443                 }
444
445 #if NET_2_0
446                 protected bool Initialized {
447                         get { return initialized; }
448                 }
449
450                 protected bool IsBoundUsingDataSourceID
451                 {
452                         get { return (DataSourceID.Length != 0); }
453                 }
454
455                 protected bool RequiresDataBinding
456                 {
457                         get { return requiresDataBinding; }
458                         set { 
459                                 requiresDataBinding = value;
460                                 if (value && preRendered && IsBoundUsingDataSourceID && Page != null && !Page.IsCallback)
461                                         EnsureDataBound ();
462                         }
463                 }
464
465                 protected DataSourceSelectArguments SelectArguments
466                 {
467                         get {
468                                 // MSDN: The first call to the SelectArguments property calls the 
469                                 // CreateDataSourceSelectArguments method to return the Empty value.
470                                 if (selectArguments == null)
471                                         selectArguments = CreateDataSourceSelectArguments();
472                                 return selectArguments;
473                         }
474                 }
475
476                 protected virtual DataSourceSelectArguments CreateDataSourceSelectArguments ()
477                 {
478                         // MSDN: Returns the Empty value. 
479                         return DataSourceSelectArguments.Empty;
480                 }
481
482                 protected void EnsureDataBound ()
483                 {
484                         if (IsBoundUsingDataSourceID && RequiresDataBinding)
485                                 DataBind ();
486                 }
487
488                 private void SelectCallback (IEnumerable data)
489                 {
490                         this.data = data;
491                 }
492
493 #endif
494 #if NET_2_0
495                 protected virtual 
496 #endif
497                 IEnumerable GetData ()
498                 {
499                         IEnumerable result;
500 #if NET_2_0
501                         if (IsBoundUsingDataSourceID) {
502                                 if (DataSourceID.Length == 0)
503                                         return null;
504
505                                 if (boundDataSource == null)
506                                         return null;
507
508                                 DataSourceView dsv = boundDataSource.GetView (String.Empty);
509                                 dsv.Select (SelectArguments, new DataSourceViewSelectCallback (SelectCallback));
510
511                                 result = data;
512                                 data = null;
513                         }
514                         else
515 #endif
516                                 result = DataSourceResolver.ResolveDataSource (DataSource, DataMember);
517
518                         return result;
519                 }
520
521 #if NET_2_0
522                 protected virtual void OnDataPropertyChanged ()
523                 {
524                         if (Initialized)
525                                 RequiresDataBinding = true;
526                 }
527
528                 protected virtual void OnDataSourceViewChanged (object sender, EventArgs e)
529                 {
530                         RequiresDataBinding = true;
531                 }
532
533                 protected internal override void OnInit (EventArgs e)
534                 {
535                         base.OnInit (e);
536                         if (Page != null) {
537                                 Page.PreLoad += new EventHandler (OnPagePreLoad);
538
539                                 if (!IsViewStateEnabled && Page.IsPostBack)
540                                         RequiresDataBinding = true;
541                         }
542                 }
543
544                 protected virtual void OnPagePreLoad (object sender, EventArgs e) 
545                 {
546                         Initialize ();
547                 }
548
549                 protected internal override void OnLoad (EventArgs e)
550                 {
551                         if (!Initialized)
552                                 Initialize ();
553
554                         base.OnLoad (e);
555                 }
556
557                 private void Initialize () 
558                 {
559                         if (Page != null) {
560                                 if (!Page.IsPostBack || (IsViewStateEnabled && (ViewState ["Items"] == null)))
561                                         RequiresDataBinding = true;
562                         }
563                         
564                         if (IsBoundUsingDataSourceID)
565                                 ConnectToDataSource ();
566                 
567                         initialized = true;
568                 }
569
570                 protected internal override void OnPreRender (EventArgs e)
571                 {
572                         preRendered = true;
573                         EnsureDataBound ();
574                         base.OnPreRender (e);
575                 }
576
577                 void ConnectToDataSource ()
578                 {
579                         /* verify that the data source exists and is an IDataSource */
580                         object ctrl = null;
581                         if (Parent != null)
582                                 ctrl = Parent.FindControl (DataSourceID);
583
584                         if (ctrl == null || !(ctrl is IDataSource)) {
585                                 string format;
586
587                                 if (ctrl == null)
588                                         format = "DataSourceID of '{0}' must be the ID of a control of type IDataSource.  A control with ID '{1}' could not be found.";
589                                 else
590                                         format = "DataSourceID of '{0}' must be the ID of a control of type IDataSource.  '{1}' is not an IDataSource.";
591
592                                 throw new HttpException (String.Format (format, ID, DataSourceID));
593                         }
594
595                         boundDataSource = (IDataSource)ctrl;
596                         boundDataSource.GetView (String.Empty).DataSourceViewChanged += new EventHandler(OnDataSourceViewChanged);
597                 }
598 #endif
599         }
600 }