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);
397 IEnumerable list = GetData ().ExecuteSelect (DataSourceSelectArguments.Empty);
399 PerformDataBinding (list);
408 override void OnPreRender (EventArgs e)
410 base.OnPreRender (e);
412 if (Page != null && Enabled)
413 Page.RegisterEnabledControl (this);
418 protected virtual void OnTextChanged (EventArgs e)
420 EventHandler handler = (EventHandler) Events [TextChangedEvent];
427 protected internal override
429 void PerformDataBinding (IEnumerable dataSource)
431 if (dataSource == null)
438 if (!AppendDataBoundItems)
442 string format = DataTextFormatString;
446 string text_field = DataTextField;
447 string value_field = DataValueField;
448 ListItemCollection coll = Items;
449 foreach (object container in dataSource) {
454 if (text_field != "") {
455 text = DataBinder.GetPropertyValue (container, text_field, format);
458 if (value_field != "") {
459 val = DataBinder.GetPropertyValue (container, value_field).ToString ();
461 else if (text_field == "") {
462 text = val = container.ToString ();
464 text = String.Format (format, container);
466 else if (text != null) {
473 coll.Add (new ListItem (text, val));
478 if (!String.IsNullOrEmpty (_selectedValue)) {
479 if (!SetSelectedValue (_selectedValue))
480 throw new ArgumentOutOfRangeException ("value", String.Format ("'{0}' has a SelectedValue which is invalid because it does not exist in the list of items.", ID));
481 if (_selectedIndex >= 0 && _selectedIndex != SelectedIndex)
482 throw new ArgumentException ("SelectedIndex and SelectedValue are mutually exclusive.");
484 else if (_selectedIndex >= 0) {
485 SelectedIndex = _selectedIndex;
488 if (saved_selected_value != null) {
489 SelectedValue = saved_selected_value;
490 if (saved_selected_index != -2 && saved_selected_index != SelectedIndex)
491 throw new ArgumentException ("SelectedIndex and SelectedValue are mutually exclusive.");
493 else if (saved_selected_index != -2) {
494 SelectedIndex = saved_selected_index;
495 // No need to check saved_selected_value here, as it's done before.
501 [MonoTODO ("why override?")]
502 protected override void PerformSelect ()
504 OnDataBinding (EventArgs.Empty);
505 RequiresDataBinding = false;
507 OnDataBound (EventArgs.Empty);
510 protected internal override void RenderContents (HtmlTextWriter writer)
512 bool selected = false;
513 bool havePage = Page != null;
514 for (int i = 0; i < Items.Count; i++) {
515 ListItem item = Items [i];
517 Page.ClientScript.RegisterForEventValidation (this.UniqueID, item.Value.ToString ());
518 writer.WriteBeginTag ("option");
521 VerifyMultiSelect ();
522 writer.WriteAttribute ("selected", "selected", false);
525 writer.WriteAttribute ("value", item.Value, true);
527 if (item.HasAttributes)
528 item.Attributes.Render (writer);
531 string encoded = HttpUtility.HtmlEncode (item.Text);
532 writer.Write (encoded);
533 writer.WriteEndTag ("option");
540 internal ArrayList GetSelectedIndicesInternal ()
542 ArrayList selected = null;
545 if (items != null && (count = items.Count) > 0) {
546 selected = new ArrayList ();
547 for (int i = 0; i < count; i++) {
548 if (items [i].Selected)
555 protected override object SaveViewState ()
557 object baseState = null;
558 object itemsState = null;
560 baseState = base.SaveViewState ();
562 IStateManager manager = items as IStateManager;
564 itemsState = manager.SaveViewState ();
566 if (baseState == null && itemsState == null)
569 return new Pair (baseState, itemsState);
572 protected override void LoadViewState (object savedState)
574 object baseState = null;
575 object itemsState = null;
577 Pair pair = savedState as Pair;
579 baseState = pair.First;
580 itemsState = pair.Second;
583 base.LoadViewState (baseState);
585 if (itemsState != null) {
586 IStateManager manager = Items as IStateManager;
587 manager.LoadViewState (itemsState);
592 [MonoTODO ("Not implemented")]
593 protected void SetPostDataSelection (int selectedIndex)
595 throw new NotImplementedException ();
599 protected override void TrackViewState ()
601 base.TrackViewState ();
602 IStateManager manager = items as IStateManager;
604 manager.TrackViewState ();
607 protected virtual void OnSelectedIndexChanged (EventArgs e)
609 EventHandler handler = (EventHandler) Events [SelectedIndexChangedEvent];
615 protected internal virtual void VerifyMultiSelect ()
617 throw new HttpException("Multi select is not supported");
621 [WebSysDescription ("")]
622 [WebCategory ("Action")]
623 public event EventHandler SelectedIndexChanged {
624 add { Events.AddHandler (SelectedIndexChangedEvent, value); }
625 remove { Events.RemoveHandler (SelectedIndexChangedEvent, value); }
629 /* sealed in the docs */
630 public event EventHandler TextChanged {
632 Events.AddHandler (TextChangedEvent, value);
635 Events.RemoveHandler (TextChangedEvent, value);
641 [DefaultValue (false)]
642 [WebSysDescription ("")]
643 [WebCategory ("Behavior")]
644 public virtual bool CausesValidation {
646 return ViewState.GetBool ("CausesValidation", false);
650 ViewState ["CausesValidation"] = value;
656 [WebSysDescription ("")]
657 [WebCategoryAttribute ("Behavior")]
658 public virtual string ValidationGroup {
660 return ViewState.GetString ("ValidationGroup", "");
663 ViewState ["ValidationGroup"] = value;