2 // System.Web.UI.HtmlControls.HtmlSelect.cs
5 // Dick Porter <dick@ximian.com>
7 // Copyright (C) 2005-2010 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.
29 using System.Web.UI.WebControls;
30 using System.Web.Util;
31 using System.ComponentModel;
32 using System.Collections;
33 using System.Collections.Specialized;
34 using System.Globalization;
35 using System.Security.Permissions;
37 namespace System.Web.UI.HtmlControls
40 [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
41 [AspNetHostingPermission (SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
43 [DefaultEvent ("ServerChange")]
44 [ValidationProperty ("Value")]
45 [ControlBuilder (typeof (HtmlSelectBuilder))]
46 [SupportsEventValidation]
47 public class HtmlSelect : HtmlContainerControl, IPostBackDataHandler, IParserAccessor
49 static readonly object EventServerChange = new object ();
51 DataSourceView _boundDataSourceView;
52 bool requiresDataBinding;
55 ListItemCollection items;
57 public HtmlSelect () : base ("select")
62 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
63 [WebSysDescription("")]
65 public virtual string DataMember {
67 string member = Attributes["datamember"];
70 return (String.Empty);
77 Attributes.Remove ("datamember");
79 Attributes["datamember"] = value;
85 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
86 [WebSysDescription("")]
88 public virtual object DataSource {
93 if ((value != null) &&
94 !(value is IEnumerable) &&
95 !(value is IListSource)) {
96 throw new ArgumentException ();
104 public virtual string DataSourceID {
106 return ViewState.GetString ("DataSourceID", "");
109 if (DataSourceID == value)
111 ViewState ["DataSourceID"] = value;
112 if (_boundDataSourceView != null)
113 _boundDataSourceView.DataSourceViewChanged -= OnDataSourceViewChanged;
114 _boundDataSourceView = null;
115 OnDataPropertyChanged ();
120 [WebSysDescription("")]
121 [WebCategory("Data")]
122 public virtual string DataTextField {
124 string text = Attributes["datatextfield"];
127 return (String.Empty);
134 Attributes.Remove ("datatextfield");
136 Attributes["datatextfield"] = value;
142 [WebSysDescription("")]
143 [WebCategory("Data")]
144 public virtual string DataValueField {
146 string value = Attributes["datavaluefield"];
149 return (String.Empty);
156 Attributes.Remove ("datavaluefield");
158 Attributes["datavaluefield"] = value;
163 public override string InnerHtml {
165 throw new NotSupportedException ();
168 throw new NotSupportedException ();
172 public override string InnerText {
174 throw new NotSupportedException ();
177 throw new NotSupportedException ();
181 protected bool IsBoundUsingDataSourceID {
183 return (DataSourceID.Length != 0);
187 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
189 public ListItemCollection Items {
192 items = new ListItemCollection ();
193 if (IsTrackingViewState)
194 ((IStateManager) items).TrackViewState ();
202 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
203 [WebSysDescription("")]
204 [WebCategory("Behavior")]
205 public bool Multiple {
207 string multi = Attributes["multiple"];
216 if (value == false) {
217 Attributes.Remove ("multiple");
219 Attributes["multiple"] = "multiple";
225 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
226 [WebSysDescription("")]
227 [WebCategory("Behavior")]
237 protected bool RequiresDataBinding {
238 get { return requiresDataBinding; }
239 set { requiresDataBinding = value; }
242 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
244 public virtual int SelectedIndex {
246 /* Make sure Items has been initialised */
247 ListItemCollection listitems = Items;
249 for (int i = 0; i < listitems.Count; i++) {
250 if (listitems[i].Selected) {
255 /* There is always a selected item in
256 * non-multiple mode, if the size is
259 if (!Multiple && Size <= 1) {
260 /* Select the first item */
261 if (listitems.Count > 0) {
267 listitems[0].Selected = true;
278 if (value == -1 || items == null) {
282 if (value < 0 || value >= items.Count) {
283 throw new ArgumentOutOfRangeException ("value");
286 items[value].Selected = true;
290 /* "internal infrastructure" according to the docs,
291 * but has some documentation in 2.0
293 protected virtual int[] SelectedIndices {
295 ArrayList selected = new ArrayList ();
297 int count = Items.Count;
299 for (int i = 0; i < count; i++) {
300 if (Items [i].Selected) {
305 return ((int[])selected.ToArray (typeof (int)));
310 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
313 string size = Attributes["size"];
319 return (Int32.Parse (size, Helpers.InvariantCulture));
323 Attributes.Remove ("size");
325 Attributes["size"] = value.ToString ();
330 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
331 public string Value {
333 int sel = SelectedIndex;
335 if (sel >= 0 && sel < Items.Count) {
336 return (Items[sel].Value);
339 return (String.Empty);
342 int sel = Items.IndexOf (value);
350 [WebSysDescription("")]
351 [WebCategory("Action")]
352 public event EventHandler ServerChange {
354 Events.AddHandler (EventServerChange, value);
357 Events.RemoveHandler (EventServerChange, value);
361 protected override void AddParsedSubObject (object obj)
363 if (!(obj is ListItem)) {
364 throw new HttpException ("HtmlSelect can only contain ListItem");
367 Items.Add ((ListItem)obj);
369 base.AddParsedSubObject (obj);
372 /* "internal infrastructure" according to the docs,
373 * but has some documentation in 2.0
375 protected virtual void ClearSelection ()
381 int count = items.Count;
382 for (int i = 0; i < count; i++) {
383 items[i].Selected = false;
387 protected override ControlCollection CreateControlCollection ()
389 return (base.CreateControlCollection ());
392 protected void EnsureDataBound ()
394 if (IsBoundUsingDataSourceID && RequiresDataBinding)
398 protected virtual IEnumerable GetData ()
400 if (DataSource != null && IsBoundUsingDataSourceID)
401 throw new HttpException ("Control bound using both DataSourceID and DataSource properties.");
403 if (DataSource != null)
404 return DataSourceResolver.ResolveDataSource (DataSource, DataMember);
406 if (!IsBoundUsingDataSourceID)
409 IEnumerable result = null;
411 DataSourceView boundDataSourceView = ConnectToDataSource ();
412 boundDataSourceView.Select (DataSourceSelectArguments.Empty, delegate (IEnumerable data) { result = data; });
417 protected override void LoadViewState (object savedState)
420 object second = null;
422 Pair pair = savedState as Pair;
425 second = pair.Second;
428 base.LoadViewState (first);
430 if (second != null) {
431 IStateManager manager = Items as IStateManager;
432 manager.LoadViewState (second);
436 protected override void OnDataBinding (EventArgs e)
438 base.OnDataBinding (e);
440 /* Make sure Items has been initialised */
441 ListItemCollection listitems = Items;
445 IEnumerable list = GetData ();
449 foreach (object container in list) {
453 if (DataTextField == String.Empty &&
454 DataValueField == String.Empty) {
455 text = container.ToString ();
458 if (DataTextField != String.Empty) {
459 text = DataBinder.Eval (container, DataTextField).ToString ();
462 if (DataValueField != String.Empty) {
463 value = DataBinder.Eval (container, DataValueField).ToString ();
478 value = String.Empty;
481 ListItem item = new ListItem (text, value);
482 listitems.Add (item);
484 RequiresDataBinding = false;
488 protected virtual void OnDataPropertyChanged ()
491 RequiresDataBinding = true;
494 protected virtual void OnDataSourceViewChanged (object sender,
497 RequiresDataBinding = true;
500 protected internal override void OnInit (EventArgs e)
504 Page.PreLoad += new EventHandler (OnPagePreLoad);
507 protected virtual void OnPagePreLoad (object sender, EventArgs e)
512 protected internal override void OnLoad (EventArgs e)
525 RequiresDataBinding = true;
527 if (IsBoundUsingDataSourceID)
528 ConnectToDataSource ();
533 return ViewState.GetBool ("_DataBound", false);
536 ViewState ["_DataBound"] = value;
540 DataSourceView ConnectToDataSource ()
542 if (_boundDataSourceView != null)
543 return _boundDataSourceView;
545 /* verify that the data source exists and is an IDataSource */
549 ctrl = page.FindControl (DataSourceID);
551 if (ctrl == null || !(ctrl is IDataSource)) {
555 format = "DataSourceID of '{0}' must be the ID of a control of type IDataSource. A control with ID '{1}' could not be found.";
557 format = "DataSourceID of '{0}' must be the ID of a control of type IDataSource. '{1}' is not an IDataSource.";
559 throw new HttpException (String.Format (format, ID, DataSourceID));
562 _boundDataSourceView = ((IDataSource)ctrl).GetView (String.Empty);
563 _boundDataSourceView.DataSourceViewChanged += OnDataSourceViewChanged;
564 return _boundDataSourceView;
567 protected internal override void OnPreRender (EventArgs e)
570 base.OnPreRender (e);
573 if (page != null && !Disabled) {
574 page.RegisterRequiresPostBack (this);
575 page.RegisterEnabledControl (this);
579 protected virtual void OnServerChange (EventArgs e)
581 EventHandler handler = (EventHandler)Events[EventServerChange];
583 if (handler != null) {
588 protected override void RenderAttributes (HtmlTextWriter w)
592 page.ClientScript.RegisterForEventValidation (UniqueID);
594 /* If there is no "name" attribute,
595 * LoadPostData doesn't work...
597 w.WriteAttribute ("name", Name);
598 Attributes.Remove ("name");
600 /* Don't render the databinding attributes */
601 Attributes.Remove ("datamember");
602 Attributes.Remove ("datatextfield");
603 Attributes.Remove ("datavaluefield");
605 base.RenderAttributes (w);
608 protected internal override void RenderChildren (HtmlTextWriter w)
610 base.RenderChildren (w);
617 bool done_sel = false;
619 int count = items.Count;
620 for (int i = 0; i < count; i++) {
621 ListItem item = items[i];
624 /* Write the <option> elements this
625 * way so that the output HTML matches
626 * the ms version (can't make
627 * HtmlTextWriterTag.Option an inline
628 * element, cos that breaks other
631 w.WriteBeginTag ("option");
632 if (item.Selected && !done_sel) {
634 w.WriteAttribute ("selected", "selected");
641 w.WriteAttribute ("value", item.Value, true);
642 w.Write (HtmlTextWriter.TagRightChar);
644 w.Write (HttpUtility.HtmlEncode(item.Text));
645 w.WriteEndTag ("option");
652 protected override object SaveViewState ()
655 object second = null;
657 first = base.SaveViewState ();
659 IStateManager manager = items as IStateManager;
660 if (manager != null) {
661 second = manager.SaveViewState ();
664 if (first == null && second == null)
667 return new Pair (first, second);
670 /* "internal infrastructure" according to the docs,
671 * but has some documentation in 2.0
673 protected virtual void Select (int[] selectedIndices)
681 int count = items.Count;
682 foreach (int i in selectedIndices) {
683 if (i >= 0 && i < count) {
684 items[i].Selected = true;
689 protected override void TrackViewState ()
691 base.TrackViewState ();
693 IStateManager manager = items as IStateManager;
694 if (manager != null) {
695 manager.TrackViewState ();
699 protected virtual void RaisePostDataChangedEvent ()
701 OnServerChange (EventArgs.Empty);
704 protected virtual bool LoadPostData (string postDataKey, NameValueCollection postCollection)
706 /* postCollection contains the values that are
710 string[] values = postCollection.GetValues (postDataKey);
711 bool changed = false;
713 if (values != null) {
716 * selections. We can't just
717 * set the new list, because
718 * we need to know if the set
719 * has changed from last time
721 int value_len = values.Length;
722 int[] old_sel = SelectedIndices;
723 int[] new_sel = new int[value_len];
724 int old_sel_len = old_sel.Length;
726 for (int i = 0; i < value_len; i++) {
727 new_sel[i] = Items.IndexOf (values[i]);
728 if (old_sel_len != value_len ||
729 old_sel[i] != new_sel[i]) {
738 /* Just take the first one */
739 int sel = Items.IndexOf (values[0]);
741 if (sel != SelectedIndex) {
749 ValidateEvent (postDataKey, String.Empty);
753 bool IPostBackDataHandler.LoadPostData (string postDataKey, NameValueCollection postCollection)
755 return LoadPostData (postDataKey, postCollection);
758 void IPostBackDataHandler.RaisePostDataChangedEvent ()
760 RaisePostDataChangedEvent ();