2008-03-13 Marek Habersack <mhabersack@novell.com>
[mono.git] / mcs / class / System.Web / System.Web.UI.WebControls / ListControl.cs
1 //
2 // System.Web.UI.WebControls.ListControl.cs
3 //
4 // Authors:
5 //      Jackson Harper (jackson@ximian.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 using System;
30 using System.Drawing;
31 using System.Web.Util;
32 using System.Collections;
33 using System.Globalization;
34 using System.ComponentModel;
35 using System.Collections.Specialized;
36
37 namespace System.Web.UI.WebControls {
38
39         [DataBindingHandler("System.Web.UI.Design.WebControls.ListControlDataBindingHandler, " + Consts.AssemblySystem_Design)]
40         [DefaultEventAttribute ("SelectedIndexChanged")]
41 #if !NET_2_0
42         [DefaultPropertyAttribute ("DataSource")]
43 #endif
44         [Designer("System.Web.UI.Design.WebControls.ListControlDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
45         [ParseChildrenAttribute (true, "Items")]
46 #if NET_2_0
47         [ControlValueProperty ("SelectedValue", null)]
48 #endif  
49         public abstract class ListControl :
50 #if NET_2_0
51         DataBoundControl, IEditableTextControl, ITextControl
52 #else           
53         WebControl
54 #endif
55         {
56
57                 private static readonly object SelectedIndexChangedEvent = new object ();
58 #if NET_2_0
59                 private static readonly object TextChangedEvent = new object ();
60 #endif
61
62                 private ListItemCollection items;
63 #if NET_2_0
64                 int _selectedIndex = -2;
65                 string _selectedValue;
66 #else           
67                 int saved_selected_index = -2;
68                 string saved_selected_value;
69 #endif
70
71                 public ListControl () : base (HtmlTextWriterTag.Select)
72                 {
73                 }
74
75 #if NET_2_0
76                 [DefaultValue (false)]
77                 [Themeable (false)]
78                 [WebSysDescription ("")]
79                 [WebCategory ("Behavior")]
80                 public virtual bool AppendDataBoundItems
81                 {
82                         get {
83                                 return ViewState.GetBool ("AppendDataBoundItems", false);
84                         }
85                         set {
86                                 ViewState ["AppendDataBoundItems"] = value;
87                                 if (Initialized)
88                                         RequiresDataBinding = true;
89                         }
90                 }
91 #endif          
92                 
93 #if NET_2_0
94                 [Themeable (false)]
95 #endif
96                 [DefaultValue(false)]
97                 [WebSysDescription ("")]
98                 [WebCategory ("Behavior")]
99                 public virtual bool AutoPostBack {
100                         get { return ViewState.GetBool ("AutoPostBack", false); }
101                         set { ViewState ["AutoPostBack"] = value; }
102                 }
103
104 #if ONLY_1_1
105                 [DefaultValue("")]
106                 [WebSysDescription ("")]
107                 [WebCategory ("Data")]
108                 public virtual string DataMember {
109                         get { return ViewState.GetString ("DataMember", String.Empty); }
110                         set { ViewState ["DataMember"] = value; }
111                 }
112
113                 private object data_source;
114
115                 [Bindable(true)]
116                 [DefaultValue(null)]
117                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
118                 [WebSysDescription ("")]
119                 [WebCategory ("Data")]
120                 public virtual object DataSource {
121                         get { return data_source; }
122                         set { 
123                                 if(value == null || value is IListSource || value is IEnumerable) { 
124                                         data_source = value;
125                                         return;
126                                 }
127                                 throw new ArgumentException("Invalid DataSource Type");
128                         }
129                 }
130 #endif          
131
132 #if NET_2_0
133                 [Themeable (false)]
134 #endif          
135                 [DefaultValue("")]
136                 [WebSysDescription ("")]
137                 [WebCategory ("Data")]
138                 public virtual string DataTextField {
139                         get { return ViewState.GetString ("DataTextField", String.Empty); }
140                         set { 
141                                 ViewState ["DataTextField"] = value;
142 #if NET_2_0
143                                 if (Initialized)
144                                         RequiresDataBinding = true;
145 #endif
146                         }
147                 }
148
149 #if NET_2_0
150                 [Themeable (false)]
151 #endif          
152                 [DefaultValue("")]
153                 [WebSysDescription ("")]
154                 [WebCategory ("Data")]
155                 public virtual string DataTextFormatString {
156                         get { return ViewState.GetString ("DataTextFormatString", String.Empty); }
157                         set { 
158                                 ViewState ["DataTextFormatString"] = value;
159 #if NET_2_0
160                                 if (Initialized)
161                                         RequiresDataBinding = true;
162 #endif
163                         }
164                 }
165
166 #if NET_2_0
167                 [Themeable (false)]
168 #endif          
169                 [DefaultValue("")]
170                 [WebSysDescription ("")]
171                 [WebCategory ("Data")]
172                 public virtual string DataValueField {
173                         get { return ViewState.GetString ("DataValueField", String.Empty); }
174                         set { 
175                                 ViewState ["DataValueField"] = value;
176 #if NET_2_0
177                                 if (Initialized)
178                                         RequiresDataBinding = true;
179 #endif
180                         }
181                 }
182
183 #if NET_2_0
184                 [Editor ("System.Web.UI.Design.WebControls.ListItemsCollectionEditor," + Consts.AssemblySystem_Design, "System.Drawing.Design.UITypeEditor, " + Consts.AssemblySystem_Drawing)]
185 #endif          
186                 [DefaultValue(null)]
187                 [MergableProperty(false)]
188                 [PersistenceMode(PersistenceMode.InnerDefaultProperty)]
189                 [WebSysDescription ("")]
190                 [WebCategory ("Misc")]
191                 public virtual ListItemCollection Items {
192                         get {
193                                 if (items == null) {
194                                         items = new ListItemCollection ();
195                                         if (IsTrackingViewState)
196                                                 ((IStateManager) items).TrackViewState ();
197                                 }
198                                 return items;
199                         }
200                 }
201
202                 // I can't find this info stored in the viewstate anywhere
203                 // so it must be calculated on the fly.
204                 [Bindable(true)]
205                 [Browsable(false)]
206                 [DefaultValue(0)]
207                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
208 #if NET_2_0
209                 [Themeable (false)]
210 #endif          
211                 [WebSysDescription ("")]
212                 [WebCategory ("Misc")]
213                 public virtual int SelectedIndex {
214                         get {
215                                 if (items == null)
216                                         return -1;
217                                 for (int i = 0; i < items.Count; i++) {
218                                         if (items [i].Selected)
219                                                 return i;
220                                 }
221                                 return -1;
222                         }
223                         set {
224 #if NET_2_0
225                                 _selectedIndex = value;
226
227                                 if (value < -1)
228                                         throw new ArgumentOutOfRangeException ("value");
229
230                                 if (value >= Items.Count) 
231                                         return;
232
233                                 ClearSelection ();
234                                 if (value == -1)
235                                         return;
236
237                                 items [value].Selected = true;
238
239                                 /* you'd think this would be called, but noooo */
240                                 //OnSelectedIndexChanged (EventArgs.Empty);
241 #else
242                                 if (items == null || items.Count == 0) {
243                                         // This will happen when assigning this property
244                                         // before DataBind () is called on the control.
245                                         saved_selected_index = value;
246                                         return;
247                                 }
248
249                                 if (value < -1 || value >= Items.Count)
250                                         throw new ArgumentOutOfRangeException ("value");
251
252                                 ClearSelection ();
253                                 if (value == -1)
254                                         return;
255
256                                 items [value].Selected = true;
257
258                                 /* you'd think this would be called, but noooo */
259                                 //OnSelectedIndexChanged (EventArgs.Empty);
260 #endif
261                         }
262                 }
263
264                 [Browsable(false)]
265                 [DefaultValue(null)]
266                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
267                 [WebSysDescription ("")]
268                 [WebCategory ("Misc")]
269                 public virtual ListItem SelectedItem {
270                         get {
271                                 int si = SelectedIndex;
272                                 if (si == -1)
273                                         return null;
274                                 return Items [si];
275                         }
276                 }
277
278 #if NET_2_0
279                 [Bindable(true, BindingDirection.TwoWay)]
280                 [Themeable (false)]
281 #else           
282                 [Bindable(true)]
283 #endif          
284                 [Browsable(false)]
285                 [DefaultValue("")]
286                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
287                 [WebSysDescription ("")]
288                 [WebCategory ("Misc")]
289                 public virtual string SelectedValue {
290                         get {
291                                 int si = SelectedIndex;
292                                 if (si == -1)
293                                         return String.Empty;
294                                 return Items [si].Value;
295                         }
296                         set {
297 #if NET_2_0
298                                 _selectedValue = value;
299                                 SetSelectedValue (value);
300 #else
301                                 ClearSelection ();
302                                 if (items == null || items.Count == 0) {
303                                         // This will happen when assigning this property
304                                         // before DataBind () is called on the control.
305                                         saved_selected_value = value;
306                                         return;
307                                 }
308
309                                 int count = Items.Count;
310                                 ListItemCollection coll = Items;
311                                 bool thr = true;
312                                 for (int i = 0; i < count; i++) {
313                                         if (coll [i].Value == value) {
314                                                 coll [i].Selected = true;
315                                                 thr = false;
316                                         }
317                                 }
318
319                                 if (thr) {
320                                         string msg = String.Format ("Argument value is out of range: {0}", value);
321                                         throw new ArgumentOutOfRangeException (msg);
322                                 }
323 #endif
324                         }
325                 }
326
327 #if NET_2_0
328                 bool SetSelectedValue (string value)
329                 {
330                         if (items != null && items.Count > 0) {
331                                 int count = items.Count;
332                                 ListItemCollection coll = Items;
333                                 for (int i = 0; i < count; i++) {
334                                         if (coll [i].Value == value) {
335                                                 ClearSelection ();
336                                                 coll [i].Selected = true;
337                                                 return true;
338                                         }
339                                 }
340                         }
341                         return false;
342                 }
343
344                 [Themeable (false)]
345                 [DefaultValue ("")]
346                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
347                 [Browsable (false)]
348                 [WebSysDescription ("")]
349                 [WebCategoryAttribute ("Behavior")]
350                 public virtual string Text {
351                         get {
352                                 return SelectedValue;
353                         }
354                         set {
355                                 SelectedValue = value;
356                                 /* you'd think this would be called, but noooo */
357                                 //OnTextChanged (EventArgs.Empty);
358                         }
359                 }
360
361 #if HAVE_CONTROL_ADAPTERS
362                 protected virtual new
363 #else           
364                 protected override
365 #endif
366                 HtmlTextWriterTag TagKey
367                 {
368                         get {
369                                 return HtmlTextWriterTag.Select;
370                         }
371                 }
372
373                 protected override void AddAttributesToRender (HtmlTextWriter w)
374                 {
375                         base.AddAttributesToRender (w);
376                 }
377                 
378 #endif          
379
380                 public virtual void ClearSelection ()
381                 {
382                         if (items == null)
383                                 return;
384
385                         int count = Items.Count;
386                         for (int i = 0; i<count; i++)
387                                 items [i].Selected = false;
388                 }
389
390                 protected override void OnDataBinding (EventArgs e)
391                 {
392                         base.OnDataBinding (e);
393
394 #if !NET_2_0
395                         IEnumerable list = DataSourceResolver.ResolveDataSource (DataSource, DataMember);
396                         PerformDataBinding (list);
397 #else
398                         IEnumerable list = GetData ().ExecuteSelect (DataSourceSelectArguments.Empty);
399                         InternalPerformDataBinding (list);
400 #endif
401
402                 }
403
404 #if NET_2_0
405                 protected internal
406 #else           
407                 protected
408 #endif          
409                 override void OnPreRender (EventArgs e)
410                 {
411                         base.OnPreRender (e);
412 #if NET_2_0
413                         if (Page != null && Enabled)
414                                 Page.RegisterEnabledControl (this);
415 #endif
416                 }
417
418 #if NET_2_0
419                 protected virtual void OnTextChanged (EventArgs e)
420                 {
421                         EventHandler handler = (EventHandler) Events [TextChangedEvent];
422                         if (handler != null)
423                                 handler (this, e);
424                 }
425 #endif          
426
427 #if NET_2_0
428                 protected internal override
429 #endif
430                 void PerformDataBinding (IEnumerable dataSource)
431                 {
432                         if (dataSource == null)
433 #if NET_2_0
434                                 goto setselected;
435 #else
436                                 return;
437 #endif
438 #if NET_2_0
439                         if (!AppendDataBoundItems)
440 #endif
441                                 Items.Clear ();
442
443                         string format = DataTextFormatString;
444                         if (format == "")
445                                 format = null;
446
447                         string text_field = DataTextField;
448                         string value_field = DataValueField;
449                         ListItemCollection coll = Items;
450                         foreach (object container in dataSource) {
451                                 string text;
452                                 string val;
453
454                                 text = val = null;
455                                 if (text_field != "") {
456                                         text = DataBinder.GetPropertyValue (container, text_field, format);
457                                 }
458
459                                 if (value_field != "") {
460                                         val = DataBinder.GetPropertyValue (container, value_field).ToString ();
461                                 }
462                                 else if (text_field == "") {
463                                         text = val = container.ToString ();
464                                         if (format != null)
465                                                 text = String.Format (format, container);
466                                 }
467                                 else if (text != null) {
468                                         val = text;
469                                 }
470
471                                 if (text == null)
472                                         text = val;
473
474                                 coll.Add (new ListItem (text, val));
475                         }
476
477 #if NET_2_0
478                 setselected:
479                         if (!String.IsNullOrEmpty (_selectedValue)) {
480                                 if (!SetSelectedValue (_selectedValue))
481                                         throw new ArgumentOutOfRangeException ("value", String.Format ("'{0}' has a SelectedValue which is invalid because it does not exist in the list of items.", ID));
482                                 if (_selectedIndex >= 0 && _selectedIndex != SelectedIndex)
483                                         throw new ArgumentException ("SelectedIndex and SelectedValue are mutually exclusive.");
484                         }
485                         else if (_selectedIndex >= 0) {
486                                 SelectedIndex = _selectedIndex;
487                         }
488 #else
489                         if (saved_selected_value != null) {
490                                 SelectedValue = saved_selected_value;
491                                 if (saved_selected_index != -2 && saved_selected_index != SelectedIndex)
492                                         throw new ArgumentException ("SelectedIndex and SelectedValue are mutually exclusive.");
493                         }
494                         else if (saved_selected_index != -2) {
495                                 SelectedIndex = saved_selected_index;
496                                 // No need to check saved_selected_value here, as it's done before.
497                         }
498 #endif
499                 }
500
501 #if NET_2_0
502                 [MonoTODO ("why override?")]
503                 protected override void PerformSelect ()
504                 {
505                         OnDataBinding (EventArgs.Empty);
506                         RequiresDataBinding = false;
507                         MarkAsDataBound ();
508                         OnDataBound (EventArgs.Empty);
509                 }
510
511                 protected internal override void RenderContents (HtmlTextWriter writer)
512                 {
513                         bool selected = false;
514                         bool havePage = Page != null;
515                         for (int i = 0; i < Items.Count; i++) {
516                                 ListItem item = Items [i];
517                                 if (havePage)
518                                         Page.ClientScript.RegisterForEventValidation (this.UniqueID, item.Value.ToString ());
519                                 writer.WriteBeginTag ("option");
520                                 if (item.Selected) {
521                                         if (selected)
522                                                 VerifyMultiSelect ();
523                                         writer.WriteAttribute ("selected", "selected", false);
524                                         selected = true;
525                                 }
526                                 writer.WriteAttribute ("value", item.Value, true);
527
528                                 if (item.HasAttributes)
529                                         item.Attributes.Render (writer);
530
531                                 writer.Write (">");
532                                 string encoded = HttpUtility.HtmlEncode (item.Text);
533                                 writer.Write (encoded);
534                                 writer.WriteEndTag ("option");
535                                 writer.WriteLine ();
536                         }
537                 }
538
539 #endif          
540
541                 internal ArrayList GetSelectedIndicesInternal ()
542                 {
543                         ArrayList selected = null;
544                         int count;
545                         
546                         if (items != null && (count = items.Count) > 0) {
547                                 selected = new ArrayList ();
548                                 for (int i = 0; i < count; i++) {
549                                         if (items [i].Selected)
550                                                 selected.Add (i);
551                                 }
552                         }
553                         return selected;
554                 }
555
556                 protected override object SaveViewState ()
557                 {
558                         object baseState = null;
559                         object itemsState = null;
560
561                         baseState = base.SaveViewState ();
562
563                         IStateManager manager = items as IStateManager;
564                         if (manager != null)
565                                 itemsState = manager.SaveViewState ();
566
567                         if (baseState == null && itemsState == null)
568                                 return null;
569
570                         return new Pair (baseState, itemsState);
571                 }
572
573                 protected override void LoadViewState (object savedState)
574                 {
575                         object baseState = null;
576                         object itemsState = null;
577
578                         Pair pair = savedState as Pair;
579                         if (pair != null) {
580                                 baseState = pair.First;
581                                 itemsState = pair.Second;
582                         }
583
584                         base.LoadViewState (baseState);
585
586                         if (itemsState != null) {
587                                 IStateManager manager = Items as IStateManager;
588                                 manager.LoadViewState (itemsState);
589                         }
590                 }
591
592 #if NET_2_0
593                 [MonoTODO ("Not implemented")]
594                 protected void SetPostDataSelection (int selectedIndex)
595                 {
596                         throw new NotImplementedException ();
597                 }
598 #endif          
599
600                 protected override void TrackViewState ()
601                 {
602                         base.TrackViewState ();
603                         IStateManager manager = items as IStateManager;
604                         if (manager != null)
605                                 manager.TrackViewState ();
606                 }
607
608                 protected virtual void OnSelectedIndexChanged (EventArgs e)
609                 {
610                         EventHandler handler = (EventHandler) Events [SelectedIndexChangedEvent];
611                         if (handler != null)
612                                 handler (this, e);
613                 }
614
615 #if NET_2_0             
616                 protected internal virtual void VerifyMultiSelect ()
617                 {
618             throw new HttpException("Multi select is not supported");
619                 }
620 #endif          
621
622                 [WebSysDescription ("")]
623                 [WebCategory ("Action")]
624                 public event EventHandler SelectedIndexChanged {
625                         add { Events.AddHandler (SelectedIndexChangedEvent, value); }
626                         remove { Events.RemoveHandler (SelectedIndexChangedEvent, value); }
627                 }
628
629 #if NET_2_0
630                 /* sealed in the docs */
631                 public event EventHandler TextChanged {
632                         add {
633                                 Events.AddHandler (TextChangedEvent, value);
634                         }
635                         remove {
636                                 Events.RemoveHandler (TextChangedEvent, value);
637                         }
638                 }
639                 
640                 
641                 [Themeable (false)]
642                 [DefaultValue (false)]
643                 [WebSysDescription ("")]
644                 [WebCategory ("Behavior")]
645                 public virtual bool CausesValidation {
646                         get {
647                                 return ViewState.GetBool ("CausesValidation", false);
648                         }
649
650                         set {
651                                 ViewState ["CausesValidation"] = value;
652                         }
653                 }
654
655                 [Themeable (false)]
656                 [DefaultValue ("")]
657                 [WebSysDescription ("")]
658                 [WebCategoryAttribute ("Behavior")]
659                 public virtual string ValidationGroup {
660                         get {
661                                 return ViewState.GetString ("ValidationGroup", "");
662                         }
663                         set {
664                                 ViewState ["ValidationGroup"] = value;
665                         }
666                 }
667
668         
669 #endif
670         }
671 }
672
673
674
675
676