1 // 2 // System.Web.UI.HtmlControls.HtmlSelect.cs 3 // 4 // Author: 5 // Dick Porter <dick@ximian.com> 6 // 7 // Copyright (C) 2005-2010 Novell, Inc (http://www.novell.com) 8 // 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: 16 // 17 // The above copyright notice and this permission notice shall be 18 // included in all copies or substantial portions of the Software. 19 // 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. 27 // 28 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; 36 37 namespace System.Web.UI.HtmlControls 38 { 39 // CAS 40 [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)] 41 [AspNetHostingPermission (SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)] 42 // attributes 43 [DefaultEvent ("ServerChange")] 44 [ValidationProperty ("Value")] 45 [ControlBuilder (typeof (HtmlSelectBuilder))] 46 [SupportsEventValidation] 47 public class HtmlSelect : HtmlContainerControl, IPostBackDataHandler, IParserAccessor 48 { 49 static readonly object EventServerChange = new object (); 50 51 DataSourceView _boundDataSourceView; 52 bool requiresDataBinding; 53 bool _initialized; 54 object datasource; 55 ListItemCollection items; 56 HtmlSelect()57 public HtmlSelect () : base ("select") 58 { 59 } 60 61 [DefaultValue ("")] 62 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)] 63 [WebSysDescription("")] 64 [WebCategory("Data")] 65 public virtual string DataMember { 66 get { 67 string member = Attributes["datamember"]; 68 69 if (member == null) { 70 return (String.Empty); 71 } 72 73 return (member); 74 } 75 set { 76 if (value == null) { 77 Attributes.Remove ("datamember"); 78 } else { 79 Attributes["datamember"] = value; 80 } 81 } 82 } 83 84 [DefaultValue (null)] 85 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)] 86 [WebSysDescription("")] 87 [WebCategory("Data")] 88 public virtual object DataSource { 89 get { 90 return (datasource); 91 } 92 set { 93 if ((value != null) && 94 !(value is IEnumerable) && 95 !(value is IListSource)) { 96 throw new ArgumentException (); 97 } 98 99 datasource = value; 100 } 101 } 102 103 [DefaultValue ("")] 104 public virtual string DataSourceID { 105 get { 106 return ViewState.GetString ("DataSourceID", ""); 107 } 108 set { 109 if (DataSourceID == value) 110 return; 111 ViewState ["DataSourceID"] = value; 112 if (_boundDataSourceView != null) 113 _boundDataSourceView.DataSourceViewChanged -= OnDataSourceViewChanged; 114 _boundDataSourceView = null; 115 OnDataPropertyChanged (); 116 } 117 } 118 119 [DefaultValue ("")] 120 [WebSysDescription("")] 121 [WebCategory("Data")] 122 public virtual string DataTextField { 123 get { 124 string text = Attributes["datatextfield"]; 125 126 if (text == null) { 127 return (String.Empty); 128 } 129 130 return (text); 131 } 132 set { 133 if (value == null) { 134 Attributes.Remove ("datatextfield"); 135 } else { 136 Attributes["datatextfield"] = value; 137 } 138 } 139 } 140 141 [DefaultValue ("")] 142 [WebSysDescription("")] 143 [WebCategory("Data")] 144 public virtual string DataValueField { 145 get { 146 string value = Attributes["datavaluefield"]; 147 148 if (value == null) { 149 return (String.Empty); 150 } 151 152 return (value); 153 } 154 set { 155 if (value == null) { 156 Attributes.Remove ("datavaluefield"); 157 } else { 158 Attributes["datavaluefield"] = value; 159 } 160 } 161 } 162 163 public override string InnerHtml { 164 get { 165 throw new NotSupportedException (); 166 } 167 set { 168 throw new NotSupportedException (); 169 } 170 } 171 172 public override string InnerText { 173 get { 174 throw new NotSupportedException (); 175 } 176 set { 177 throw new NotSupportedException (); 178 } 179 } 180 181 protected bool IsBoundUsingDataSourceID { 182 get { 183 return (DataSourceID.Length != 0); 184 } 185 } 186 187 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)] 188 [Browsable (false)] 189 public ListItemCollection Items { 190 get { 191 if (items == null) { 192 items = new ListItemCollection (); 193 if (IsTrackingViewState) 194 ((IStateManager) items).TrackViewState (); 195 } 196 197 return (items); 198 } 199 } 200 201 [DefaultValue ("")] 202 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)] 203 [WebSysDescription("")] 204 [WebCategory("Behavior")] 205 public bool Multiple { 206 get { 207 string multi = Attributes["multiple"]; 208 209 if (multi == null) { 210 return (false); 211 } 212 213 return (true); 214 } 215 set { 216 if (value == false) { 217 Attributes.Remove ("multiple"); 218 } else { 219 Attributes["multiple"] = "multiple"; 220 } 221 } 222 } 223 224 [DefaultValue ("")] 225 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)] 226 [WebSysDescription("")] 227 [WebCategory("Behavior")] 228 public string Name { 229 get { 230 return (UniqueID); 231 } 232 set { 233 /* Do nothing */ 234 } 235 } 236 237 protected bool RequiresDataBinding { 238 get { return requiresDataBinding; } 239 set { requiresDataBinding = value; } 240 } 241 242 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)] 243 [Browsable (false)] 244 public virtual int SelectedIndex { 245 get { 246 /* Make sure Items has been initialised */ 247 ListItemCollection listitems = Items; 248 249 for (int i = 0; i < listitems.Count; i++) { 250 if (listitems[i].Selected) { 251 return (i); 252 } 253 } 254 255 /* There is always a selected item in 256 * non-multiple mode, if the size is 257 * <= 1 258 */ 259 if (!Multiple && Size <= 1) { 260 /* Select the first item */ 261 if (listitems.Count > 0) { 262 /* And make it stick 263 * if there is 264 * anything in the 265 * list 266 */ 267 listitems[0].Selected = true; 268 } 269 270 return (0); 271 } 272 273 return (-1); 274 } 275 set { 276 ClearSelection (); 277 278 if (value == -1 || items == null) { 279 return; 280 } 281 282 if (value < 0 || value >= items.Count) { 283 throw new ArgumentOutOfRangeException ("value"); 284 } 285 286 items[value].Selected = true; 287 } 288 } 289 290 /* "internal infrastructure" according to the docs, 291 * but has some documentation in 2.0 292 */ 293 protected virtual int[] SelectedIndices { 294 get { 295 ArrayList selected = new ArrayList (); 296 297 int count = Items.Count; 298 299 for (int i = 0; i < count; i++) { 300 if (Items [i].Selected) { 301 selected.Add (i); 302 } 303 } 304 305 return ((int[])selected.ToArray (typeof (int))); 306 } 307 } 308 309 310 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)] 311 public int Size { 312 get { 313 string size = Attributes["size"]; 314 315 if (size == null) { 316 return (-1); 317 } 318 319 return (Int32.Parse (size, Helpers.InvariantCulture)); 320 } 321 set { 322 if (value == -1) { 323 Attributes.Remove ("size"); 324 } else { 325 Attributes["size"] = value.ToString (); 326 } 327 } 328 } 329 330 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)] 331 public string Value { 332 get { 333 int sel = SelectedIndex; 334 335 if (sel >= 0 && sel < Items.Count) { 336 return (Items[sel].Value); 337 } 338 339 return (String.Empty); 340 } 341 set { 342 int sel = Items.IndexOf (value); 343 344 if (sel >= 0) { 345 SelectedIndex = sel; 346 } 347 } 348 } 349 350 [WebSysDescription("")] 351 [WebCategory("Action")] 352 public event EventHandler ServerChange { 353 add { 354 Events.AddHandler (EventServerChange, value); 355 } 356 remove { 357 Events.RemoveHandler (EventServerChange, value); 358 } 359 } 360 AddParsedSubObject(object obj)361 protected override void AddParsedSubObject (object obj) 362 { 363 if (!(obj is ListItem)) { 364 throw new HttpException ("HtmlSelect can only contain ListItem"); 365 } 366 367 Items.Add ((ListItem)obj); 368 369 base.AddParsedSubObject (obj); 370 } 371 372 /* "internal infrastructure" according to the docs, 373 * but has some documentation in 2.0 374 */ ClearSelection()375 protected virtual void ClearSelection () 376 { 377 if (items == null) { 378 return; 379 } 380 381 int count = items.Count; 382 for (int i = 0; i < count; i++) { 383 items[i].Selected = false; 384 } 385 } 386 CreateControlCollection()387 protected override ControlCollection CreateControlCollection () 388 { 389 return (base.CreateControlCollection ()); 390 } 391 EnsureDataBound()392 protected void EnsureDataBound () 393 { 394 if (IsBoundUsingDataSourceID && RequiresDataBinding) 395 DataBind (); 396 } 397 GetData()398 protected virtual IEnumerable GetData () 399 { 400 if (DataSource != null && IsBoundUsingDataSourceID) 401 throw new HttpException ("Control bound using both DataSourceID and DataSource properties."); 402 403 if (DataSource != null) 404 return DataSourceResolver.ResolveDataSource (DataSource, DataMember); 405 406 if (!IsBoundUsingDataSourceID) 407 return null; 408 409 IEnumerable result = null; 410 411 DataSourceView boundDataSourceView = ConnectToDataSource (); 412 boundDataSourceView.Select (DataSourceSelectArguments.Empty, delegate (IEnumerable data) { result = data; }); 413 414 return result; 415 } 416 LoadViewState(object savedState)417 protected override void LoadViewState (object savedState) 418 { 419 object first = null; 420 object second = null; 421 422 Pair pair = savedState as Pair; 423 if (pair != null) { 424 first = pair.First; 425 second = pair.Second; 426 } 427 428 base.LoadViewState (first); 429 430 if (second != null) { 431 IStateManager manager = Items as IStateManager; 432 manager.LoadViewState (second); 433 } 434 } 435 OnDataBinding(EventArgs e)436 protected override void OnDataBinding (EventArgs e) 437 { 438 base.OnDataBinding (e); 439 440 /* Make sure Items has been initialised */ 441 ListItemCollection listitems = Items; 442 443 listitems.Clear (); 444 445 IEnumerable list = GetData (); 446 if (list == null) 447 return; 448 449 foreach (object container in list) { 450 string text = null; 451 string value = null; 452 453 if (DataTextField == String.Empty && 454 DataValueField == String.Empty) { 455 text = container.ToString (); 456 value = text; 457 } else { 458 if (DataTextField != String.Empty) { 459 text = DataBinder.Eval (container, DataTextField).ToString (); 460 } 461 462 if (DataValueField != String.Empty) { 463 value = DataBinder.Eval (container, DataValueField).ToString (); 464 } else { 465 value = text; 466 } 467 468 if (text == null && 469 value != null) { 470 text = value; 471 } 472 } 473 474 if (text == null) { 475 text = String.Empty; 476 } 477 if (value == null) { 478 value = String.Empty; 479 } 480 481 ListItem item = new ListItem (text, value); 482 listitems.Add (item); 483 } 484 RequiresDataBinding = false; 485 IsDataBound = true; 486 } 487 OnDataPropertyChanged()488 protected virtual void OnDataPropertyChanged () 489 { 490 if (_initialized) 491 RequiresDataBinding = true; 492 } 493 OnDataSourceViewChanged(object sender, EventArgs e)494 protected virtual void OnDataSourceViewChanged (object sender, 495 EventArgs e) 496 { 497 RequiresDataBinding = true; 498 } 499 OnInit(EventArgs e)500 protected internal override void OnInit (EventArgs e) 501 { 502 base.OnInit (e); 503 504 Page.PreLoad += new EventHandler (OnPagePreLoad); 505 } 506 OnPagePreLoad(object sender, EventArgs e)507 protected virtual void OnPagePreLoad (object sender, EventArgs e) 508 { 509 Initialize (); 510 } 511 OnLoad(EventArgs e)512 protected internal override void OnLoad (EventArgs e) 513 { 514 if (!_initialized) 515 Initialize (); 516 517 base.OnLoad (e); 518 } 519 Initialize()520 void Initialize () 521 { 522 _initialized = true; 523 524 if (!IsDataBound) 525 RequiresDataBinding = true; 526 527 if (IsBoundUsingDataSourceID) 528 ConnectToDataSource (); 529 } 530 531 bool IsDataBound{ 532 get { 533 return ViewState.GetBool ("_DataBound", false); 534 } 535 set { 536 ViewState ["_DataBound"] = value; 537 } 538 } 539 ConnectToDataSource()540 DataSourceView ConnectToDataSource () 541 { 542 if (_boundDataSourceView != null) 543 return _boundDataSourceView; 544 545 /* verify that the data source exists and is an IDataSource */ 546 object ctrl = null; 547 Page page = Page; 548 if (page != null) 549 ctrl = page.FindControl (DataSourceID); 550 551 if (ctrl == null || !(ctrl is IDataSource)) { 552 string format; 553 554 if (ctrl == null) 555 format = "DataSourceID of '{0}' must be the ID of a control of type IDataSource. A control with ID '{1}' could not be found."; 556 else 557 format = "DataSourceID of '{0}' must be the ID of a control of type IDataSource. '{1}' is not an IDataSource."; 558 559 throw new HttpException (String.Format (format, ID, DataSourceID)); 560 } 561 562 _boundDataSourceView = ((IDataSource)ctrl).GetView (String.Empty); 563 _boundDataSourceView.DataSourceViewChanged += OnDataSourceViewChanged; 564 return _boundDataSourceView; 565 } 566 OnPreRender(EventArgs e)567 protected internal override void OnPreRender (EventArgs e) 568 { 569 EnsureDataBound (); 570 base.OnPreRender (e); 571 572 Page page = Page; 573 if (page != null && !Disabled) { 574 page.RegisterRequiresPostBack (this); 575 page.RegisterEnabledControl (this); 576 } 577 } 578 OnServerChange(EventArgs e)579 protected virtual void OnServerChange (EventArgs e) 580 { 581 EventHandler handler = (EventHandler)Events[EventServerChange]; 582 583 if (handler != null) { 584 handler (this, e); 585 } 586 } 587 RenderAttributes(HtmlTextWriter writer)588 protected override void RenderAttributes (HtmlTextWriter writer) 589 { 590 Page page = Page; 591 if (page != null) 592 page.ClientScript.RegisterForEventValidation (UniqueID); 593 594 /* If there is no "name" attribute, 595 * LoadPostData doesn't work... 596 */ 597 writer.WriteAttribute ("name", Name); 598 Attributes.Remove ("name"); 599 600 /* Don't render the databinding attributes */ 601 Attributes.Remove ("datamember"); 602 Attributes.Remove ("datatextfield"); 603 Attributes.Remove ("datavaluefield"); 604 605 base.RenderAttributes (writer); 606 } 607 RenderChildren(HtmlTextWriter writer)608 protected internal override void RenderChildren (HtmlTextWriter writer) 609 { 610 base.RenderChildren (writer); 611 612 if (items == null) 613 return; 614 615 writer.WriteLine (); 616 617 bool done_sel = false; 618 619 int count = items.Count; 620 for (int i = 0; i < count; i++) { 621 ListItem item = items[i]; 622 writer.Indent++; 623 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 629 * stuff.) 630 */ 631 writer.WriteBeginTag ("option"); 632 if (item.Selected && !done_sel) { 633 634 writer.WriteAttribute ("selected", "selected"); 635 636 if (!Multiple) { 637 done_sel = true; 638 } 639 } 640 641 writer.WriteAttribute ("value", item.Value, true); 642 if (item.HasAttributes) { 643 AttributeCollection attrs = item.Attributes; 644 foreach (string key in attrs.Keys) 645 writer.WriteAttribute (key, HttpUtility.HtmlAttributeEncode (attrs [key])); 646 } 647 writer.Write (HtmlTextWriter.TagRightChar); 648 649 writer.Write (HttpUtility.HtmlEncode(item.Text)); 650 writer.WriteEndTag ("option"); 651 writer.WriteLine (); 652 653 writer.Indent--; 654 } 655 } 656 SaveViewState()657 protected override object SaveViewState () 658 { 659 object first = null; 660 object second = null; 661 662 first = base.SaveViewState (); 663 664 IStateManager manager = items as IStateManager; 665 if (manager != null) { 666 second = manager.SaveViewState (); 667 } 668 669 if (first == null && second == null) 670 return (null); 671 672 return new Pair (first, second); 673 } 674 675 /* "internal infrastructure" according to the docs, 676 * but has some documentation in 2.0 677 */ Select(int[] selectedIndices)678 protected virtual void Select (int[] selectedIndices) 679 { 680 if (items == null) { 681 return; 682 } 683 684 ClearSelection (); 685 686 int count = items.Count; 687 foreach (int i in selectedIndices) { 688 if (i >= 0 && i < count) { 689 items[i].Selected = true; 690 } 691 } 692 } 693 TrackViewState()694 protected override void TrackViewState () 695 { 696 base.TrackViewState (); 697 698 IStateManager manager = items as IStateManager; 699 if (manager != null) { 700 manager.TrackViewState (); 701 } 702 } 703 RaisePostDataChangedEvent()704 protected virtual void RaisePostDataChangedEvent () 705 { 706 OnServerChange (EventArgs.Empty); 707 } 708 LoadPostData(string postDataKey, NameValueCollection postCollection)709 protected virtual bool LoadPostData (string postDataKey, NameValueCollection postCollection) 710 { 711 /* postCollection contains the values that are 712 * selected 713 */ 714 715 string[] values = postCollection.GetValues (postDataKey); 716 bool changed = false; 717 718 if (values != null) { 719 if (Multiple) { 720 /* We have a set of 721 * selections. We can't just 722 * set the new list, because 723 * we need to know if the set 724 * has changed from last time 725 */ 726 int value_len = values.Length; 727 int[] old_sel = SelectedIndices; 728 int[] new_sel = new int[value_len]; 729 int old_sel_len = old_sel.Length; 730 731 for (int i = 0; i < value_len; i++) { 732 new_sel[i] = Items.IndexOf (values[i]); 733 if (old_sel_len != value_len || 734 old_sel[i] != new_sel[i]) { 735 changed = true; 736 } 737 } 738 739 if (changed) { 740 Select (new_sel); 741 } 742 } else { 743 /* Just take the first one */ 744 int sel = Items.IndexOf (values[0]); 745 746 if (sel != SelectedIndex) { 747 SelectedIndex = sel; 748 changed = true; 749 } 750 } 751 } 752 753 if (changed) 754 ValidateEvent (postDataKey, String.Empty); 755 return (changed); 756 } 757 IPostBackDataHandler.LoadPostData(string postDataKey, NameValueCollection postCollection)758 bool IPostBackDataHandler.LoadPostData (string postDataKey, NameValueCollection postCollection) 759 { 760 return LoadPostData (postDataKey, postCollection); 761 } 762 IPostBackDataHandler.RaisePostDataChangedEvent()763 void IPostBackDataHandler.RaisePostDataChangedEvent () 764 { 765 RaisePostDataChangedEvent (); 766 } 767 } 768 } 769