Merge pull request #4453 from lambdageek/bug-49721
[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         [Designer("System.Web.UI.Design.WebControls.ListControlDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
42         [ParseChildrenAttribute (true, "Items")]
43         [ControlValueProperty ("SelectedValue", null)]
44         public abstract class ListControl : DataBoundControl, IEditableTextControl, ITextControl
45         {
46
47                 static readonly object SelectedIndexChangedEvent = new object ();
48                 static readonly object TextChangedEvent = new object ();
49
50                 ListItemCollection items;
51                 int _selectedIndex = -2;
52                 string _selectedValue;
53
54                 public ListControl () : base (HtmlTextWriterTag.Select)
55                 {
56                 }
57
58                 [DefaultValue (false)]
59                 [Themeable (false)]
60                 [WebSysDescription ("")]
61                 [WebCategory ("Behavior")]
62                 public virtual bool AppendDataBoundItems
63                 {
64                         get {
65                                 return ViewState.GetBool ("AppendDataBoundItems", false);
66                         }
67                         set {
68                                 ViewState ["AppendDataBoundItems"] = value;
69                                 if (Initialized)
70                                         RequiresDataBinding = true;
71                         }
72                 }
73                 
74                 [Themeable (false)]
75                 [DefaultValue(false)]
76                 [WebSysDescription ("")]
77                 [WebCategory ("Behavior")]
78                 public virtual bool AutoPostBack {
79                         get { return ViewState.GetBool ("AutoPostBack", false); }
80                         set { ViewState ["AutoPostBack"] = value; }
81                 }
82
83                 [Themeable (false)]
84                 [DefaultValue("")]
85                 [WebSysDescription ("")]
86                 [WebCategory ("Data")]
87                 public virtual string DataTextField {
88                         get { return ViewState.GetString ("DataTextField", String.Empty); }
89                         set { 
90                                 ViewState ["DataTextField"] = value;
91                                 if (Initialized)
92                                         RequiresDataBinding = true;
93                         }
94                 }
95
96                 [Themeable (false)]
97                 [DefaultValue("")]
98                 [WebSysDescription ("")]
99                 [WebCategory ("Data")]
100                 public virtual string DataTextFormatString {
101                         get { return ViewState.GetString ("DataTextFormatString", String.Empty); }
102                         set { 
103                                 ViewState ["DataTextFormatString"] = value;
104                                 if (Initialized)
105                                         RequiresDataBinding = true;
106                         }
107                 }
108
109                 [Themeable (false)]
110                 [DefaultValue("")]
111                 [WebSysDescription ("")]
112                 [WebCategory ("Data")]
113                 public virtual string DataValueField {
114                         get { return ViewState.GetString ("DataValueField", String.Empty); }
115                         set { 
116                                 ViewState ["DataValueField"] = value;
117                                 if (Initialized)
118                                         RequiresDataBinding = true;
119                         }
120                 }
121
122                 [Editor ("System.Web.UI.Design.WebControls.ListItemsCollectionEditor," + Consts.AssemblySystem_Design, "System.Drawing.Design.UITypeEditor, " + Consts.AssemblySystem_Drawing)]
123                 [DefaultValue(null)]
124                 [MergableProperty(false)]
125                 [PersistenceMode(PersistenceMode.InnerDefaultProperty)]
126                 [WebSysDescription ("")]
127                 [WebCategory ("Misc")]
128                 public virtual ListItemCollection Items {
129                         get {
130                                 if (items == null) {
131                                         items = new ListItemCollection ();
132                                         if (IsTrackingViewState)
133                                                 ((IStateManager) items).TrackViewState ();
134                                 }
135                                 return items;
136                         }
137                 }
138
139                 // I can't find this info stored in the viewstate anywhere
140                 // so it must be calculated on the fly.
141                 [Bindable(true)]
142                 [Browsable(false)]
143                 [DefaultValue(0)]
144                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
145                 [Themeable (false)]
146                 [WebSysDescription ("")]
147                 [WebCategory ("Misc")]
148                 public virtual int SelectedIndex {
149                         get {
150                                 if (items == null)
151                                         return -1;
152                                 for (int i = 0; i < items.Count; i++) {
153                                         if (items [i].Selected)
154                                                 return i;
155                                 }
156                                 return -1;
157                         }
158                         set {
159                                 _selectedIndex = value;
160
161                                 if (value < -1)
162                                         throw new ArgumentOutOfRangeException ("value");
163
164                                 if (value >= Items.Count) 
165                                         return;
166
167                                 ClearSelection ();
168                                 if (value == -1)
169                                         return;
170
171                                 items [value].Selected = true;
172
173                                 /* you'd think this would be called, but noooo */
174                                 //OnSelectedIndexChanged (EventArgs.Empty);
175                         }
176                 }
177
178                 [Browsable(false)]
179                 [DefaultValue(null)]
180                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
181                 [WebSysDescription ("")]
182                 [WebCategory ("Misc")]
183                 public virtual ListItem SelectedItem {
184                         get {
185                                 int si = SelectedIndex;
186                                 if (si == -1)
187                                         return null;
188                                 return Items [si];
189                         }
190                 }
191
192                 [Bindable(true, BindingDirection.TwoWay)]
193                 [Themeable (false)]
194                 [Browsable(false)]
195                 [DefaultValue("")]
196                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
197                 [WebSysDescription ("")]
198                 [WebCategory ("Misc")]
199                 public virtual string SelectedValue {
200                         get {
201                                 int si = SelectedIndex;
202                                 if (si == -1)
203                                         return String.Empty;
204                                 return Items [si].Value;
205                         }
206                         set {
207                                 _selectedValue = value;
208                                 SetSelectedValue (value);
209                         }
210                 }
211
212                 bool SetSelectedValue (string value)
213                 {
214                         if (items != null && items.Count > 0) {
215                                 int count = items.Count;
216                                 ListItemCollection coll = Items;
217                                 for (int i = 0; i < count; i++) {
218                                         if (coll [i].Value == value) {
219                                                 ClearSelection ();
220                                                 coll [i].Selected = true;
221                                                 return true;
222                                         }
223                                 }
224                         }
225                         return false;
226                 }
227
228                 [Themeable (false)]
229                 [DefaultValue ("")]
230                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
231                 [Browsable (false)]
232                 [WebSysDescription ("")]
233                 [WebCategoryAttribute ("Behavior")]
234                 public virtual string Text {
235                         get {
236                                 return SelectedValue;
237                         }
238                         set {
239                                 SelectedValue = value;
240                                 /* you'd think this would be called, but noooo */
241                                 //OnTextChanged (EventArgs.Empty);
242                         }
243                 }
244
245 #if HAVE_CONTROL_ADAPTERS
246                 protected virtual new
247 #else           
248                 protected override
249 #endif
250                 HtmlTextWriterTag TagKey
251                 {
252                         get {
253                                 return HtmlTextWriterTag.Select;
254                         }
255                 }
256
257                 protected override void AddAttributesToRender (HtmlTextWriter writer)
258                 {
259                         base.AddAttributesToRender (writer);
260                 }
261
262                 public virtual void ClearSelection ()
263                 {
264                         if (items == null)
265                                 return;
266
267                         int count = Items.Count;
268                         for (int i = 0; i<count; i++)
269                                 items [i].Selected = false;
270                 }
271
272                 protected override void OnDataBinding (EventArgs e)
273                 {
274                         base.OnDataBinding (e);
275                         IEnumerable list = GetData ().ExecuteSelect (DataSourceSelectArguments.Empty);
276                         InternalPerformDataBinding (list);
277                 }
278
279                 protected internal override void OnPreRender (EventArgs e)
280                 {
281                         base.OnPreRender (e);
282                         Page page = Page;
283                         if (page != null && IsEnabled)
284                                 page.RegisterEnabledControl (this);
285                 }
286
287                 protected virtual void OnTextChanged (EventArgs e)
288                 {
289                         EventHandler handler = (EventHandler) Events [TextChangedEvent];
290                         if (handler != null)
291                                 handler (this, e);
292                 }
293
294                 protected internal override void PerformDataBinding (IEnumerable dataSource)
295                 {
296                         if (dataSource == null)
297                                 goto setselected;
298                         if (!AppendDataBoundItems)
299                                 Items.Clear ();
300
301                         string format = DataTextFormatString;
302                         if (format.Length == 0)
303                                 format = null;
304
305                         string text_field = DataTextField;
306                         string value_field = DataValueField;
307
308                         if (text_field.Length == 0)
309                                 text_field = null;
310                         if (value_field.Length == 0)
311                                 value_field = null;
312                         
313                         ListItemCollection coll = Items;
314                         foreach (object container in dataSource) {
315                                 string text;
316                                 string val;
317
318                                 text = val = null;
319                                 if (text_field != null)
320                                         text = DataBinder.GetPropertyValue (container, text_field, format);
321                                 
322                                 if (value_field != null)
323                                         val = DataBinder.GetPropertyValue (container, value_field).ToString ();
324                                 else if (text_field == null) {
325                                         text = val = container.ToString ();
326                                         if (format != null)
327                                                 text = String.Format (format, container);
328                                 } else if (text != null)
329                                         val = text;
330
331                                 if (text == null)
332                                         text = val;
333
334                                 coll.Add (new ListItem (text, val));
335                         }
336
337                 setselected:
338                         if (!String.IsNullOrEmpty (_selectedValue)) {
339                                 if (!SetSelectedValue (_selectedValue))
340                                         throw new ArgumentOutOfRangeException ("value", String.Format ("'{0}' has a SelectedValue which is invalid because it does not exist in the list of items.", ID));
341                                 if (_selectedIndex >= 0 && _selectedIndex != SelectedIndex)
342                                         throw new ArgumentException ("SelectedIndex and SelectedValue are mutually exclusive.");
343                         }
344                         else if (_selectedIndex >= 0) {
345                                 SelectedIndex = _selectedIndex;
346                         }
347                 }
348
349                 [MonoTODO ("why override?")]
350                 protected override void PerformSelect ()
351                 {
352                         OnDataBinding (EventArgs.Empty);
353                         RequiresDataBinding = false;
354                         MarkAsDataBound ();
355                         OnDataBound (EventArgs.Empty);
356                 }
357
358                 protected internal override void RenderContents (HtmlTextWriter writer)
359                 {
360                         bool selected = false;
361                         Page page = Page;
362                         for (int i = 0; i < Items.Count; i++) {
363                                 ListItem item = Items [i];
364                                 if (page != null)
365                                         page.ClientScript.RegisterForEventValidation (UniqueID, item.Value);
366                                 writer.WriteBeginTag ("option");
367                                 if (item.Selected) {
368                                         if (selected)
369                                                 VerifyMultiSelect ();
370                                         writer.WriteAttribute ("selected", "selected", false);
371                                         selected = true;
372                                 }
373                                 writer.WriteAttribute ("value", item.Value, true);
374
375                                 if (item.HasAttributes)
376                                         item.Attributes.Render (writer);
377
378                                 writer.Write (">");
379                                 string encoded = HttpUtility.HtmlEncode (item.Text);
380                                 writer.Write (encoded);
381                                 writer.WriteEndTag ("option");
382                                 writer.WriteLine ();
383                         }
384                 }
385
386                 internal ArrayList GetSelectedIndicesInternal ()
387                 {
388                         ArrayList selected = null;
389                         int count;
390                         
391                         if (items != null && (count = items.Count) > 0) {
392                                 selected = new ArrayList ();
393                                 for (int i = 0; i < count; i++) {
394                                         if (items [i].Selected)
395                                                 selected.Add (i);
396                                 }
397                         }
398                         return selected;
399                 }
400
401                 protected override object SaveViewState ()
402                 {
403                         object baseState = null;
404                         object itemsState = null;
405
406                         baseState = base.SaveViewState ();
407
408                         IStateManager manager = items as IStateManager;
409                         if (manager != null)
410                                 itemsState = manager.SaveViewState ();
411
412                         // .NET 2.0+ never returns null. It returns a Triplet with the Third member
413                         // set to an instance of ArrayList. Since we don't have a use (at least atm)
414                         // for this, we will just return a pair with both members null.
415                         return new Pair (baseState, itemsState);
416                 }
417
418                 protected override void LoadViewState (object savedState)
419                 {
420                         object baseState = null;
421                         object itemsState = null;
422
423                         Pair pair = savedState as Pair;
424                         if (pair != null) {
425                                 baseState = pair.First;
426                                 itemsState = pair.Second;
427                         }
428
429                         base.LoadViewState (baseState);
430
431                         if (itemsState != null) {
432                                 IStateManager manager = Items as IStateManager;
433                                 manager.LoadViewState (itemsState);
434                         }
435                 }
436
437                 [MonoTODO ("Not implemented")]
438                 protected void SetPostDataSelection (int selectedIndex)
439                 {
440                         throw new NotImplementedException ();
441                 }
442
443                 protected override void TrackViewState ()
444                 {
445                         base.TrackViewState ();
446                         IStateManager manager = items as IStateManager;
447                         if (manager != null)
448                                 manager.TrackViewState ();
449                 }
450
451                 protected virtual void OnSelectedIndexChanged (EventArgs e)
452                 {
453                         EventHandler handler = (EventHandler) Events [SelectedIndexChangedEvent];
454                         if (handler != null)
455                                 handler (this, e);
456                 }
457
458                 protected internal virtual void VerifyMultiSelect ()
459                 {
460                         if (!MultiSelectOk ())
461                                 throw new HttpException("Multi select is not supported");
462                 }
463
464                 internal virtual bool MultiSelectOk ()
465                 {
466                         return false;
467                 }
468
469                 [WebSysDescription ("")]
470                 [WebCategory ("Action")]
471                 public event EventHandler SelectedIndexChanged {
472                         add { Events.AddHandler (SelectedIndexChangedEvent, value); }
473                         remove { Events.RemoveHandler (SelectedIndexChangedEvent, value); }
474                 }
475
476                 /* sealed in the docs */
477                 public event EventHandler TextChanged {
478                         add {
479                                 Events.AddHandler (TextChangedEvent, value);
480                         }
481                         remove {
482                                 Events.RemoveHandler (TextChangedEvent, value);
483                         }
484                 }
485                 
486                 
487                 [Themeable (false)]
488                 [DefaultValue (false)]
489                 [WebSysDescription ("")]
490                 [WebCategory ("Behavior")]
491                 public virtual bool CausesValidation {
492                         get {
493                                 return ViewState.GetBool ("CausesValidation", false);
494                         }
495
496                         set {
497                                 ViewState ["CausesValidation"] = value;
498                         }
499                 }
500
501                 [Themeable (false)]
502                 [DefaultValue ("")]
503                 [WebSysDescription ("")]
504                 [WebCategoryAttribute ("Behavior")]
505                 public virtual string ValidationGroup {
506                         get {
507                                 return ViewState.GetString ("ValidationGroup", "");
508                         }
509                         set {
510                                 ViewState ["ValidationGroup"] = value;
511                         }
512                 }
513         }
514 }
515
516
517
518
519