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;
88 RequiresDataBinding = true;
97 [WebSysDescription ("")]
98 [WebCategory ("Behavior")]
99 public virtual bool AutoPostBack {
100 get { return ViewState.GetBool ("AutoPostBack", false); }
101 set { ViewState ["AutoPostBack"] = value; }
106 [WebSysDescription ("")]
107 [WebCategory ("Data")]
108 public virtual string DataMember {
109 get { return ViewState.GetString ("DataMember", String.Empty); }
110 set { ViewState ["DataMember"] = value; }
113 private object data_source;
117 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
118 [WebSysDescription ("")]
119 [WebCategory ("Data")]
120 public virtual object DataSource {
121 get { return data_source; }
123 if(value == null || value is IListSource || value is IEnumerable) {
127 throw new ArgumentException("Invalid DataSource Type");
136 [WebSysDescription ("")]
137 [WebCategory ("Data")]
138 public virtual string DataTextField {
139 get { return ViewState.GetString ("DataTextField", String.Empty); }
141 ViewState ["DataTextField"] = value;
144 RequiresDataBinding = true;
153 [WebSysDescription ("")]
154 [WebCategory ("Data")]
155 public virtual string DataTextFormatString {
156 get { return ViewState.GetString ("DataTextFormatString", String.Empty); }
158 ViewState ["DataTextFormatString"] = value;
161 RequiresDataBinding = true;
170 [WebSysDescription ("")]
171 [WebCategory ("Data")]
172 public virtual string DataValueField {
173 get { return ViewState.GetString ("DataValueField", String.Empty); }
175 ViewState ["DataValueField"] = value;
178 RequiresDataBinding = true;
184 [Editor ("System.Web.UI.Design.WebControls.ListItemsCollectionEditor," + Consts.AssemblySystem_Design, "System.Drawing.Design.UITypeEditor, " + Consts.AssemblySystem_Drawing)]
187 [MergableProperty(false)]
188 [PersistenceMode(PersistenceMode.InnerDefaultProperty)]
189 [WebSysDescription ("")]
190 [WebCategory ("Misc")]
191 public virtual ListItemCollection Items {
194 items = new ListItemCollection ();
195 if (IsTrackingViewState)
196 ((IStateManager) items).TrackViewState ();
202 // I can't find this info stored in the viewstate anywhere
203 // so it must be calculated on the fly.
207 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
211 [WebSysDescription ("")]
212 [WebCategory ("Misc")]
213 public virtual int SelectedIndex {
217 for (int i = 0; i < items.Count; i++) {
218 if (items [i].Selected)
225 _selectedIndex = value;
228 throw new ArgumentOutOfRangeException ("value");
230 if (value >= Items.Count)
237 items [value].Selected = true;
239 /* you'd think this would be called, but noooo */
240 //OnSelectedIndexChanged (EventArgs.Empty);
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;
249 if (value < -1 || value >= Items.Count)
250 throw new ArgumentOutOfRangeException ("value");
256 items [value].Selected = true;
258 /* you'd think this would be called, but noooo */
259 //OnSelectedIndexChanged (EventArgs.Empty);
266 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
267 [WebSysDescription ("")]
268 [WebCategory ("Misc")]
269 public virtual ListItem SelectedItem {
271 int si = SelectedIndex;
279 [Bindable(true, BindingDirection.TwoWay)]
286 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
287 [WebSysDescription ("")]
288 [WebCategory ("Misc")]
289 public virtual string SelectedValue {
291 int si = SelectedIndex;
294 return Items [si].Value;
298 _selectedValue = value;
299 SetSelectedValue (value);
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;
309 int count = Items.Count;
310 ListItemCollection coll = Items;
312 for (int i = 0; i < count; i++) {
313 if (coll [i].Value == value) {
314 coll [i].Selected = true;
320 string msg = String.Format ("Argument value is out of range: {0}", value);
321 throw new ArgumentOutOfRangeException (msg);
328 bool SetSelectedValue (string value)
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) {
336 coll [i].Selected = true;
346 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
348 [WebSysDescription ("")]
349 [WebCategoryAttribute ("Behavior")]
350 public virtual string Text {
352 return SelectedValue;
355 SelectedValue = value;
356 /* you'd think this would be called, but noooo */
357 //OnTextChanged (EventArgs.Empty);
361 #if HAVE_CONTROL_ADAPTERS
362 protected virtual new
366 HtmlTextWriterTag TagKey
369 return HtmlTextWriterTag.Select;
373 protected override void AddAttributesToRender (HtmlTextWriter w)
375 base.AddAttributesToRender (w);
380 public virtual void ClearSelection ()
385 int count = Items.Count;
386 for (int i = 0; i<count; i++)
387 items [i].Selected = false;
390 protected override void OnDataBinding (EventArgs e)
392 base.OnDataBinding (e);
395 IEnumerable list = DataSourceResolver.ResolveDataSource (DataSource, DataMember);
396 PerformDataBinding (list);
398 IEnumerable list = GetData ().ExecuteSelect (DataSourceSelectArguments.Empty);
399 InternalPerformDataBinding (list);
409 override void OnPreRender (EventArgs e)
411 base.OnPreRender (e);
413 if (Page != null && Enabled)
414 Page.RegisterEnabledControl (this);
419 protected virtual void OnTextChanged (EventArgs e)
421 EventHandler handler = (EventHandler) Events [TextChangedEvent];
428 protected internal override
430 void PerformDataBinding (IEnumerable dataSource)
432 if (dataSource == null)
439 if (!AppendDataBoundItems)
443 string format = DataTextFormatString;
447 string text_field = DataTextField;
448 string value_field = DataValueField;
449 ListItemCollection coll = Items;
450 foreach (object container in dataSource) {
455 if (text_field != "") {
456 text = DataBinder.GetPropertyValue (container, text_field, format);
459 if (value_field != "") {
460 val = DataBinder.GetPropertyValue (container, value_field).ToString ();
462 else if (text_field == "") {
463 text = val = container.ToString ();
465 text = String.Format (format, container);
467 else if (text != null) {
474 coll.Add (new ListItem (text, val));
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.");
485 else if (_selectedIndex >= 0) {
486 SelectedIndex = _selectedIndex;
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.");
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.
502 [MonoTODO ("why override?")]
503 protected override void PerformSelect ()
505 OnDataBinding (EventArgs.Empty);
506 RequiresDataBinding = false;
508 OnDataBound (EventArgs.Empty);
511 protected internal override void RenderContents (HtmlTextWriter writer)
513 bool selected = false;
514 bool havePage = Page != null;
515 for (int i = 0; i < Items.Count; i++) {
516 ListItem item = Items [i];
518 Page.ClientScript.RegisterForEventValidation (this.UniqueID, item.Value.ToString ());
519 writer.WriteBeginTag ("option");
522 VerifyMultiSelect ();
523 writer.WriteAttribute ("selected", "selected", false);
526 writer.WriteAttribute ("value", item.Value, true);
528 if (item.HasAttributes)
529 item.Attributes.Render (writer);
532 string encoded = HttpUtility.HtmlEncode (item.Text);
533 writer.Write (encoded);
534 writer.WriteEndTag ("option");
541 internal ArrayList GetSelectedIndicesInternal ()
543 ArrayList selected = null;
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)
556 protected override object SaveViewState ()
558 object baseState = null;
559 object itemsState = null;
561 baseState = base.SaveViewState ();
563 IStateManager manager = items as IStateManager;
565 itemsState = manager.SaveViewState ();
567 if (baseState == null && itemsState == null)
570 return new Pair (baseState, itemsState);
573 protected override void LoadViewState (object savedState)
575 object baseState = null;
576 object itemsState = null;
578 Pair pair = savedState as Pair;
580 baseState = pair.First;
581 itemsState = pair.Second;
584 base.LoadViewState (baseState);
586 if (itemsState != null) {
587 IStateManager manager = Items as IStateManager;
588 manager.LoadViewState (itemsState);
593 [MonoTODO ("Not implemented")]
594 protected void SetPostDataSelection (int selectedIndex)
596 throw new NotImplementedException ();
600 protected override void TrackViewState ()
602 base.TrackViewState ();
603 IStateManager manager = items as IStateManager;
605 manager.TrackViewState ();
608 protected virtual void OnSelectedIndexChanged (EventArgs e)
610 EventHandler handler = (EventHandler) Events [SelectedIndexChangedEvent];
616 protected internal virtual void VerifyMultiSelect ()
618 throw new HttpException("Multi select is not supported");
622 [WebSysDescription ("")]
623 [WebCategory ("Action")]
624 public event EventHandler SelectedIndexChanged {
625 add { Events.AddHandler (SelectedIndexChangedEvent, value); }
626 remove { Events.RemoveHandler (SelectedIndexChangedEvent, value); }
630 /* sealed in the docs */
631 public event EventHandler TextChanged {
633 Events.AddHandler (TextChangedEvent, value);
636 Events.RemoveHandler (TextChangedEvent, value);
642 [DefaultValue (false)]
643 [WebSysDescription ("")]
644 [WebCategory ("Behavior")]
645 public virtual bool CausesValidation {
647 return ViewState.GetBool ("CausesValidation", false);
651 ViewState ["CausesValidation"] = value;
657 [WebSysDescription ("")]
658 [WebCategoryAttribute ("Behavior")]
659 public virtual string ValidationGroup {
661 return ViewState.GetString ("ValidationGroup", "");
664 ViewState ["ValidationGroup"] = value;