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