1 //------------------------------------------------------------------------------
2 // <copyright file="CatalogZoneBase.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 //------------------------------------------------------------------------------
7 namespace System.Web.UI.WebControls.WebParts {
10 using System.Collections;
11 using System.Collections.Specialized;
12 using System.ComponentModel;
14 using System.Globalization;
17 using System.Web.UI.WebControls;
18 using System.Web.Util;
20 public abstract class CatalogZoneBase : ToolZone, IPostBackDataHandler {
22 private CatalogPartCollection _catalogParts;
24 private string[] _selectedCheckBoxValues;
25 private string _selectedZoneID;
26 private string _selectedCatalogPartID;
28 private const int baseIndex = 0;
29 private const int addVerbIndex = 1;
30 private const int closeVerbIndex = 2;
31 private const int partLinkStyleIndex = 3;
32 private const int selectedPartLinkStyleIndex = 4;
33 private const int viewStateArrayLength = 5;
35 // Use same baseIndex as above
36 private const int selectedCatalogPartIDIndex = 1;
37 private const int controlStateArrayLength = 2;
39 private WebPartVerb _addVerb;
40 private WebPartVerb _closeVerb;
41 private Style _partLinkStyle;
42 private Style _selectedPartLinkStyle;
43 private CatalogPartChrome _catalogPartChrome;
45 private const string addEventArgument = "add";
46 private const string closeEventArgument = "close";
47 private const string selectEventArgument = "select";
49 protected CatalogZoneBase() : base(WebPartManager.CatalogDisplayMode) {
54 DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
55 NotifyParentProperty(true),
56 PersistenceMode(PersistenceMode.InnerProperty),
58 WebSysDescription(SR.CatalogZoneBase_AddVerb),
60 public virtual WebPartVerb AddVerb {
62 if (_addVerb == null) {
63 _addVerb = new WebPartCatalogAddVerb();
64 _addVerb.EventArgument = addEventArgument;
65 if (IsTrackingViewState) {
66 ((IStateManager)_addVerb).TrackViewState();
74 internal string CheckBoxName {
76 return UniqueID + ID_SEPARATOR + "_checkbox";
82 DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
84 public CatalogPartChrome CatalogPartChrome {
86 if (_catalogPartChrome == null) {
87 _catalogPartChrome = CreateCatalogPartChrome();
89 return _catalogPartChrome;
95 DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
97 public CatalogPartCollection CatalogParts {
99 if (_catalogParts == null) {
100 CatalogPartCollection catalogParts = CreateCatalogParts();
102 // Verify that each CatalogPart has a nonempty ID. Don't throw an exception in the designer,
103 // since we want only the offending control to render as an error block, not the whole CatalogZone.
105 foreach (CatalogPart catalogPart in catalogParts) {
106 if (String.IsNullOrEmpty(catalogPart.ID)) {
107 throw new InvalidOperationException(SR.GetString(SR.CatalogZoneBase_NoCatalogPartID));
112 _catalogParts = catalogParts;
114 // Call EnsureChildControls to parent the CatalogParts and set the WebPartManager, and Zone
115 EnsureChildControls();
118 return _catalogParts;
124 DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
125 NotifyParentProperty(true),
126 PersistenceMode(PersistenceMode.InnerProperty),
127 WebCategory("Verbs"),
128 WebSysDescription(SR.CatalogZoneBase_CloseVerb),
130 public virtual WebPartVerb CloseVerb {
132 if (_closeVerb == null) {
133 _closeVerb = new WebPartCatalogCloseVerb();
134 _closeVerb.EventArgument = closeEventArgument;
135 if (IsTrackingViewState) {
136 ((IStateManager)_closeVerb).TrackViewState();
145 WebSysDefaultValue(SR.CatalogZoneBase_DefaultEmptyZoneText)
147 public override string EmptyZoneText {
148 // Must look at viewstate directly instead of the property in the base class,
149 // so we can distinguish between an unset property and a property set to String.Empty.
151 string s = (string)ViewState["EmptyZoneText"];
152 return((s == null) ? SR.GetString(SR.CatalogZoneBase_DefaultEmptyZoneText) : s);
155 ViewState["EmptyZoneText"] = value;
160 WebSysDefaultValue(SR.CatalogZoneBase_HeaderText),
162 public override string HeaderText {
164 string s = (string)ViewState["HeaderText"];
165 return((s == null) ? SR.GetString(SR.CatalogZoneBase_HeaderText) : s);
168 ViewState["HeaderText"] = value;
173 WebSysDefaultValue(SR.CatalogZoneBase_InstructionText),
175 public override string InstructionText {
177 string s = (string)ViewState["InstructionText"];
178 return((s == null) ? SR.GetString(SR.CatalogZoneBase_InstructionText) : s);
181 ViewState["InstructionText"] = value;
187 NotifyParentProperty(true),
188 DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
189 PersistenceMode(PersistenceMode.InnerProperty),
190 WebCategory("Styles"),
191 WebSysDescription(SR.CatalogZoneBase_PartLinkStyle),
193 public Style PartLinkStyle {
195 if (_partLinkStyle == null) {
196 _partLinkStyle = new Style();
197 if (IsTrackingViewState) {
198 ((IStateManager)_partLinkStyle).TrackViewState();
202 return _partLinkStyle;
209 WebCategory("Behavior"),
210 WebSysDescription(SR.CatalogZoneBase_SelectedCatalogPartID),
212 public string SelectedCatalogPartID {
214 if (String.IsNullOrEmpty(_selectedCatalogPartID)) {
219 CatalogPartCollection catalogParts = CatalogParts;
220 if (catalogParts != null && catalogParts.Count > 0) {
221 return catalogParts[0].ID;
228 return _selectedCatalogPartID;
232 _selectedCatalogPartID = value;
237 private CatalogPart SelectedCatalogPart {
239 CatalogPartCollection catalogParts = CatalogParts;
240 if (catalogParts != null && catalogParts.Count > 0) {
241 if (String.IsNullOrEmpty(_selectedCatalogPartID)) {
242 return catalogParts[0];
245 return catalogParts[_selectedCatalogPartID];
249 // If there are no catalog parts, return null
257 WebSysDefaultValue(SR.CatalogZoneBase_DefaultSelectTargetZoneText),
258 WebCategory("Behavior"),
259 WebSysDescription(SR.CatalogZoneBase_SelectTargetZoneText),
261 public virtual string SelectTargetZoneText {
263 string s = (string)ViewState["SelectTargetZoneText"];
264 return((s == null) ? SR.GetString(SR.CatalogZoneBase_DefaultSelectTargetZoneText) : s);
267 ViewState["SelectTargetZoneText"] = value;
273 NotifyParentProperty(true),
274 DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
275 PersistenceMode(PersistenceMode.InnerProperty),
276 WebCategory("Styles"),
277 WebSysDescription(SR.CatalogZoneBase_SelectedPartLinkStyle),
279 public Style SelectedPartLinkStyle {
281 if (_selectedPartLinkStyle == null) {
282 _selectedPartLinkStyle = new Style();
283 if (IsTrackingViewState) {
284 ((IStateManager)_selectedPartLinkStyle).TrackViewState();
288 return _selectedPartLinkStyle;
294 WebCategory("Behavior"),
295 WebSysDescription(SR.CatalogZoneBase_ShowCatalogIcons),
297 public virtual bool ShowCatalogIcons {
299 object b = ViewState["ShowCatalogIcons"];
300 return (b != null) ? (bool)b : true;
303 ViewState["ShowCatalogIcons"] = value;
307 private string ZonesID {
309 return UniqueID + ID_SEPARATOR + "_zones";
313 private void AddSelectedWebParts() {
314 WebPartZoneBase selectedZone = null;
315 if (WebPartManager != null) {
316 selectedZone = WebPartManager.Zones[_selectedZoneID];
319 CatalogPart selectedCatalogPart = SelectedCatalogPart;
320 WebPartDescriptionCollection availableWebParts = null;
321 if (selectedCatalogPart != null) {
322 availableWebParts = selectedCatalogPart.GetAvailableWebPartDescriptions();
325 if (selectedZone != null && selectedZone.AllowLayoutChange &&
326 _selectedCheckBoxValues != null && availableWebParts != null) {
327 ArrayList selectedWebParts = new ArrayList();
329 // Fetch all of the WebParts before calling AddWebPart() on any of them.
330 // This is necessary if the CatalogPart would refresh its list of
331 // AvailableWebPartDescriptions in response to adding a WebPart.
332 // PageCatalogPart is an example of this. (VSWhidbey 337539)
333 for (int i = 0; i < _selectedCheckBoxValues.Length; i++) {
334 string value = _selectedCheckBoxValues[i];
335 WebPartDescription webPartDescription = availableWebParts[value];
336 if (webPartDescription != null) {
337 WebPart part = selectedCatalogPart.GetWebPart(webPartDescription);
339 selectedWebParts.Add(part);
344 AddWebParts(selectedWebParts, selectedZone);
348 private void AddWebParts(ArrayList webParts, WebPartZoneBase zone) {
349 // Add web parts from the list in reverse order, so they appear in the zone in the same
350 // order they were returned from the catalog part. (VSWhidbey 77750)
353 foreach (WebPart part in webParts) {
354 WebPartZoneBase targetZone = zone;
355 if (part.AllowZoneChange == false && part.Zone != null) {
356 targetZone = part.Zone;
359 // WebPartManager is checked for null in AddWebParts()
360 Debug.Assert(WebPartManager != null);
361 // Add new parts to the top of the Zone, so the user will see them without scrolling the page
362 WebPartManager.AddWebPart(part, targetZone, 0);
366 protected override void Close() {
367 if (WebPartManager != null) {
368 WebPartManager.DisplayMode = WebPartManager.BrowseDisplayMode;
372 protected virtual CatalogPartChrome CreateCatalogPartChrome() {
373 return new CatalogPartChrome(this);
376 protected abstract CatalogPartCollection CreateCatalogParts();
379 protected internal override void CreateChildControls() {
383 foreach (CatalogPart catalogPart in CatalogParts) {
384 catalogPart.SetWebPartManager(WebPartManager);
385 catalogPart.SetZone(this);
386 Controls.Add(catalogPart);
390 internal string GetCheckBoxID(string value) {
391 return ClientID + ClientIDSeparator + "_checkbox" + ClientIDSeparator + value;
394 // Called by a derived class if the list of CatalogParts changes, and they want CreateCatalogParts()
395 // to be called again.
396 protected void InvalidateCatalogParts() {
397 _catalogParts = null;
398 ChildControlsCreated = false;
402 /// Loads the control state for those properties that should persist across postbacks
403 /// even when EnableViewState=false.
405 protected internal override void LoadControlState(object savedState) {
406 if (savedState == null) {
407 base.LoadControlState(null);
410 object[] myState = (object[])savedState;
411 if (myState.Length != controlStateArrayLength) {
412 throw new ArgumentException(SR.GetString(SR.Invalid_ControlState));
415 base.LoadControlState(myState[baseIndex]);
416 if (myState[selectedCatalogPartIDIndex] != null) {
417 _selectedCatalogPartID = (string)myState[selectedCatalogPartIDIndex];
422 protected virtual bool LoadPostData(string postDataKey, NameValueCollection postCollection) {
424 string selectedCheckBoxValues = postCollection[CheckBoxName];
425 if (!String.IsNullOrEmpty(selectedCheckBoxValues)) {
427 //Validate postback reference if exists in the postdata.
428 ValidateEvent(CheckBoxName);
430 _selectedCheckBoxValues = selectedCheckBoxValues.Split(',');
433 _selectedZoneID = postCollection[ZonesID];
435 // Do not raise a changed event
439 protected override void LoadViewState(object savedState) {
440 if (savedState == null) {
441 base.LoadViewState(null);
444 object[] myState = (object[]) savedState;
445 if (myState.Length != viewStateArrayLength) {
446 throw new ArgumentException(SR.GetString(SR.ViewState_InvalidViewState));
449 base.LoadViewState(myState[baseIndex]);
450 if (myState[addVerbIndex] != null) {
451 ((IStateManager) AddVerb).LoadViewState(myState[addVerbIndex]);
453 if (myState[closeVerbIndex] != null) {
454 ((IStateManager) CloseVerb).LoadViewState(myState[closeVerbIndex]);
456 if (myState[partLinkStyleIndex] != null) {
457 ((IStateManager) PartLinkStyle).LoadViewState(myState[partLinkStyleIndex]);
459 if (myState[selectedPartLinkStyleIndex] != null) {
460 ((IStateManager) SelectedPartLinkStyle).LoadViewState(myState[selectedPartLinkStyleIndex]);
465 protected internal override void OnInit(EventArgs e) {
469 Debug.Assert(page != null);
471 page.RegisterRequiresControlState(this);
475 // We don't need to handle WebPartManager.DisplayModeChanged in this class.
476 // We need it in EditorZoneBase since the available editor parts changes when the
477 // WebPartToEdit changes, but the list of catalog parts never changes
478 // when the DisplayMode changes.
480 protected internal override void OnPreRender(EventArgs e) {
482 CatalogPartChrome.PerformPreRender();
483 Page.RegisterRequiresPostBack(this);
486 protected override void RaisePostBackEvent(string eventArgument) {
487 string[] eventArguments = eventArgument.Split(ID_SEPARATOR);
489 if ((eventArguments.Length == 2) && (eventArguments[0] == selectEventArgument)) {
490 SelectedCatalogPartID = eventArguments[1];
492 else if (String.Equals(eventArgument, addEventArgument, StringComparison.OrdinalIgnoreCase)) {
493 if (AddVerb.Visible && AddVerb.Enabled) {
494 AddSelectedWebParts();
497 else if (String.Equals(eventArgument, closeEventArgument, StringComparison.OrdinalIgnoreCase)) {
498 if (CloseVerb.Visible && CloseVerb.Enabled) {
503 base.RaisePostBackEvent(eventArgument);
507 protected internal override void Render(HtmlTextWriter writer) {
509 Page.VerifyRenderingInServerForm(this);
515 protected override void RenderBody(HtmlTextWriter writer) {
516 RenderBodyTableBeginTag(writer);
518 RenderDesignerRegionBeginTag(writer, Orientation.Vertical);
521 CatalogPartCollection catalogParts = CatalogParts;
522 if (catalogParts != null && catalogParts.Count > 0) {
523 bool firstCell = true;
524 // Only render links if there is more than 1 catalog part (VSWhidbey 77672)
525 if (catalogParts.Count > 1) {
526 writer.RenderBeginTag(HtmlTextWriterTag.Tr);
527 writer.RenderBeginTag(HtmlTextWriterTag.Td);
529 RenderCatalogPartLinks(writer);
530 writer.RenderEndTag(); // Td
531 writer.RenderEndTag(); // Tr
534 CatalogPartChrome chrome = CatalogPartChrome;
536 foreach (CatalogPart catalogPart in catalogParts) {
537 RenderCatalogPart(writer, catalogPart, chrome, ref firstCell);
541 CatalogPart selectedCatalogPart = SelectedCatalogPart;
542 if (selectedCatalogPart != null) {
543 RenderCatalogPart(writer, selectedCatalogPart, chrome, ref firstCell);
547 writer.RenderBeginTag(HtmlTextWriterTag.Tr);
549 // Mozilla renders padding on an empty TD without this attribute
550 writer.AddStyleAttribute(HtmlTextWriterStyle.Padding, "0");
552 // Add an extra row with height of 100%, to Microsoft up any extra space
553 // if the height of the zone is larger than its contents
554 // Mac IE needs height=100% set on <td> instead of <tr>
555 writer.AddStyleAttribute(HtmlTextWriterStyle.Height, "100%");
557 writer.RenderBeginTag(HtmlTextWriterTag.Td);
558 writer.RenderEndTag(); // Td
559 writer.RenderEndTag(); // Tr
562 RenderEmptyZoneText(writer);
566 RenderDesignerRegionEndTag(writer);
568 RenderBodyTableEndTag(writer);
571 private void RenderCatalogPart(HtmlTextWriter writer, CatalogPart catalogPart, CatalogPartChrome chrome, ref bool firstCell) {
572 writer.RenderBeginTag(HtmlTextWriterTag.Tr);
575 writer.AddStyleAttribute(HtmlTextWriterStyle.PaddingTop, "0");
577 writer.RenderBeginTag(HtmlTextWriterTag.Td);
580 chrome.RenderCatalogPart(writer, catalogPart);
582 writer.RenderEndTag(); // Td
583 writer.RenderEndTag(); // Tr
586 protected virtual void RenderCatalogPartLinks(HtmlTextWriter writer) {
587 RenderInstructionText(writer);
589 CatalogPart selectedCatalogPart = SelectedCatalogPart;
590 foreach (CatalogPart catalogPart in CatalogParts) {
591 WebPartDescriptionCollection availableWebParts = catalogPart.GetAvailableWebPartDescriptions();
592 int count = ((availableWebParts != null) ? availableWebParts.Count : 0);
594 string displayTitle = catalogPart.DisplayTitle;
596 string text = displayTitle + " (" + count.ToString(CultureInfo.CurrentCulture) + ")";
598 if (catalogPart == selectedCatalogPart) {
599 Label label = new Label();
602 label.ApplyStyle(SelectedPartLinkStyle);
603 label.RenderControl(writer);
606 Debug.Assert(!String.IsNullOrEmpty(catalogPart.ID));
607 string eventArgument = selectEventArgument + ID_SEPARATOR + catalogPart.ID;
609 ZoneLinkButton linkButton = new ZoneLinkButton(this, eventArgument);
610 linkButton.Text = text;
611 linkButton.ToolTip = SR.GetString(SR.CatalogZoneBase_SelectCatalogPart, displayTitle);
612 linkButton.Page = Page;
613 linkButton.ApplyStyle(PartLinkStyle);
614 linkButton.RenderControl(writer);
623 private void RenderEmptyZoneText(HtmlTextWriter writer) {
624 string emptyZoneText = EmptyZoneText;
625 if (!String.IsNullOrEmpty(emptyZoneText)) {
626 writer.RenderBeginTag(HtmlTextWriterTag.Tr);
628 writer.AddAttribute(HtmlTextWriterAttribute.Valign, "top");
630 Style emptyZoneTextStyle = EmptyZoneTextStyle;
631 if (!emptyZoneTextStyle.IsEmpty) {
632 emptyZoneTextStyle.AddAttributesToRender(writer, this);
634 writer.RenderBeginTag(HtmlTextWriterTag.Td);
636 writer.Write(emptyZoneText);
638 writer.RenderEndTag(); // Td
639 writer.RenderEndTag(); // Tr
643 protected override void RenderFooter(HtmlTextWriter writer) {
644 writer.AddStyleAttribute(HtmlTextWriterStyle.Margin, "4px");
645 writer.RenderBeginTag(HtmlTextWriterTag.Div);
647 DropDownList zonesDropDownList = new DropDownList();
648 zonesDropDownList.ClientIDMode = ClientIDMode.AutoID;
649 zonesDropDownList.ID = ZonesID;
651 // Populate the DropDownList
653 // Add sample zone to dropdown
654 zonesDropDownList.Items.Add(SR.GetString(SR.Zone_SampleHeaderText));
657 if (WebPartManager != null && WebPartManager.Zones != null) {
658 foreach (WebPartZoneBase zone in WebPartManager.Zones) {
659 if (zone.AllowLayoutChange) {
660 Debug.Assert(!String.IsNullOrEmpty(zone.ID));
661 ListItem item = new ListItem(zone.DisplayTitle, zone.ID);
662 if (String.Equals(zone.ID, _selectedZoneID, StringComparison.OrdinalIgnoreCase)) {
663 item.Selected = true;
665 zonesDropDownList.Items.Add(item);
671 LabelStyle.AddAttributesToRender(writer, this);
672 // Only render the "for" attribute if we are going to render the associated DropDownList (VSWhidbey 541458)
673 if (zonesDropDownList.Items.Count > 0) {
674 writer.AddAttribute(HtmlTextWriterAttribute.For, zonesDropDownList.ClientID);
676 writer.RenderBeginTag(HtmlTextWriterTag.Label);
677 writer.Write(SelectTargetZoneText);
678 writer.RenderEndTag();
680 // Render before the DropDownList (VSWhidbey 77709)
681 writer.Write(" ");
683 zonesDropDownList.ApplyStyle(EditUIStyle);
684 // Do not render empty DropDownList (VSWhidbey 534498)
685 if (zonesDropDownList.Items.Count > 0) {
686 zonesDropDownList.RenderControl(writer);
689 writer.Write(" ");
693 writer.RenderEndTag(); // Div
696 private void RenderInstructionText(HtmlTextWriter writer) {
697 string instructionText = InstructionText;
698 if (!String.IsNullOrEmpty(instructionText)) {
699 Label label = new Label();
700 label.Text = instructionText;
702 label.ApplyStyle(InstructionTextStyle);
703 label.RenderControl(writer);
709 protected override void RenderVerbs(HtmlTextWriter writer) {
711 bool originalAddVerbEnabled = false;
713 CatalogPart selectedCatalogPart = SelectedCatalogPart;
714 if (selectedCatalogPart != null) {
715 WebPartDescriptionCollection availableWebParts = selectedCatalogPart.GetAvailableWebPartDescriptions();
716 count = ((availableWebParts != null) ? availableWebParts.Count : 0);
719 // If the current CatalogPart has no WebPartDescriptions, disable the AddVerb
721 originalAddVerbEnabled = AddVerb.Enabled;
722 AddVerb.Enabled = false;
726 RenderVerbsInternal(writer, new WebPartVerb[] {AddVerb, CloseVerb});
730 AddVerb.Enabled = originalAddVerbEnabled;
736 /// Saves the control state for those properties that should persist across postbacks
737 /// even when EnableViewState=false.
739 protected internal override object SaveControlState() {
740 object[] myState = new object[controlStateArrayLength];
742 myState[baseIndex] = base.SaveControlState();
743 if (!String.IsNullOrEmpty(_selectedCatalogPartID)) {
744 myState[selectedCatalogPartIDIndex] = _selectedCatalogPartID;
747 for (int i=0; i < controlStateArrayLength; i++) {
748 if (myState[i] != null) {
753 // More performant to return null than an array of null values
757 protected override object SaveViewState() {
758 object[] myState = new object[viewStateArrayLength];
760 myState[baseIndex] = base.SaveViewState();
761 myState[addVerbIndex] = (_addVerb != null) ? ((IStateManager)_addVerb).SaveViewState() : null;
762 myState[closeVerbIndex] = (_closeVerb != null) ? ((IStateManager)_closeVerb).SaveViewState() : null;
763 myState[partLinkStyleIndex] = (_partLinkStyle != null) ? ((IStateManager)_partLinkStyle).SaveViewState() : null;
764 myState[selectedPartLinkStyleIndex] = (_selectedPartLinkStyle != null) ? ((IStateManager)_selectedPartLinkStyle).SaveViewState() : null;
766 for (int i=0; i < viewStateArrayLength; i++) {
767 if (myState[i] != null) {
772 // More performant to return null than an array of null values
776 protected override void TrackViewState() {
777 base.TrackViewState();
779 if (_addVerb != null) {
780 ((IStateManager) _addVerb).TrackViewState();
782 if (_closeVerb != null) {
783 ((IStateManager) _closeVerb).TrackViewState();
785 if (_partLinkStyle != null) {
786 ((IStateManager) _partLinkStyle).TrackViewState();
788 if (_selectedPartLinkStyle != null) {
789 ((IStateManager) _selectedPartLinkStyle).TrackViewState();
793 #region Implementation of IPostBackDataHandler
794 bool IPostBackDataHandler.LoadPostData(string postDataKey, NameValueCollection postCollection) {
795 return LoadPostData(postDataKey, postCollection);
798 void IPostBackDataHandler.RaisePostDataChangedEvent() {