2 // System.Web.UI.WebControls.ListControl.cs
5 // Jackson Harper (jackson@ximian.com)
7 // (C) 2005 Novell, Inc (http://www.novell.com)
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:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
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.
31 using System.Web.Util;
32 using System.Collections;
33 using System.Globalization;
34 using System.ComponentModel;
35 using System.Collections.Specialized;
37 namespace System.Web.UI.WebControls {
39 [DataBindingHandler("System.Web.UI.Design.WebControls.ListControlDataBindingHandler, " + Consts.AssemblySystem_Design)]
40 [DefaultEventAttribute ("SelectedIndexChanged")]
42 [DefaultPropertyAttribute ("DataSource")]
44 [Designer("System.Web.UI.Design.WebControls.ListControlDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
45 [ParseChildrenAttribute (true, "Items")]
47 [ControlValueProperty ("SelectedValue", null)]
49 public abstract class ListControl :
51 DataBoundControl, IEditableTextControl, ITextControl
57 private static readonly object SelectedIndexChangedEvent = new object ();
59 private static readonly object TextChangedEvent = new object ();
62 private ListItemCollection items;
64 int _selectedIndex = -2;
65 string _selectedValue;
67 int saved_selected_index = -2;
68 string saved_selected_value;
71 public ListControl () : base (HtmlTextWriterTag.Select)
76 [DefaultValue (false)]
78 [WebSysDescription ("")]
79 [WebCategory ("Behavior")]
80 public virtual bool AppendDataBoundItems
83 return ViewState.GetBool ("AppendDataBoundItems", false);
86 ViewState ["AppendDataBoundItems"] = value;
95 [WebSysDescription ("")]
96 [WebCategory ("Behavior")]
97 public virtual bool AutoPostBack {
98 get { return ViewState.GetBool ("AutoPostBack", false); }
99 set { ViewState ["AutoPostBack"] = value; }
104 [WebSysDescription ("")]
105 [WebCategory ("Data")]
106 public virtual string DataMember {
107 get { return ViewState.GetString ("DataMember", String.Empty); }
108 set { ViewState ["DataMember"] = value; }
111 private object data_source;
115 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
116 [WebSysDescription ("")]
117 [WebCategory ("Data")]
118 public virtual object DataSource {
119 get { return data_source; }
121 if(value == null || value is IListSource || value is IEnumerable) {
125 throw new ArgumentException("Invalid DataSource Type");
134 [WebSysDescription ("")]
135 [WebCategory ("Data")]
136 public virtual string DataTextField {
137 get { return ViewState.GetString ("DataTextField", String.Empty); }
138 set { ViewState ["DataTextField"] = value; }
145 [WebSysDescription ("")]
146 [WebCategory ("Data")]
147 public virtual string DataTextFormatString {
148 get { return ViewState.GetString ("DataTextFormatString", String.Empty); }
149 set { ViewState ["DataTextFormatString"] = value; }
156 [WebSysDescription ("")]
157 [WebCategory ("Data")]
158 public virtual string DataValueField {
159 get { return ViewState.GetString ("DataValueField", String.Empty); }
160 set { ViewState ["DataValueField"] = value; }
164 [Editor ("System.Web.UI.Design.WebControls.ListItemsCollectionEditor," + Consts.AssemblySystem_Design, "System.Drawing.Design.UITypeEditor, " + Consts.AssemblySystem_Drawing)]
167 [MergableProperty(false)]
168 [PersistenceMode(PersistenceMode.InnerDefaultProperty)]
169 [WebSysDescription ("")]
170 [WebCategory ("Misc")]
171 public virtual ListItemCollection Items {
174 items = new ListItemCollection ();
179 // I can't find this info stored in the viewstate anywhere
180 // so it must be calculated on the fly.
184 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
188 [WebSysDescription ("")]
189 [WebCategory ("Misc")]
190 public virtual int SelectedIndex {
194 for (int i = 0; i < items.Count; i++) {
195 if (items [i].Selected)
202 _selectedIndex = value;
205 throw new ArgumentOutOfRangeException ("value");
207 if (value >= Items.Count)
214 items [value].Selected = true;
216 /* you'd think this would be called, but noooo */
217 //OnSelectedIndexChanged (EventArgs.Empty);
219 if (items == null || items.Count == 0) {
220 // This will happen when assigning this property
221 // before DataBind () is called on the control.
222 saved_selected_index = value;
226 if (value < -1 || value >= Items.Count)
227 throw new ArgumentOutOfRangeException ("value");
233 items [value].Selected = true;
235 /* you'd think this would be called, but noooo */
236 //OnSelectedIndexChanged (EventArgs.Empty);
243 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
244 [WebSysDescription ("")]
245 [WebCategory ("Misc")]
246 public virtual ListItem SelectedItem {
248 int si = SelectedIndex;
256 [Bindable(true, BindingDirection.TwoWay)]
263 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
264 [WebSysDescription ("")]
265 [WebCategory ("Misc")]
266 public virtual string SelectedValue {
268 int si = SelectedIndex;
271 return Items [si].Value;
275 _selectedValue = value;
276 SetSelectedValue (value);
279 if (items == null || items.Count == 0) {
280 // This will happen when assigning this property
281 // before DataBind () is called on the control.
282 saved_selected_value = value;
286 int count = Items.Count;
287 ListItemCollection coll = Items;
289 for (int i = 0; i < count; i++) {
290 if (coll [i].Value == value) {
291 coll [i].Selected = true;
297 string msg = String.Format ("Argument value is out of range: {0}", value);
298 throw new ArgumentOutOfRangeException (msg);
305 bool SetSelectedValue (string value)
307 if (items != null && items.Count > 0) {
308 int count = items.Count;
309 ListItemCollection coll = Items;
310 for (int i = 0; i < count; i++) {
311 if (coll [i].Value == value) {
313 coll [i].Selected = true;
323 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
325 [WebSysDescription ("")]
326 [WebCategoryAttribute ("Behavior")]
327 public virtual string Text {
329 return SelectedValue;
332 SelectedValue = value;
333 /* you'd think this would be called, but noooo */
334 //OnTextChanged (EventArgs.Empty);
338 #if HAVE_CONTROL_ADAPTERS
339 protected virtual new
343 HtmlTextWriterTag TagKey
346 return HtmlTextWriterTag.Select;
350 protected override void AddAttributesToRender (HtmlTextWriter w)
352 base.AddAttributesToRender (w);
357 public virtual void ClearSelection ()
362 int count = Items.Count;
363 for (int i = 0; i<count; i++)
364 items [i].Selected = false;
367 protected override void OnDataBinding (EventArgs e)
369 base.OnDataBinding (e);
372 IEnumerable list = DataSourceResolver.ResolveDataSource (DataSource, DataMember);
373 PerformDataBinding (list);
382 override void OnPreRender (EventArgs e)
384 base.OnPreRender (e);
386 if (Page != null && Enabled)
387 Page.RegisterEnabledControl (this);
392 protected virtual void OnTextChanged (EventArgs e)
394 EventHandler handler = (EventHandler) Events [TextChangedEvent];
401 protected internal override
403 void PerformDataBinding (IEnumerable dataSource)
405 if (dataSource == null)
408 if (!AppendDataBoundItems)
412 string format = DataTextFormatString;
416 string text_field = DataTextField;
417 string value_field = DataValueField;
418 ListItemCollection coll = Items;
419 foreach (object container in dataSource) {
424 if (text_field != "") {
425 text = DataBinder.GetPropertyValue (container, text_field, format);
428 if (value_field != "") {
429 val = DataBinder.GetPropertyValue (container, value_field).ToString ();
431 else if (text_field == "") {
432 text = val = container.ToString ();
434 text = String.Format (format, container);
436 else if (text != null) {
443 coll.Add (new ListItem (text, val));
447 if (!String.IsNullOrEmpty (_selectedValue)) {
448 if (!SetSelectedValue (_selectedValue))
449 throw new ArgumentOutOfRangeException ("value", String.Format ("'{0}' has a SelectedValue which is invalid because it does not exist in the list of items.", ID));
450 if (_selectedIndex >= 0 && _selectedIndex != SelectedIndex)
451 throw new ArgumentException ("SelectedIndex and SelectedValue are mutually exclusive.");
453 else if (_selectedIndex >= 0) {
454 SelectedIndex = _selectedIndex;
457 if (saved_selected_value != null) {
458 SelectedValue = saved_selected_value;
459 if (saved_selected_index != -2 && saved_selected_index != SelectedIndex)
460 throw new ArgumentException ("SelectedIndex and SelectedValue are mutually exclusive.");
462 else if (saved_selected_index != -2) {
463 SelectedIndex = saved_selected_index;
464 // No need to check saved_selected_value here, as it's done before.
470 [MonoTODO ("why override?")]
471 protected override void PerformSelect ()
473 base.PerformSelect ();
476 protected internal override void RenderContents (HtmlTextWriter writer)
478 bool selected = false;
479 bool havePage = Page != null;
480 for (int i = 0; i < Items.Count; i++) {
481 ListItem item = Items [i];
483 Page.ClientScript.RegisterForEventValidation (this.UniqueID, item.Value.ToString ());
484 writer.WriteBeginTag ("option");
487 VerifyMultiSelect ();
488 writer.WriteAttribute ("selected", "selected", false);
491 writer.WriteAttribute ("value", item.Value, true);
493 if (item.HasAttributes)
494 item.Attributes.Render (writer);
497 string encoded = HttpUtility.HtmlEncode (item.Text);
498 writer.Write (encoded);
499 writer.WriteEndTag ("option");
506 internal ArrayList GetSelectedIndicesInternal ()
508 ArrayList selected = null;
510 selected = new ArrayList ();
511 int count = Items.Count;
512 for (int i = 0; i < count; i++) {
513 if (items [i].Selected)
520 protected override object SaveViewState ()
523 object second = null;
525 first = base.SaveViewState ();
527 IStateManager manager = items as IStateManager;
529 second = manager.SaveViewState ();
531 ArrayList selected = GetSelectedIndicesInternal ();
533 if (first == null && second == null && selected == null)
536 return new Triplet (first, second, selected);
539 protected override void LoadViewState (object savedState)
542 object second = null;
543 ArrayList indices = null;
545 Triplet triplet = savedState as Triplet;
546 if (triplet != null) {
547 first = triplet.First;
548 second = triplet.Second;
549 indices = triplet.Third as ArrayList;
552 base.LoadViewState (first);
554 if (second != null) {
555 IStateManager manager = Items as IStateManager;
556 manager.LoadViewState (second);
559 if (indices != null) {
560 foreach (int index in indices)
561 Items [index].Selected = true;
566 [MonoTODO ("Not implemented")]
567 protected void SetPostDataSelection (int selectedIndex)
569 throw new NotImplementedException ();
573 protected override void TrackViewState ()
575 base.TrackViewState ();
576 IStateManager manager = items as IStateManager;
578 manager.TrackViewState ();
581 protected virtual void OnSelectedIndexChanged (EventArgs e)
583 EventHandler handler = (EventHandler) Events [SelectedIndexChangedEvent];
589 protected internal virtual void VerifyMultiSelect ()
591 throw new HttpException("Multi select is not supported");
595 [WebSysDescription ("")]
596 [WebCategory ("Action")]
597 public event EventHandler SelectedIndexChanged {
598 add { Events.AddHandler (SelectedIndexChangedEvent, value); }
599 remove { Events.RemoveHandler (SelectedIndexChangedEvent, value); }
603 /* sealed in the docs */
604 public event EventHandler TextChanged {
606 Events.AddHandler (TextChangedEvent, value);
609 Events.RemoveHandler (TextChangedEvent, value);
615 [DefaultValue (false)]
616 [WebSysDescription ("")]
617 [WebCategory ("Behavior")]
618 public virtual bool CausesValidation {
620 return ViewState.GetBool ("CausesValidation", false);
624 ViewState ["CausesValidation"] = value;
630 [WebSysDescription ("")]
631 [WebCategoryAttribute ("Behavior")]
632 public virtual string ValidationGroup {
634 return ViewState.GetString ("ValidationGroup", "");
637 ViewState ["ValidationGroup"] = value;