2 // System.Web.UI.HtmlControls.HtmlSelect.cs
5 // Dick Porter <dick@ximian.com>
7 // Copyright (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.
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 public class HtmlSelect : HtmlContainerControl , IPostBackDataHandler
52 DataSourceView boundDataSourceView;
53 IDataSource boundDataSource;
54 private bool requiresDataBinding;
57 public HtmlSelect () : base ("select")
62 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
63 [WebSysDescription("")]
65 public virtual string DataMember
68 string member = Attributes["datamember"];
71 return (String.Empty);
78 Attributes.Remove ("datamember");
80 Attributes["datamember"] = value;
88 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
89 [WebSysDescription("")]
91 public virtual object DataSource
97 if ((value != null) &&
98 !(value is IEnumerable) &&
99 !(value is IListSource)) {
100 throw new ArgumentException ();
109 public virtual string DataSourceID
112 return ViewState.GetString ("DataSourceID", "");
115 if (datasource != null)
116 throw new HttpException ("Only one of DataSource and DataSourceID can be specified.");
117 ViewState ["DataSourceID"] = value;
119 OnDataPropertyChanged ();
126 [WebSysDescription("")]
127 [WebCategory("Data")]
128 public virtual string DataTextField
131 string text = Attributes["datatextfield"];
134 return (String.Empty);
141 Attributes.Remove ("datatextfield");
143 Attributes["datatextfield"] = value;
149 [WebSysDescription("")]
150 [WebCategory("Data")]
151 public virtual string DataValueField
154 string value = Attributes["datavaluefield"];
157 return (String.Empty);
164 Attributes.Remove ("datavaluefield");
166 Attributes["datavaluefield"] = value;
171 public override string InnerHtml
174 throw new NotSupportedException ();
177 throw new NotSupportedException ();
181 public override string InnerText
184 throw new NotSupportedException ();
187 throw new NotSupportedException ();
192 protected bool IsBoundUsingDataSourceID
195 return (DataSourceID.Length != 0);
200 ListItemCollection items;
202 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
204 public ListItemCollection Items
208 items = new ListItemCollection ();
216 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
217 [WebSysDescription("")]
218 [WebCategory("Behavior")]
222 string multi = Attributes["multiple"];
231 if (value == false) {
232 Attributes.Remove ("multiple");
234 Attributes["multiple"] = "multiple";
240 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
241 [WebSysDescription("")]
242 [WebCategory("Behavior")]
254 protected bool RequiresDataBinding
256 get { return requiresDataBinding; }
257 set { requiresDataBinding = value; }
261 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
263 public virtual int SelectedIndex
266 /* Make sure Items has been initialised */
267 ListItemCollection listitems = Items;
269 for (int i = 0; i < listitems.Count; i++) {
270 if (listitems[i].Selected) {
275 /* There is always a selected item in
276 * non-multiple mode, if the size is
279 if (!Multiple && Size <= 1) {
280 /* Select the first item */
281 if (listitems.Count > 0) {
287 listitems[0].Selected = true;
298 if (value == -1 || items == null) {
302 if (value < 0 || value >= items.Count) {
303 throw new ArgumentOutOfRangeException ("value");
306 items[value].Selected = true;
310 /* "internal infrastructure" according to the docs,
311 * but has some documentation in 2.0
313 protected virtual int[] SelectedIndices
316 ArrayList selected = new ArrayList ();
318 int count = Items.Count;
320 for (int i = 0; i < count; i++) {
321 if (Items [i].Selected) {
326 return ((int[])selected.ToArray (typeof (int)));
331 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
335 string size = Attributes["size"];
341 return (Int32.Parse (size, CultureInfo.InvariantCulture));
345 Attributes.Remove ("size");
347 Attributes["size"] = value.ToString ();
352 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
356 int sel = SelectedIndex;
358 if (sel >= 0 && sel < Items.Count) {
359 return (Items[sel].Value);
362 return (String.Empty);
365 int sel = Items.IndexOf (value);
373 private static readonly object EventServerChange = new object ();
375 [WebSysDescription("")]
376 [WebCategory("Action")]
377 public event EventHandler ServerChange
380 Events.AddHandler (EventServerChange, value);
383 Events.RemoveHandler (EventServerChange, value);
387 protected override void AddParsedSubObject (object obj)
389 if (!(obj is ListItem)) {
390 throw new HttpException ("HtmlSelect can only contain ListItem");
393 Items.Add ((ListItem)obj);
395 base.AddParsedSubObject (obj);
398 /* "internal infrastructure" according to the docs,
399 * but has some documentation in 2.0
401 protected virtual void ClearSelection ()
407 int count = items.Count;
408 for (int i = 0; i < count; i++) {
409 items[i].Selected = false;
413 protected override ControlCollection CreateControlCollection ()
415 return (base.CreateControlCollection ());
419 protected void EnsureDataBound ()
421 if (IsBoundUsingDataSourceID && RequiresDataBinding)
425 private void SelectCallback (IEnumerable data)
430 protected virtual IEnumerable GetData ()
434 if (DataSourceID.Length == 0)
437 boundDataSourceView = boundDataSource.GetView (String.Empty);
438 boundDataSourceView.Select (new DataSourceSelectArguments (), SelectCallback);
439 boundDataSourceView.DataSourceViewChanged += OnDataSourceViewChanged;
448 protected override void LoadViewState (object savedState)
451 object second = null;
452 int[] selected = null;
454 Triplet triplet = savedState as Triplet;
455 if (triplet != null) {
456 first = triplet.First;
457 second = triplet.Second;
458 selected = triplet.Third as int[];
461 base.LoadViewState (first);
463 if (second != null) {
464 IStateManager manager = Items as IStateManager;
465 manager.LoadViewState (second);
468 if (selected != null) {
473 protected override void OnDataBinding (EventArgs e)
475 base.OnDataBinding (e);
477 /* Make sure Items has been initialised */
478 ListItemCollection listitems = Items;
485 if (IsBoundUsingDataSourceID)
489 list = DataSourceResolver.ResolveDataSource (DataSource, DataMember);
495 foreach (object container in list) {
499 if (DataTextField == String.Empty &&
500 DataValueField == String.Empty) {
501 text = container.ToString ();
504 if (DataTextField != String.Empty) {
505 text = DataBinder.Eval (container, DataTextField).ToString ();
508 if (DataValueField != String.Empty) {
509 value = DataBinder.Eval (container, DataValueField).ToString ();
524 value = String.Empty;
527 ListItem item = new ListItem (text, value);
528 listitems.Add (item);
534 protected virtual void OnDataPropertyChanged ()
536 RequiresDataBinding = true;
540 protected virtual void OnDataSourceViewChanged (object sender,
543 RequiresDataBinding = true;
547 protected internal override void OnInit (EventArgs e)
552 protected internal override void OnLoad (EventArgs e)
554 if ((Page != null) && !Page.IsPostBack)
555 RequiresDataBinding = true;
559 if (IsBoundUsingDataSourceID)
560 ConnectToDataSource ();
563 void ConnectToDataSource ()
565 /* verify that the data source exists and is an IDataSource */
568 ctrl = Page.FindControl (DataSourceID);
570 if (ctrl == null || !(ctrl is IDataSource)) {
574 format = "DataSourceID of '{0}' must be the ID of a control of type IDataSource. A control with ID '{1}' could not be found.";
576 format = "DataSourceID of '{0}' must be the ID of a control of type IDataSource. '{1}' is not an IDataSource.";
578 throw new HttpException (String.Format (format, ID, DataSourceID));
581 boundDataSource = (IDataSource)ctrl;
590 override void OnPreRender (EventArgs e)
596 base.OnPreRender (e);
599 Page.RegisterRequiresPostBack (this);
603 protected virtual void OnServerChange (EventArgs e)
605 EventHandler handler = (EventHandler)Events[EventServerChange];
607 if (handler != null) {
612 protected override void RenderAttributes (HtmlTextWriter w)
614 /* If there is no "name" attribute,
615 * LoadPostData doesn't work...
617 w.WriteAttribute ("name", Name);
618 Attributes.Remove ("name");
620 /* Don't render the databinding attributes */
621 Attributes.Remove ("datamember");
622 Attributes.Remove ("datatextfield");
623 Attributes.Remove ("datavaluefield");
625 base.RenderAttributes (w);
633 override void RenderChildren (HtmlTextWriter w)
635 base.RenderChildren (w);
643 bool done_sel = false;
645 int count = items.Count;
646 for (int i = 0; i < count; i++) {
647 ListItem item = items[i];
651 /* Write the <option> elements this
652 * way so that the output HTML matches
653 * the ms version (can't make
654 * HtmlTextWriterTag.Option an inline
655 * element, cos that breaks other
658 w.WriteBeginTag ("option");
659 if (item.Selected && !done_sel) {
661 w.WriteAttribute ("selected", "selected");
668 w.WriteAttribute ("value", item.Value);
669 w.Write (HtmlTextWriter.TagRightChar);
672 w.WriteEndTag ("option");
679 protected override object SaveViewState ()
682 object second = null;
683 object selected = null;
685 first = base.SaveViewState ();
687 IStateManager manager = items as IStateManager;
688 if (manager != null) {
689 second = manager.SaveViewState ();
692 selected = SelectedIndices;
700 return (new Triplet (first, second, selected));
703 /* "internal infrastructure" according to the docs,
704 * but has some documentation in 2.0
706 protected virtual void Select (int[] selectedIndices)
714 int count = items.Count;
715 foreach (int i in selectedIndices) {
716 if (i >= 0 && i < count) {
717 items[i].Selected = true;
722 protected override void TrackViewState ()
724 base.TrackViewState ();
726 IStateManager manager = items as IStateManager;
727 if (manager != null) {
728 manager.TrackViewState ();
733 protected virtual bool LoadPostData (string postDataKey, NameValueCollection postCollection)
735 return (LoadPostData (postDataKey, postCollection));
738 protected virtual void RaisePostDataChangedEvent ()
740 RaisePostDataChangedEvent ();
744 bool IPostBackDataHandler.LoadPostData (string postDataKey, NameValueCollection postCollection)
746 /* postCollection contains the values that are
750 string[] values = postCollection.GetValues (postDataKey);
751 bool changed = false;
753 if (values != null) {
756 * selections. We can't just
757 * set the new list, because
758 * we need to know if the set
759 * has changed from last time
761 int value_len = values.Length;
762 int[] old_sel = SelectedIndices;
763 int[] new_sel = new int[value_len];
764 int old_sel_len = old_sel.Length;
766 for (int i = 0; i < value_len; i++) {
767 new_sel[i] = Items.IndexOf (values[i]);
768 if (old_sel_len != value_len ||
769 old_sel[i] != new_sel[i]) {
778 /* Just take the first one */
779 int sel = Items.IndexOf (values[0]);
781 if (sel != SelectedIndex) {
791 void IPostBackDataHandler.RaisePostDataChangedEvent ()
793 OnServerChange (EventArgs.Empty);