Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.Web / UI / WebParts / CatalogZoneBase.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="CatalogZoneBase.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //------------------------------------------------------------------------------
6
7 namespace System.Web.UI.WebControls.WebParts {
8
9     using System;
10     using System.Collections;
11     using System.Collections.Specialized;
12     using System.ComponentModel;
13     using System.Drawing;
14     using System.Globalization;
15     using System.Web;
16     using System.Web.UI;
17     using System.Web.UI.WebControls;
18     using System.Web.Util;
19
20     public abstract class CatalogZoneBase : ToolZone, IPostBackDataHandler {
21
22         private CatalogPartCollection _catalogParts;
23
24         private string[] _selectedCheckBoxValues;
25         private string _selectedZoneID;
26         private string _selectedCatalogPartID;
27
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;
34
35         // Use same baseIndex as above
36         private const int selectedCatalogPartIDIndex = 1;
37         private const int controlStateArrayLength = 2;
38
39         private WebPartVerb _addVerb;
40         private WebPartVerb _closeVerb;
41         private Style _partLinkStyle;
42         private Style _selectedPartLinkStyle;
43         private CatalogPartChrome _catalogPartChrome;
44
45         private const string addEventArgument = "add";
46         private const string closeEventArgument = "close";
47         private const string selectEventArgument = "select";
48
49         protected CatalogZoneBase() : base(WebPartManager.CatalogDisplayMode) {
50         }
51
52         [
53         DefaultValue(null),
54         DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
55         NotifyParentProperty(true),
56         PersistenceMode(PersistenceMode.InnerProperty),
57         WebCategory("Verbs"),
58         WebSysDescription(SR.CatalogZoneBase_AddVerb),
59         ]
60         public virtual WebPartVerb AddVerb {
61             get {
62                 if (_addVerb == null) {
63                     _addVerb = new WebPartCatalogAddVerb();
64                     _addVerb.EventArgument = addEventArgument;
65                     if (IsTrackingViewState) {
66                         ((IStateManager)_addVerb).TrackViewState();
67                     }
68                 }
69
70                 return _addVerb;
71             }
72         }
73
74         internal string CheckBoxName {
75             get {
76                 return UniqueID + ID_SEPARATOR + "_checkbox";
77             }
78         }
79
80         [
81         Browsable(false),
82         DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
83         ]
84         public CatalogPartChrome CatalogPartChrome {
85             get {
86                 if (_catalogPartChrome == null) {
87                     _catalogPartChrome = CreateCatalogPartChrome();
88                 }
89                 return _catalogPartChrome;
90             }
91         }
92
93         [
94         Browsable(false),
95         DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
96         ]
97         public CatalogPartCollection CatalogParts {
98             get {
99                 if (_catalogParts == null) {
100                     CatalogPartCollection catalogParts = CreateCatalogParts();
101
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.
104                     if (!DesignMode) {
105                         foreach (CatalogPart catalogPart in catalogParts) {
106                             if (String.IsNullOrEmpty(catalogPart.ID)) {
107                                 throw new InvalidOperationException(SR.GetString(SR.CatalogZoneBase_NoCatalogPartID));
108                             }
109                         }
110                     }
111
112                     _catalogParts = catalogParts;
113
114                     // Call EnsureChildControls to parent the CatalogParts and set the WebPartManager, and Zone
115                     EnsureChildControls();
116                 }
117
118                 return _catalogParts;
119             }
120         }
121
122         [
123         DefaultValue(null),
124         DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
125         NotifyParentProperty(true),
126         PersistenceMode(PersistenceMode.InnerProperty),
127         WebCategory("Verbs"),
128         WebSysDescription(SR.CatalogZoneBase_CloseVerb),
129         ]
130         public virtual WebPartVerb CloseVerb {
131             get {
132                 if (_closeVerb == null) {
133                     _closeVerb = new WebPartCatalogCloseVerb();
134                     _closeVerb.EventArgument = closeEventArgument;
135                     if (IsTrackingViewState) {
136                         ((IStateManager)_closeVerb).TrackViewState();
137                     }
138                 }
139
140                 return _closeVerb;
141             }
142         }
143
144         [
145         WebSysDefaultValue(SR.CatalogZoneBase_DefaultEmptyZoneText)
146         ]
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.
150             get {
151                 string s = (string)ViewState["EmptyZoneText"];
152                 return((s == null) ? SR.GetString(SR.CatalogZoneBase_DefaultEmptyZoneText) : s);
153             }
154             set {
155                 ViewState["EmptyZoneText"] = value;
156             }
157         }
158
159         [
160         WebSysDefaultValue(SR.CatalogZoneBase_HeaderText),
161         ]
162         public override string HeaderText {
163             get {
164                 string s = (string)ViewState["HeaderText"];
165                 return((s == null) ? SR.GetString(SR.CatalogZoneBase_HeaderText) : s);
166             }
167             set {
168                 ViewState["HeaderText"] = value;
169             }
170         }
171
172         [
173         WebSysDefaultValue(SR.CatalogZoneBase_InstructionText),
174         ]
175         public override string InstructionText {
176             get {
177                 string s = (string)ViewState["InstructionText"];
178                 return((s == null) ? SR.GetString(SR.CatalogZoneBase_InstructionText) : s);
179             }
180             set {
181                 ViewState["InstructionText"] = value;
182             }
183         }
184
185         [
186         DefaultValue(null),
187         NotifyParentProperty(true),
188         DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
189         PersistenceMode(PersistenceMode.InnerProperty),
190         WebCategory("Styles"),
191         WebSysDescription(SR.CatalogZoneBase_PartLinkStyle),
192         ]
193         public Style PartLinkStyle {
194             get {
195                 if (_partLinkStyle == null) {
196                     _partLinkStyle = new Style();
197                     if (IsTrackingViewState) {
198                         ((IStateManager)_partLinkStyle).TrackViewState();
199                     }
200                 }
201
202                 return _partLinkStyle;
203             }
204         }
205
206         [
207         DefaultValue(""),
208         Themeable(false),
209         WebCategory("Behavior"),
210         WebSysDescription(SR.CatalogZoneBase_SelectedCatalogPartID),
211         ]
212         public string SelectedCatalogPartID {
213             get {
214                 if (String.IsNullOrEmpty(_selectedCatalogPartID)) {
215                     if (DesignMode) {
216                         return String.Empty;
217                     }
218                     else {
219                         CatalogPartCollection catalogParts = CatalogParts;
220                         if (catalogParts != null && catalogParts.Count > 0) {
221                             return catalogParts[0].ID;
222                         } else {
223                             return String.Empty;
224                         }
225                     }
226                 }
227                 else {
228                     return _selectedCatalogPartID;
229                 }
230             }
231             set {
232                 _selectedCatalogPartID = value;
233             }
234         }
235
236         // 
237         private CatalogPart SelectedCatalogPart {
238             get {
239                 CatalogPartCollection catalogParts = CatalogParts;
240                 if (catalogParts != null && catalogParts.Count > 0) {
241                     if (String.IsNullOrEmpty(_selectedCatalogPartID)) {
242                         return catalogParts[0];
243                     }
244                     else {
245                         return catalogParts[_selectedCatalogPartID];
246                     }
247                 }
248                 else {
249                     // If there are no catalog parts, return null
250                     return null;
251                 }
252             }
253         }
254
255         [
256         Localizable(true),
257         WebSysDefaultValue(SR.CatalogZoneBase_DefaultSelectTargetZoneText),
258         WebCategory("Behavior"),
259         WebSysDescription(SR.CatalogZoneBase_SelectTargetZoneText),
260         ]
261         public virtual string SelectTargetZoneText {
262             get {
263                 string s = (string)ViewState["SelectTargetZoneText"];
264                 return((s == null) ? SR.GetString(SR.CatalogZoneBase_DefaultSelectTargetZoneText) : s);
265             }
266             set {
267                 ViewState["SelectTargetZoneText"] = value;
268             }
269         }
270
271         [
272         DefaultValue(null),
273         NotifyParentProperty(true),
274         DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
275         PersistenceMode(PersistenceMode.InnerProperty),
276         WebCategory("Styles"),
277         WebSysDescription(SR.CatalogZoneBase_SelectedPartLinkStyle),
278         ]
279         public Style SelectedPartLinkStyle {
280             get {
281                 if (_selectedPartLinkStyle == null) {
282                     _selectedPartLinkStyle = new Style();
283                     if (IsTrackingViewState) {
284                         ((IStateManager)_selectedPartLinkStyle).TrackViewState();
285                     }
286                 }
287
288                 return _selectedPartLinkStyle;
289             }
290         }
291
292         [
293         DefaultValue(true),
294         WebCategory("Behavior"),
295         WebSysDescription(SR.CatalogZoneBase_ShowCatalogIcons),
296         ]
297         public virtual bool ShowCatalogIcons {
298             get {
299                 object b = ViewState["ShowCatalogIcons"];
300                 return (b != null) ? (bool)b : true;
301             }
302             set {
303                 ViewState["ShowCatalogIcons"] = value;
304             }
305         }
306
307         private string ZonesID {
308             get {
309                 return UniqueID + ID_SEPARATOR + "_zones";
310             }
311         }
312
313         private void AddSelectedWebParts() {
314             WebPartZoneBase selectedZone = null;
315             if (WebPartManager != null) {
316                 selectedZone = WebPartManager.Zones[_selectedZoneID];
317             }
318
319             CatalogPart selectedCatalogPart = SelectedCatalogPart;
320             WebPartDescriptionCollection availableWebParts = null;
321             if (selectedCatalogPart != null) {
322                 availableWebParts = selectedCatalogPart.GetAvailableWebPartDescriptions();
323             }
324
325             if (selectedZone != null && selectedZone.AllowLayoutChange &&
326                 _selectedCheckBoxValues != null && availableWebParts != null) {
327                 ArrayList selectedWebParts = new ArrayList();
328
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);
338                         if (part != null) {
339                             selectedWebParts.Add(part);
340                         }
341                     }
342                 }
343
344                 AddWebParts(selectedWebParts, selectedZone);
345             }
346         }
347
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)
351             webParts.Reverse();
352
353             foreach (WebPart part in webParts) {
354                 WebPartZoneBase targetZone = zone;
355                 if (part.AllowZoneChange == false && part.Zone != null) {
356                     targetZone = part.Zone;
357                 }
358
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);
363             }
364         }
365
366         protected override void Close() {
367             if (WebPartManager != null) {
368                 WebPartManager.DisplayMode = WebPartManager.BrowseDisplayMode;
369             }
370         }
371
372         protected virtual CatalogPartChrome CreateCatalogPartChrome() {
373             return new CatalogPartChrome(this);
374         }
375
376         protected abstract CatalogPartCollection CreateCatalogParts();
377
378         /// <internalonly/>
379         protected internal override void CreateChildControls() {
380             Controls.Clear();
381
382             // 
383             foreach (CatalogPart catalogPart in CatalogParts) {
384                 catalogPart.SetWebPartManager(WebPartManager);
385                 catalogPart.SetZone(this);
386                 Controls.Add(catalogPart);
387             }
388         }
389
390         internal string GetCheckBoxID(string value) {
391             return ClientID + ClientIDSeparator + "_checkbox" + ClientIDSeparator + value;
392         }
393
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;
399         }
400
401         /// <devdoc>
402         /// Loads the control state for those properties that should persist across postbacks
403         /// even when EnableViewState=false.
404         /// </devdoc>
405         protected internal override void LoadControlState(object savedState) {
406             if (savedState == null) {
407                 base.LoadControlState(null);
408             }
409             else {
410                 object[] myState = (object[])savedState;
411                 if (myState.Length != controlStateArrayLength) {
412                     throw new ArgumentException(SR.GetString(SR.Invalid_ControlState));
413                 }
414
415                 base.LoadControlState(myState[baseIndex]);
416                 if (myState[selectedCatalogPartIDIndex] != null) {
417                     _selectedCatalogPartID = (string)myState[selectedCatalogPartIDIndex];
418                 }
419             }
420         }
421
422         protected virtual bool LoadPostData(string postDataKey, NameValueCollection postCollection) {
423
424             string selectedCheckBoxValues = postCollection[CheckBoxName];
425             if (!String.IsNullOrEmpty(selectedCheckBoxValues)) {
426
427                 //Validate postback reference if exists in the postdata.
428                 ValidateEvent(CheckBoxName);
429
430                 _selectedCheckBoxValues = selectedCheckBoxValues.Split(',');
431             }
432
433             _selectedZoneID = postCollection[ZonesID];
434
435             // Do not raise a changed event
436             return false;
437         }
438
439         protected override void LoadViewState(object savedState) {
440             if (savedState == null) {
441                 base.LoadViewState(null);
442             }
443             else {
444                 object[] myState = (object[]) savedState;
445                 if (myState.Length != viewStateArrayLength) {
446                     throw new ArgumentException(SR.GetString(SR.ViewState_InvalidViewState));
447                 }
448
449                 base.LoadViewState(myState[baseIndex]);
450                 if (myState[addVerbIndex] != null) {
451                     ((IStateManager) AddVerb).LoadViewState(myState[addVerbIndex]);
452                 }
453                 if (myState[closeVerbIndex] != null) {
454                     ((IStateManager) CloseVerb).LoadViewState(myState[closeVerbIndex]);
455                 }
456                 if (myState[partLinkStyleIndex] != null) {
457                     ((IStateManager) PartLinkStyle).LoadViewState(myState[partLinkStyleIndex]);
458                 }
459                 if (myState[selectedPartLinkStyleIndex] != null) {
460                     ((IStateManager) SelectedPartLinkStyle).LoadViewState(myState[selectedPartLinkStyleIndex]);
461                 }
462             }
463         }
464
465         protected internal override void OnInit(EventArgs e) {
466             base.OnInit(e);
467
468             Page page = Page;
469             Debug.Assert(page != null);
470             if (page != null) {
471                 page.RegisterRequiresControlState(this);
472             }
473         }
474
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.
479
480         protected internal override void OnPreRender(EventArgs e) {
481             base.OnPreRender(e);
482             CatalogPartChrome.PerformPreRender();
483             Page.RegisterRequiresPostBack(this);
484         }
485
486         protected override void RaisePostBackEvent(string eventArgument) {
487             string[] eventArguments = eventArgument.Split(ID_SEPARATOR);
488
489             if ((eventArguments.Length == 2) && (eventArguments[0] == selectEventArgument)) {
490                 SelectedCatalogPartID = eventArguments[1];
491             }
492             else if (String.Equals(eventArgument, addEventArgument, StringComparison.OrdinalIgnoreCase)) {
493                 if (AddVerb.Visible && AddVerb.Enabled) {
494                     AddSelectedWebParts();
495                 }
496             }
497             else if (String.Equals(eventArgument, closeEventArgument, StringComparison.OrdinalIgnoreCase)) {
498                 if (CloseVerb.Visible && CloseVerb.Enabled) {
499                     Close();
500                 }
501             }
502             else {
503                 base.RaisePostBackEvent(eventArgument);
504             }
505         }
506
507         protected internal override void Render(HtmlTextWriter writer) {
508             if (Page != null) {
509                 Page.VerifyRenderingInServerForm(this);
510             }
511
512             base.Render(writer);
513         }
514
515         protected override void RenderBody(HtmlTextWriter writer) {
516             RenderBodyTableBeginTag(writer);
517             if (DesignMode) {
518                 RenderDesignerRegionBeginTag(writer, Orientation.Vertical);
519             }
520
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);
528                     firstCell = false;
529                     RenderCatalogPartLinks(writer);
530                     writer.RenderEndTag();  // Td
531                     writer.RenderEndTag();  // Tr
532                 }
533
534                 CatalogPartChrome chrome = CatalogPartChrome;
535                 if (DesignMode) {
536                     foreach (CatalogPart catalogPart in catalogParts) {
537                         RenderCatalogPart(writer, catalogPart, chrome, ref firstCell);
538                     }
539                 }
540                 else {
541                     CatalogPart selectedCatalogPart = SelectedCatalogPart;
542                     if (selectedCatalogPart != null) {
543                         RenderCatalogPart(writer, selectedCatalogPart, chrome, ref firstCell);
544                     }
545                 }
546
547                 writer.RenderBeginTag(HtmlTextWriterTag.Tr);
548
549                 // Mozilla renders padding on an empty TD without this attribute
550                 writer.AddStyleAttribute(HtmlTextWriterStyle.Padding, "0");
551
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%");
556
557                 writer.RenderBeginTag(HtmlTextWriterTag.Td);
558                 writer.RenderEndTag(); // Td
559                 writer.RenderEndTag(); // Tr
560             }
561             else {
562                 RenderEmptyZoneText(writer);
563             }
564
565             if (DesignMode) {
566                 RenderDesignerRegionEndTag(writer);
567             }
568             RenderBodyTableEndTag(writer);
569         }
570
571         private void RenderCatalogPart(HtmlTextWriter writer, CatalogPart catalogPart, CatalogPartChrome chrome, ref bool firstCell) {
572             writer.RenderBeginTag(HtmlTextWriterTag.Tr);
573
574             if (!firstCell) {
575                 writer.AddStyleAttribute(HtmlTextWriterStyle.PaddingTop, "0");
576             }
577             writer.RenderBeginTag(HtmlTextWriterTag.Td);
578             firstCell = false;
579
580             chrome.RenderCatalogPart(writer, catalogPart);
581
582             writer.RenderEndTag();  // Td
583             writer.RenderEndTag();  // Tr
584         }
585
586         protected virtual void RenderCatalogPartLinks(HtmlTextWriter writer) {
587             RenderInstructionText(writer);
588
589             CatalogPart selectedCatalogPart = SelectedCatalogPart;
590             foreach (CatalogPart catalogPart in CatalogParts) {
591                 WebPartDescriptionCollection availableWebParts = catalogPart.GetAvailableWebPartDescriptions();
592                 int count = ((availableWebParts != null) ? availableWebParts.Count : 0);
593
594                 string displayTitle = catalogPart.DisplayTitle;
595                 // 
596                 string text = displayTitle + " (" + count.ToString(CultureInfo.CurrentCulture) + ")";
597
598                 if (catalogPart == selectedCatalogPart) {
599                     Label label = new Label();
600                     label.Text = text;
601                     label.Page = Page;
602                     label.ApplyStyle(SelectedPartLinkStyle);
603                     label.RenderControl(writer);
604                 }
605                 else {
606                     Debug.Assert(!String.IsNullOrEmpty(catalogPart.ID));
607                     string eventArgument = selectEventArgument + ID_SEPARATOR + catalogPart.ID;
608
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);
615                 }
616
617                 writer.WriteBreak();
618             }
619
620             writer.WriteBreak();
621         }
622
623         private void RenderEmptyZoneText(HtmlTextWriter writer) {
624             string emptyZoneText = EmptyZoneText;
625             if (!String.IsNullOrEmpty(emptyZoneText)) {
626                 writer.RenderBeginTag(HtmlTextWriterTag.Tr);
627
628                 writer.AddAttribute(HtmlTextWriterAttribute.Valign, "top");
629
630                 Style emptyZoneTextStyle = EmptyZoneTextStyle;
631                 if (!emptyZoneTextStyle.IsEmpty) {
632                     emptyZoneTextStyle.AddAttributesToRender(writer, this);
633                 }
634                 writer.RenderBeginTag(HtmlTextWriterTag.Td);
635
636                 writer.Write(emptyZoneText);
637
638                 writer.RenderEndTag();  // Td
639                 writer.RenderEndTag();  // Tr
640             }
641         }
642
643         protected override void RenderFooter(HtmlTextWriter writer) {
644             writer.AddStyleAttribute(HtmlTextWriterStyle.Margin, "4px");
645             writer.RenderBeginTag(HtmlTextWriterTag.Div);
646
647             DropDownList zonesDropDownList = new DropDownList();
648             zonesDropDownList.ClientIDMode = ClientIDMode.AutoID;
649             zonesDropDownList.ID = ZonesID;
650
651             // Populate the DropDownList
652             if (DesignMode) {
653                 // Add sample zone to dropdown
654                 zonesDropDownList.Items.Add(SR.GetString(SR.Zone_SampleHeaderText));
655             }
656             else {
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;
664                             }
665                             zonesDropDownList.Items.Add(item);
666                         }
667                     }
668                 }
669             }
670
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);
675             }
676             writer.RenderBeginTag(HtmlTextWriterTag.Label);
677             writer.Write(SelectTargetZoneText);
678             writer.RenderEndTag();
679
680             // Render &nbsp; before the DropDownList (VSWhidbey 77709)
681             writer.Write("&nbsp;");
682
683             zonesDropDownList.ApplyStyle(EditUIStyle);
684             // Do not render empty DropDownList (VSWhidbey 534498)
685             if (zonesDropDownList.Items.Count > 0) {
686                 zonesDropDownList.RenderControl(writer);
687             }
688
689             writer.Write("&nbsp;");
690
691             RenderVerbs(writer);
692
693             writer.RenderEndTag();  // Div
694         }
695
696         private void RenderInstructionText(HtmlTextWriter writer) {
697             string instructionText = InstructionText;
698             if (!String.IsNullOrEmpty(instructionText)) {
699                 Label label = new Label();
700                 label.Text = instructionText;
701                 label.Page = Page;
702                 label.ApplyStyle(InstructionTextStyle);
703                 label.RenderControl(writer);
704                 writer.WriteBreak();
705                 writer.WriteBreak();
706             }
707         }
708
709         protected override void RenderVerbs(HtmlTextWriter writer) {
710             int count = 0;
711             bool originalAddVerbEnabled = false;
712
713             CatalogPart selectedCatalogPart = SelectedCatalogPart;
714             if (selectedCatalogPart != null) {
715                 WebPartDescriptionCollection availableWebParts = selectedCatalogPart.GetAvailableWebPartDescriptions();
716                 count = ((availableWebParts != null) ? availableWebParts.Count : 0);
717             }
718
719             // If the current CatalogPart has no WebPartDescriptions, disable the AddVerb
720             if (count == 0) {
721                 originalAddVerbEnabled = AddVerb.Enabled;
722                 AddVerb.Enabled = false;
723             }
724
725             try {
726                 RenderVerbsInternal(writer, new WebPartVerb[] {AddVerb, CloseVerb});
727             }
728             finally {
729                 if (count == 0) {
730                     AddVerb.Enabled = originalAddVerbEnabled;
731                 }
732             }
733         }
734
735         /// <devdoc>
736         /// Saves the control state for those properties that should persist across postbacks
737         /// even when EnableViewState=false.
738         /// </devdoc>
739         protected internal override object SaveControlState() {
740             object[] myState = new object[controlStateArrayLength];
741
742             myState[baseIndex] = base.SaveControlState();
743             if (!String.IsNullOrEmpty(_selectedCatalogPartID)) {
744                 myState[selectedCatalogPartIDIndex] = _selectedCatalogPartID;
745             }
746
747             for (int i=0; i < controlStateArrayLength; i++) {
748                 if (myState[i] != null) {
749                     return myState;
750                 }
751             }
752
753             // More performant to return null than an array of null values
754             return null;
755         }
756
757         protected override object SaveViewState() {
758             object[] myState = new object[viewStateArrayLength];
759
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;
765
766             for (int i=0; i < viewStateArrayLength; i++) {
767                 if (myState[i] != null) {
768                     return myState;
769                 }
770             }
771
772             // More performant to return null than an array of null values
773             return null;
774         }
775
776         protected override void TrackViewState() {
777             base.TrackViewState();
778
779             if (_addVerb != null) {
780                 ((IStateManager) _addVerb).TrackViewState();
781             }
782             if (_closeVerb != null) {
783                 ((IStateManager) _closeVerb).TrackViewState();
784             }
785             if (_partLinkStyle != null) {
786                 ((IStateManager) _partLinkStyle).TrackViewState();
787             }
788             if (_selectedPartLinkStyle != null) {
789                 ((IStateManager) _selectedPartLinkStyle).TrackViewState();
790             }
791         }
792
793         #region Implementation of IPostBackDataHandler
794         bool IPostBackDataHandler.LoadPostData(string postDataKey, NameValueCollection postCollection) {
795             return LoadPostData(postDataKey, postCollection);
796         }
797
798         void IPostBackDataHandler.RaisePostDataChangedEvent() {
799         }
800         #endregion
801     }
802 }
803