1 //------------------------------------------------------------------------------
2 // <copyright file="Menu.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //------------------------------------------------------------------------------
6 
7 namespace System.Web.UI.WebControls {
8     using System.Collections;
9     using System.Collections.Generic;
10     using System.Collections.ObjectModel;
11     using System.ComponentModel;
12     using System.Drawing.Design;
13     using System.Globalization;
14     using System.IO;
15     using System.Reflection;
16     using System.Security.Permissions;
17     using System.Text;
18     using System.Web.UI.WebControls.Adapters;
19     using System.Web.Util;
20 
21     /// <devdoc>
22     ///     Provides a cascading pop-out hierarchical menu control
23     /// </devdoc>
24     [ControlValueProperty("SelectedValue")]
25     [DefaultEvent("MenuItemClick")]
26     [Designer("System.Web.UI.Design.WebControls.MenuDesigner, " + AssemblyRef.SystemDesign)]
27     [SupportsEventValidation]
28     public partial class Menu : HierarchicalDataBoundControl, IPostBackEventHandler, INamingContainer {
29 
30         internal const int ScrollUpImageIndex = 0;
31         internal const int ScrollDownImageIndex = 1;
32         internal const int PopOutImageIndex = 2;
33 
34         internal const int ImageUrlsCount = 3;
35 
36         private const string _getDesignTimeStaticHtml = "GetDesignTimeStaticHtml";
37         private const string _getDesignTimeDynamicHtml = "GetDesignTimeDynamicHtml";
38 
39         // static readonly instead of const to be able to change it in the future without breaking existing client code
40         public static readonly string MenuItemClickCommandName = "Click";
41 
42         private static readonly object _menuItemClickedEvent = new object();
43         private static readonly object _menuItemDataBoundEvent = new object();
44 
45         private MenuRenderingMode _renderingMode = MenuRenderingMode.Default;
46 
47         private string[] _imageUrls;
48 
49         private SubMenuStyle _staticMenuStyle;
50         private SubMenuStyle _dynamicMenuStyle;
51 
52         private MenuItemStyle _staticItemStyle;
53         private MenuItemStyle _staticSelectedStyle;
54         private Style _staticHoverStyle;
55         private HyperLinkStyle _staticHoverHyperLinkStyle;
56 
57         private MenuItemStyle _dynamicItemStyle;
58         private MenuItemStyle _dynamicSelectedStyle;
59         private Style _dynamicHoverStyle;
60         private HyperLinkStyle _dynamicHoverHyperLinkStyle;
61 
62         private Style _rootMenuItemStyle;
63 
64         private SubMenuStyleCollection _levelStyles;
65         private MenuItemStyleCollection _levelMenuItemStyles;
66         private MenuItemStyleCollection _levelSelectedStyles;
67 
68         // Cached styles. In the current implementation, the styles are the same for all items
69         // and submenus at a given depth.
70         private List<MenuItemStyle> _cachedMenuItemStyles;
71         private List<SubMenuStyle> _cachedSubMenuStyles;
72         private List<string> _cachedMenuItemClassNames;
73         private List<string> _cachedMenuItemHyperLinkClassNames;
74         private List<string> _cachedSubMenuClassNames;
75         private Collection<int> _cachedLevelsContainingCssClass;
76 
77         private MenuItem _rootItem;
78         private MenuItem _selectedItem;
79 
80         private MenuItemBindingCollection _bindings;
81 
82         private string _cachedScrollUpImageUrl;
83         private string _cachedScrollDownImageUrl;
84         private string _cachedPopOutImageUrl;
85 
86         private ITemplate _dynamicTemplate;
87         private ITemplate _staticTemplate;
88 
89         private int _maximumDepth;
90         private int _nodeIndex;
91 
92         private string _currentSiteMapNodeUrl;
93         private bool _dataBound;
94         private bool _subControlsDataBound;
95         private bool _accessKeyRendered;
96 
97         private PopOutPanel _panel;
98         private Style _panelStyle;
99 
100         private bool _isNotIE;
101 
102         private Type _designTimeTextWriterType;
103 
104         private MenuRenderer _renderer;
105 
106         /// <devdoc>
107         ///     Creates a new instance of a Menu.
108         /// </devdoc>
Menu()109         public Menu() {
110             _nodeIndex = 0;
111             _maximumDepth = 0;
112 
113             IncludeStyleBlock = true;
114         }
115 
116         internal bool AccessKeyRendered {
117             get {
118                 return _accessKeyRendered;
119             }
120             set {
121                 _accessKeyRendered = value;
122             }
123         }
124 
125         private Collection<int> CachedLevelsContainingCssClass {
126             get {
127                 if (_cachedLevelsContainingCssClass == null) {
128                     _cachedLevelsContainingCssClass = new Collection<int>();
129                 }
130                 return _cachedLevelsContainingCssClass;
131             }
132         }
133 
134         private List<string> CachedMenuItemClassNames {
135             get {
136                 if (_cachedMenuItemClassNames == null) {
137                     _cachedMenuItemClassNames = new List<string>();
138                 }
139                 return _cachedMenuItemClassNames;
140             }
141         }
142 
143         private List<string> CachedMenuItemHyperLinkClassNames {
144             get {
145                 if (_cachedMenuItemHyperLinkClassNames == null) {
146                     _cachedMenuItemHyperLinkClassNames = new List<string>();
147                 }
148                 return _cachedMenuItemHyperLinkClassNames;
149             }
150         }
151 
152         private List<MenuItemStyle> CachedMenuItemStyles {
153             get {
154                 if (_cachedMenuItemStyles == null) {
155                     _cachedMenuItemStyles = new List<MenuItemStyle>();
156                 }
157                 return _cachedMenuItemStyles;
158             }
159         }
160 
161         private List<string> CachedSubMenuClassNames {
162             get {
163                 if (_cachedSubMenuClassNames == null) {
164                     _cachedSubMenuClassNames = new List<string>();
165                 }
166                 return _cachedSubMenuClassNames;
167             }
168         }
169 
170         private List<SubMenuStyle> CachedSubMenuStyles {
171             get {
172                 if (_cachedSubMenuStyles == null) {
173                     _cachedSubMenuStyles = new List<SubMenuStyle>();
174                 }
175                 return _cachedSubMenuStyles;
176             }
177         }
178 
179 
180         /// <devdoc>
181         ///     Gets the hidden field ID for the expand state of this Menu
182         /// </devdoc>
183         internal string ClientDataObjectID {
184             get {
185                 //
186                 return ClientID + "_Data";
187             }
188         }
189 
190         public override ControlCollection Controls {
191             get {
192                 EnsureChildControls();
193                 return base.Controls;
194             }
195         }
196 
197         [
198         DefaultValue(null),
199         Editor("System.Web.UI.Design.WebControls.MenuBindingsEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)),
200         MergableProperty(false),
201         PersistenceMode(PersistenceMode.InnerProperty),
202         WebCategory("Data"),
203         WebSysDescription(SR.Menu_Bindings)
204         ]
205         public MenuItemBindingCollection DataBindings {
206             get {
207                 if (_bindings == null) {
208                     _bindings = new MenuItemBindingCollection(this);
209                     if (IsTrackingViewState) {
210                         ((IStateManager)_bindings).TrackViewState();
211                     }
212                 }
213                 return _bindings;
214             }
215         }
216 
217 
218         [WebCategory("Behavior")]
219         [DefaultValue(500)]
220         [WebSysDescription(SR.Menu_DisappearAfter)]
221         [Themeable(false)]
222         public int DisappearAfter {
223             get {
224                 object o = ViewState["DisappearAfter"];
225                 if (o == null) {
226                     return 500;
227                 }
228                 return (int)o;
229             }
230             set {
231                 if (value < -1) {
232                     throw new ArgumentOutOfRangeException("value");
233                 }
234                 ViewState["DisappearAfter"] = value;
235             }
236         }
237 
238 
239         [DefaultValue("")]
240         [Editor("System.Web.UI.Design.ImageUrlEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor))]
241         [Themeable(true)]
242         [UrlProperty()]
243         [WebCategory("Appearance")]
244         [WebSysDescription(SR.Menu_DynamicBottomSeparatorImageUrl)]
245         public string DynamicBottomSeparatorImageUrl {
246             get {
247                 object s = ViewState["DynamicBottomSeparatorImageUrl"];
248                 if (s == null) {
249                     return String.Empty;
250                 }
251                 return (string)s;
252             }
253             set {
254                 ViewState["DynamicBottomSeparatorImageUrl"] = value;
255             }
256         }
257 
258 
259         [
260         DefaultValue(true),
261         WebCategory("Appearance"),
262         WebSysDescription(SR.Menu_DynamicDisplayPopOutImage)
263         ]
264         public bool DynamicEnableDefaultPopOutImage {
265             get {
266                 object o = ViewState["DynamicEnableDefaultPopOutImage"];
267                 if (o == null) {
268                     return true;
269                 }
270                 return (bool)o;
271             }
272             set {
273                 ViewState["DynamicEnableDefaultPopOutImage"] = value;
274             }
275         }
276 
277 
278         [
279         DefaultValue(0),
280         WebCategory("Appearance"),
281         WebSysDescription(SR.Menu_DynamicHorizontalOffset)
282         ]
283         public int DynamicHorizontalOffset {
284             get {
285                 object o = ViewState["DynamicHorizontalOffset"];
286                 if (o == null) {
287                     return 0;
288                 }
289                 return (int)o;
290             }
291             set {
292                 ViewState["DynamicHorizontalOffset"] = value;
293             }
294         }
295 
296 
297         [
298         DefaultValue(null),
299         DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
300         NotifyParentProperty(true),
301         PersistenceMode(PersistenceMode.InnerProperty),
302         WebCategory("Styles"),
303         WebSysDescription(SR.Menu_DynamicHoverStyle)
304         ]
305         public Style DynamicHoverStyle {
306             get {
307                 if (_dynamicHoverStyle == null) {
308                     _dynamicHoverStyle = new Style();
309                     if (IsTrackingViewState) {
310                         ((IStateManager)_dynamicHoverStyle).TrackViewState();
311                     }
312                 }
313                 return _dynamicHoverStyle;
314             }
315         }
316 
317         [DefaultValue("")]
318         [WebCategory("Appearance")]
319         [WebSysDescription(SR.Menu_DynamicItemFormatString)]
320         public string DynamicItemFormatString {
321             get {
322                 object s = ViewState["DynamicItemFormatString"];
323                 if (s == null) {
324                     return String.Empty;
325                 }
326                 return (string)s;
327             }
328             set {
329                 ViewState["DynamicItemFormatString"] = value;
330             }
331         }
332 
333 
334         [
335         WebCategory("Styles"),
336         DefaultValue(null),
337         DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
338         NotifyParentProperty(true),
339         PersistenceMode(PersistenceMode.InnerProperty),
340         WebSysDescription(SR.Menu_DynamicMenuItemStyle)
341         ]
342         public MenuItemStyle DynamicMenuItemStyle {
343             get {
344                 if (_dynamicItemStyle == null) {
345                     _dynamicItemStyle = new MenuItemStyle();
346                     if (IsTrackingViewState) {
347                         ((IStateManager)_dynamicItemStyle).TrackViewState();
348                     }
349                 }
350                 return _dynamicItemStyle;
351             }
352         }
353 
354 
355         [
356         WebCategory("Styles"),
357         DefaultValue(null),
358         DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
359         NotifyParentProperty(true),
360         PersistenceMode(PersistenceMode.InnerProperty),
361         WebSysDescription(SR.Menu_DynamicMenuStyle)
362         ]
363         public SubMenuStyle DynamicMenuStyle {
364             get {
365                 if (_dynamicMenuStyle == null) {
366                     _dynamicMenuStyle = new SubMenuStyle();
367                     if (IsTrackingViewState) {
368                         ((IStateManager)_dynamicMenuStyle).TrackViewState();
369                     }
370                 }
371                 return _dynamicMenuStyle;
372             }
373         }
374 
375 
376         [DefaultValue("")]
377         [Editor("System.Web.UI.Design.ImageUrlEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor))]
378         [UrlProperty()]
379         [WebCategory("Appearance")]
380         [WebSysDescription(SR.Menu_DynamicPopoutImageUrl)]
381         public string DynamicPopOutImageUrl {
382             get {
383                 object s = ViewState["DynamicPopOutImageUrl"];
384                 if (s == null) {
385                     return String.Empty;
386                 }
387                 return (string)s;
388             }
389             set {
390                 ViewState["DynamicPopOutImageUrl"] = value;
391             }
392         }
393 
394 
395         [WebSysDefaultValue(SR.MenuAdapter_Expand)]
396         [WebCategory("Appearance")]
397         [WebSysDescription(SR.Menu_DynamicPopoutImageText)]
398         public string DynamicPopOutImageTextFormatString {
399             get {
400                 object s = ViewState["DynamicPopOutImageTextFormatString"];
401                 if (s == null) {
402                     return SR.GetString(SR.MenuAdapter_Expand);
403                 }
404                 return (string)s;
405             }
406             set {
407                 ViewState["DynamicPopOutImageTextFormatString"] = value;
408             }
409         }
410 
411 
412         [
413         WebCategory("Styles"),
414         DefaultValue(null),
415         DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
416         NotifyParentProperty(true),
417         PersistenceMode(PersistenceMode.InnerProperty),
418         WebSysDescription(SR.Menu_DynamicSelectedStyle)
419         ]
420         public MenuItemStyle DynamicSelectedStyle {
421             get {
422                 if (_dynamicSelectedStyle == null) {
423                     _dynamicSelectedStyle = new MenuItemStyle();
424                     if (IsTrackingViewState) {
425                         ((IStateManager)_dynamicSelectedStyle).TrackViewState();
426                     }
427                 }
428                 return _dynamicSelectedStyle;
429             }
430         }
431 
432         [
433         Browsable(false),
434         DefaultValue(null),
435         PersistenceMode(PersistenceMode.InnerProperty),
436         TemplateContainer(typeof(MenuItemTemplateContainer)),
437         WebSysDescription(SR.Menu_DynamicTemplate)
438         ]
439         public ITemplate DynamicItemTemplate {
440             get {
441                 return _dynamicTemplate;
442             }
443             set {
444                 _dynamicTemplate = value;
445             }
446         }
447 
448 
449         [DefaultValue("")]
450         [Editor("System.Web.UI.Design.ImageUrlEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor))]
451         [UrlProperty()]
452         [WebCategory("Appearance")]
453         [WebSysDescription(SR.Menu_DynamicTopSeparatorImageUrl)]
454         public string DynamicTopSeparatorImageUrl {
455             get {
456                 object s = ViewState["DynamicTopSeparatorImageUrl"];
457                 if (s == null) {
458                     return string.Empty;
459                 }
460                 return (string)s;
461             }
462             set {
463                 ViewState["DynamicTopSeparatorImageUrl"] = value;
464             }
465         }
466 
467 
468         [
469         DefaultValue(0),
470         WebCategory("Appearance"),
471         WebSysDescription(SR.Menu_DynamicVerticalOffset)
472         ]
473         public int DynamicVerticalOffset {
474             get {
475                 object o = ViewState["DynamicVerticalOffset"];
476                 if (o == null) {
477                     return 0;
478                 }
479                 return (int)o;
480             }
481             set {
482                 ViewState["DynamicVerticalOffset"] = value;
483             }
484         }
485 
486         /// <devdoc>
487         ///     A cache of urls for the built-in images.
488         /// </devdoc>
489         private string[] ImageUrls {
490             get {
491                 if (_imageUrls == null) {
492                     _imageUrls = new string[ImageUrlsCount];
493                 }
494                 return _imageUrls;
495             }
496         }
497 
498         [DefaultValue(true)]
499         [WebCategory("Appearance")]
500         [WebSysDescription(SR.Menu_IncludeStyleBlock)]
501         public bool IncludeStyleBlock { get; set; }
502 
503         internal bool IsNotIE {
504             get {
505                 return _isNotIE;
506             }
507         }
508 
509 
510         /// <devdoc>
511         ///     Gets the collection of top-level nodes.
512         /// </devdoc>
513         [
514         DefaultValue(null),
515         Editor("System.Web.UI.Design.WebControls.MenuItemCollectionEditor," + AssemblyRef.SystemDesign, typeof(UITypeEditor)),
516         PersistenceMode(PersistenceMode.InnerProperty),
517         MergableProperty(false),
518         WebSysDescription(SR.Menu_Items)
519         ]
520         public MenuItemCollection Items {
521             get {
522                 return RootItem.ChildItems;
523             }
524         }
525 
526 
527         /// <devdoc>
528         ///     Gets and sets whether the text of the items should be wrapped
529         /// </devdoc>
530         [DefaultValue(false)]
531         [WebCategory("Appearance")]
532         [WebSysDescription(SR.Menu_ItemWrap)]
533         public bool ItemWrap {
534             get {
535                 object o = ViewState["ItemWrap"];
536                 if (o == null) {
537                     return false;
538                 }
539                 return (bool)o;
540             }
541             set {
542                 ViewState["ItemWrap"] = value;
543             }
544         }
545 
546 
547         /// <devdoc>
548         ///     Gets the collection of MenuItemStyles corresponding to each level
549         /// </devdoc>
550         [
551         DefaultValue(null),
552         Editor("System.Web.UI.Design.WebControls.MenuItemStyleCollectionEditor," + AssemblyRef.SystemDesign, typeof(UITypeEditor)),
553         PersistenceMode(PersistenceMode.InnerProperty),
554         WebCategory("Styles"),
555         WebSysDescription(SR.Menu_LevelMenuItemStyles),
556         ]
557         public MenuItemStyleCollection LevelMenuItemStyles {
558             get {
559                 if (_levelMenuItemStyles == null) {
560                     _levelMenuItemStyles = new MenuItemStyleCollection();
561                     if (IsTrackingViewState) {
562                         ((IStateManager)_levelMenuItemStyles).TrackViewState();
563                     }
564                 }
565 
566                 return _levelMenuItemStyles;
567             }
568         }
569 
570 
571         /// <devdoc>
572         ///     Gets the collection of MenuItemStyles corresponding to the selected item on each level
573         /// </devdoc>
574         [
575         DefaultValue(null),
576         Editor("System.Web.UI.Design.WebControls.MenuItemStyleCollectionEditor," + AssemblyRef.SystemDesign, typeof(UITypeEditor)),
577         PersistenceMode(PersistenceMode.InnerProperty),
578         WebCategory("Styles"),
579         WebSysDescription(SR.Menu_LevelSelectedStyles),
580         ]
581         public MenuItemStyleCollection LevelSelectedStyles {
582             get {
583                 if (_levelSelectedStyles == null) {
584                     _levelSelectedStyles = new MenuItemStyleCollection();
585                     if (IsTrackingViewState) {
586                         ((IStateManager)_levelSelectedStyles).TrackViewState();
587                     }
588                 }
589 
590                 return _levelSelectedStyles;
591             }
592         }
593 
594 
595         [
596         DefaultValue(null),
597         Editor("System.Web.UI.Design.WebControls.SubMenuStyleCollectionEditor," + AssemblyRef.SystemDesign, typeof(UITypeEditor)),
598         PersistenceMode(PersistenceMode.InnerProperty),
599         WebCategory("Styles"),
600         WebSysDescription(SR.Menu_LevelSubMenuStyles),
601         ]
602         public SubMenuStyleCollection LevelSubMenuStyles {
603             get {
604                 if (_levelStyles == null) {
605                     _levelStyles = new SubMenuStyleCollection();
606                     if (IsTrackingViewState) {
607                         ((IStateManager)_levelStyles).TrackViewState();
608                     }
609                 }
610 
611                 return _levelStyles;
612             }
613         }
614 
615         internal int MaximumDepth {
616             get {
617                 if (_maximumDepth > 0) {
618                     return _maximumDepth;
619                 }
620                 _maximumDepth = MaximumDynamicDisplayLevels + StaticDisplayLevels;
621                 if (_maximumDepth < MaximumDynamicDisplayLevels || _maximumDepth < StaticDisplayLevels) {
622                     _maximumDepth = int.MaxValue;
623                 }
624                 return _maximumDepth;
625             }
626         }
627 
628 
629         [WebCategory("Behavior")]
630         [DefaultValue(3)]
631         [Themeable(true)]
632         [WebSysDescription(SR.Menu_MaximumDynamicDisplayLevels)]
633         public int MaximumDynamicDisplayLevels {
634             get {
635                 object o = ViewState["MaximumDynamicDisplayLevels"];
636                 if (o == null) {
637                     return 3;
638                 }
639                 return (int)o;
640             }
641             set {
642                 if (value < 0) {
643                     throw new ArgumentOutOfRangeException("MaximumDynamicDisplayLevels", SR.GetString(SR.Menu_MaximumDynamicDisplayLevelsInvalid));
644                 }
645                 // Note: we're not testing against the old value here because
646                 // the setter is not the only thing that can change the underlying
647                 // ViewState entry: LoadViewState modifies it directly.
648                 ViewState["MaximumDynamicDisplayLevels"] = value;
649                 // Reset max depth cache
650                 _maximumDepth = 0;
651                 // Rebind if necessary
652                 if (_dataBound) {
653                     _dataBound = false;
654                     PerformDataBinding();
655                 }
656             }
657         }
658 
659 
660         [WebCategory("Layout")]
661         [DefaultValue(Orientation.Vertical)]
662         [WebSysDescription(SR.Menu_Orientation)]
663         public Orientation Orientation {
664             get {
665                 object o = ViewState["Orientation"];
666                 if (o == null) {
667                     return Orientation.Vertical;
668                 }
669                 return (Orientation)o;
670             }
671             set {
672                 ViewState["Orientation"] = value;
673             }
674         }
675 
676         internal PopOutPanel Panel {
677             get {
678                 if (_panel == null) {
679                     _panel = new PopOutPanel(this, _panelStyle);
680                     if (!DesignMode) {
681                         _panel.Page = Page;
682                     }
683                 }
684                 return _panel;
685             }
686         }
687 
688         [DefaultValue('/')]
689         [WebSysDescription(SR.Menu_PathSeparator)]
690         public char PathSeparator {
691             get {
692                 object o = ViewState["PathSeparator"];
693                 if (o == null) {
694                     return '/';
695                 }
696                 return (char)o;
697             }
698             set {
699                 if (value == '\0') {
700                     ViewState["PathSeparator"] = null;
701                 }
702                 else {
703                     ViewState["PathSeparator"] = value;
704                 }
705                 foreach (MenuItem item in Items) {
706                     item.ResetValuePathRecursive();
707                 }
708             }
709         }
710 
711         internal string PopoutImageUrlInternal {
712             get {
713                 if (_cachedPopOutImageUrl != null) {
714                     return _cachedPopOutImageUrl;
715                 }
716                 _cachedPopOutImageUrl = Page.ClientScript.GetWebResourceUrl(typeof(Menu), ("Menu_Popout.gif"));
717                 return _cachedPopOutImageUrl;
718             }
719         }
720 
721         private MenuRenderer Renderer {
722             get {
723                 if (_renderer == null) {
724                     switch (RenderingMode) {
725                         case MenuRenderingMode.Table:
726                             _renderer = new MenuRendererClassic(this);
727                             break;
728                         case MenuRenderingMode.List:
729                             _renderer = new MenuRendererStandards(this);
730                             break;
731                         case MenuRenderingMode.Default:
732                             if (RenderingCompatibility < VersionUtil.Framework40) {
733                                 _renderer = new MenuRendererClassic(this);
734                             }
735                             else {
736                                 _renderer = new MenuRendererStandards(this);
737                             }
738                             break;
739                         default:
740                             Debug.Fail("Unknown MenuRenderingMode.");
741                             break;
742                     }
743                 }
744                 return _renderer;
745             }
746         }
747 
748         [
749         WebCategory("Layout"),
750         DefaultValue(MenuRenderingMode.Default),
751         WebSysDescription(SR.Menu_RenderingMode)
752         ]
753         public MenuRenderingMode RenderingMode {
754             get {
755                 return _renderingMode;
756             }
757             set {
758                 if (value < MenuRenderingMode.Default || value > MenuRenderingMode.List) {
759                     throw new ArgumentOutOfRangeException("value");
760                 }
761                 if (_renderer != null) {
762                     throw new InvalidOperationException(SR.GetString(SR.Menu_CannotChangeRenderingMode));
763                 }
764                 _renderingMode = value;
765             }
766         }
767 
768         /// <devdoc>
769         ///     The 'virtual' root node of the menu. This menu item is never shown.
770         ///     It is the parent of the "real" root items.
771         /// </devdoc>
772         internal MenuItem RootItem {
773             get {
774                 if (_rootItem == null) {
775                     _rootItem = new MenuItem(this, true);
776                 }
777                 return _rootItem;
778             }
779         }
780 
781         // RootMenuItemStyle is roughly equivalent to ControlStyle.HyperLinkStyle if it existed.
782         internal Style RootMenuItemStyle {
783             get {
784                 EnsureRootMenuStyle();
785                 return _rootMenuItemStyle;
786             }
787         }
788 
789         [DefaultValue("")]
790         [Editor("System.Web.UI.Design.ImageUrlEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor))]
791         [UrlProperty()]
792         [WebCategory("Appearance")]
793         [WebSysDescription(SR.Menu_ScrollDownImageUrl)]
794         public string ScrollDownImageUrl {
795             get {
796                 object s = ViewState["ScrollDownImageUrl"];
797                 if (s == null) {
798                     return String.Empty;
799                 }
800                 return (string)s;
801             }
802             set {
803                 ViewState["ScrollDownImageUrl"] = value;
804             }
805         }
806 
807         internal string ScrollDownImageUrlInternal {
808             get {
809                 if (_cachedScrollDownImageUrl != null) {
810                     return _cachedScrollDownImageUrl;
811                 }
812                 _cachedScrollDownImageUrl = Page.ClientScript.GetWebResourceUrl(typeof(Menu), ("Menu_ScrollDown.gif"));
813                 return _cachedScrollDownImageUrl;
814             }
815         }
816 
817 
818         [WebSysDefaultValue(SR.Menu_ScrollDown)]
819         [Localizable(true)]
820         [WebCategory("Appearance")]
821         [WebSysDescription(SR.Menu_ScrollDownText)]
822         public string ScrollDownText {
823             get {
824                 object s = ViewState["ScrollDownText"];
825                 if (s == null) {
826                     return SR.GetString(SR.Menu_ScrollDown);
827                 }
828                 return (string)s;
829             }
830             set {
831                 ViewState["ScrollDownText"] = value;
832             }
833         }
834 
835 
836         [DefaultValue("")]
837         [Editor("System.Web.UI.Design.ImageUrlEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor))]
838         [UrlProperty()]
839         [WebCategory("Appearance")]
840         [WebSysDescription(SR.Menu_ScrollUpImageUrl)]
841         public string ScrollUpImageUrl {
842             get {
843                 object s = ViewState["ScrollUpImageUrl"];
844                 if (s == null) {
845                     return String.Empty;
846                 }
847                 return (string)s;
848             }
849             set {
850                 ViewState["ScrollUpImageUrl"] = value;
851             }
852         }
853 
854         internal string ScrollUpImageUrlInternal {
855             get {
856                 if (_cachedScrollUpImageUrl != null) {
857                     return _cachedScrollUpImageUrl;
858                 }
859                 _cachedScrollUpImageUrl = Page.ClientScript.GetWebResourceUrl(typeof(Menu), ("Menu_ScrollUp.gif"));
860                 return _cachedScrollUpImageUrl;
861             }
862         }
863 
864 
865         [WebSysDefaultValue(SR.Menu_ScrollUp)]
866         [Localizable(true)]
867         [WebCategory("Appearance")]
868         [WebSysDescription(SR.Menu_ScrollUpText)]
869         public string ScrollUpText {
870             get {
871                 object s = ViewState["ScrollUpText"];
872                 if (s == null) {
873                     return SR.GetString(SR.Menu_ScrollUp);
874                 }
875                 return (string)s;
876             }
877             set {
878                 ViewState["ScrollUpText"] = value;
879             }
880         }
881 
882 
883         /// <devdoc>
884         ///     Gets and sets the Menu's selected node.
885         /// </devdoc>
886         [
887         Browsable(false),
888         DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
889         ]
890         public MenuItem SelectedItem {
891             get {
892                 return _selectedItem;
893             }
894         }
895 
896 
897         [Browsable(false)]
898         [DefaultValue("")]
899         [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
900         public string SelectedValue {
901             get {
902                 if (SelectedItem != null) {
903                     return SelectedItem.Value;
904                 }
905 
906                 return String.Empty;
907             }
908         }
909 
910 
911         [WebSysDefaultValue(SR.Menu_SkipLinkTextDefault)]
912         [Localizable(true)]
913         [WebCategory("Accessibility")]
914         [WebSysDescription(SR.WebControl_SkipLinkText)]
915         public string SkipLinkText {
916             get {
917                 object s = ViewState["SkipLinkText"];
918                 if (s == null) {
919                     return SR.GetString(SR.Menu_SkipLinkTextDefault);
920                 }
921                 return (string)s;
922             }
923             set {
924                 ViewState["SkipLinkText"] = value;
925             }
926         }
927 
928 
929         [DefaultValue("")]
930         [Editor("System.Web.UI.Design.ImageUrlEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor))]
931         [UrlProperty()]
932         [WebCategory("Appearance")]
933         [WebSysDescription(SR.Menu_StaticBottomSeparatorImageUrl)]
934         public string StaticBottomSeparatorImageUrl {
935             get {
936                 object s = ViewState["StaticBottomSeparatorImageUrl"];
937                 if (s == null) {
938                     return String.Empty;
939                 }
940                 return (string)s;
941             }
942             set {
943                 ViewState["StaticBottomSeparatorImageUrl"] = value;
944             }
945         }
946 
947 
948         [WebCategory("Behavior")]
949         [DefaultValue(1)]
950         [Themeable(true)]
951         [WebSysDescription(SR.Menu_StaticDisplayLevels)]
952         public int StaticDisplayLevels {
953             get {
954                 object o = ViewState["StaticDisplayLevels"];
955                 if (o == null) {
956                     return 1;
957                 }
958                 return (int)o;
959             }
960             set {
961                 if (value < 1) {
962                     throw new ArgumentOutOfRangeException("value");
963                 }
964                 // Note: we're not testing against the old value here because
965                 // the setter is not the only thing that can change the underlying
966                 // ViewState entry: LoadViewState modifies it directly.
967                 ViewState["StaticDisplayLevels"] = value;
968                 // Reset max depth cache
969                 _maximumDepth = 0;
970                 // Rebind if necessary
971                 if (_dataBound && !DesignMode) {
972                     _dataBound = false;
973                     PerformDataBinding();
974                 }
975             }
976         }
977 
978 
979         [
980         DefaultValue(true),
981         WebCategory("Appearance"),
982         WebSysDescription(SR.Menu_StaticDisplayPopOutImage)
983         ]
984         public bool StaticEnableDefaultPopOutImage {
985             get {
986                 object o = ViewState["StaticEnableDefaultPopOutImage"];
987                 if (o == null) {
988                     return true;
989                 }
990                 return (bool)o;
991             }
992             set {
993                 ViewState["StaticEnableDefaultPopOutImage"] = value;
994             }
995         }
996 
997 
998         [
999         DefaultValue(null),
1000         DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
1001         NotifyParentProperty(true),
1002         PersistenceMode(PersistenceMode.InnerProperty),
1003         WebCategory("Styles"),
1004         WebSysDescription(SR.Menu_StaticHoverStyle)
1005         ]
1006         public Style StaticHoverStyle {
1007             get {
1008                 if (_staticHoverStyle == null) {
1009                     _staticHoverStyle = new Style();
1010                     if (IsTrackingViewState) {
1011                         ((IStateManager)_staticHoverStyle).TrackViewState();
1012                     }
1013                 }
1014                 return _staticHoverStyle;
1015             }
1016         }
1017 
1018         [DefaultValue("")]
1019         [WebCategory("Appearance")]
1020         [WebSysDescription(SR.Menu_StaticItemFormatString)]
1021         public string StaticItemFormatString {
1022             get {
1023                 object s = ViewState["StaticItemFormatString"];
1024                 if (s == null) {
1025                     return String.Empty;
1026                 }
1027                 return (string)s;
1028             }
1029             set {
1030                 ViewState["StaticItemFormatString"] = value;
1031             }
1032         }
1033 
1034 
1035         [
1036         WebCategory("Styles"),
1037         DefaultValue(null),
1038         DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
1039         NotifyParentProperty(true),
1040         PersistenceMode(PersistenceMode.InnerProperty),
1041         WebSysDescription(SR.Menu_StaticMenuItemStyle)
1042         ]
1043         public MenuItemStyle StaticMenuItemStyle {
1044             get {
1045                 if (_staticItemStyle == null) {
1046                     _staticItemStyle = new MenuItemStyle();
1047                     if (IsTrackingViewState) {
1048                         ((IStateManager)_staticItemStyle).TrackViewState();
1049                     }
1050                 }
1051                 return _staticItemStyle;
1052             }
1053         }
1054 
1055 
1056         [
1057         WebCategory("Styles"),
1058         DefaultValue(null),
1059         DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
1060         NotifyParentProperty(true),
1061         PersistenceMode(PersistenceMode.InnerProperty),
1062         WebSysDescription(SR.Menu_StaticMenuStyle)
1063         ]
1064         public SubMenuStyle StaticMenuStyle {
1065             get {
1066                 if (_staticMenuStyle == null) {
1067                     _staticMenuStyle = new SubMenuStyle();
1068                     if (IsTrackingViewState) {
1069                         ((IStateManager)_staticMenuStyle).TrackViewState();
1070                     }
1071                 }
1072                 return _staticMenuStyle;
1073             }
1074         }
1075 
1076 
1077         [DefaultValue("")]
1078         [Editor("System.Web.UI.Design.ImageUrlEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor))]
1079         [UrlProperty()]
1080         [WebCategory("Appearance")]
1081         [WebSysDescription(SR.Menu_StaticPopoutImageUrl)]
1082         public string StaticPopOutImageUrl {
1083             get {
1084                 object s = ViewState["StaticPopOutImageUrl"];
1085                 if (s == null) {
1086                     return String.Empty;
1087                 }
1088                 return (string)s;
1089             }
1090             set {
1091                 ViewState["StaticPopOutImageUrl"] = value;
1092             }
1093         }
1094 
1095 
1096         [WebSysDefaultValue(SR.MenuAdapter_Expand)]
1097         [WebCategory("Appearance")]
1098         [WebSysDescription(SR.Menu_StaticPopoutImageText)]
1099         public string StaticPopOutImageTextFormatString {
1100             get {
1101                 object s = ViewState["StaticPopOutImageTextFormatString"];
1102                 if (s == null) {
1103                     return SR.GetString(SR.MenuAdapter_Expand);
1104                 }
1105                 return (string)s;
1106             }
1107             set {
1108                 ViewState["StaticPopOutImageTextFormatString"] = value;
1109             }
1110         }
1111 
1112 
1113         [
1114         WebCategory("Styles"),
1115         DefaultValue(null),
1116         DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
1117         NotifyParentProperty(true),
1118         PersistenceMode(PersistenceMode.InnerProperty),
1119         WebSysDescription(SR.Menu_StaticSelectedStyle)
1120         ]
1121         public MenuItemStyle StaticSelectedStyle {
1122             get {
1123                 if (_staticSelectedStyle == null) {
1124                     _staticSelectedStyle = new MenuItemStyle();
1125                     if (IsTrackingViewState) {
1126                         ((IStateManager)_staticSelectedStyle).TrackViewState();
1127                     }
1128                 }
1129                 return _staticSelectedStyle;
1130             }
1131         }
1132 
1133 
1134         [WebCategory("Appearance")]
1135         [DefaultValue(typeof(Unit), "")]
1136         [Themeable(true)]
1137         [WebSysDescription(SR.Menu_StaticSubMenuIndent)]
1138         public Unit StaticSubMenuIndent {
1139             get {
1140                 object o = ViewState["StaticSubMenuIndent"];
1141                 if (o == null) {
1142                     return Unit.Empty;
1143                 }
1144                 return (Unit)o;
1145             }
1146             set {
1147                 if (value.Value < 0) {
1148                     throw new ArgumentOutOfRangeException("value");
1149                 }
1150                 ViewState["StaticSubMenuIndent"] = value;
1151             }
1152         }
1153 
1154         [
1155         Browsable(false),
1156         DefaultValue(null),
1157         PersistenceMode(PersistenceMode.InnerProperty),
1158         TemplateContainer(typeof(MenuItemTemplateContainer)),
1159         WebSysDescription(SR.Menu_StaticTemplate)
1160         ]
1161         public ITemplate StaticItemTemplate {
1162             get {
1163                 return _staticTemplate;
1164             }
1165             set {
1166                 _staticTemplate = value;
1167             }
1168         }
1169 
1170 
1171         [DefaultValue("")]
1172         [Editor("System.Web.UI.Design.ImageUrlEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor))]
1173         [UrlProperty()]
1174         [WebCategory("Appearance")]
1175         [WebSysDescription(SR.Menu_StaticTopSeparatorImageUrl)]
1176         public string StaticTopSeparatorImageUrl {
1177             get {
1178                 object s = ViewState["StaticTopSeparatorImageUrl"];
1179                 if (s == null) {
1180                     return String.Empty;
1181                 }
1182                 return (string)s;
1183             }
1184             set {
1185                 ViewState["StaticTopSeparatorImageUrl"] = value;
1186             }
1187         }
1188 
1189 
1190         /// <devdoc>
1191         ///     Gets and sets the target window that the MenuItems will browse to if selected
1192         /// </devdoc>
1193         [DefaultValue("")]
1194         [WebSysDescription(SR.MenuItem_Target)]
1195         public string Target {
1196             get {
1197                 object s = ViewState["Target"];
1198                 if (s == null) {
1199                     return String.Empty;
1200                 }
1201                 return (string)s;
1202             }
1203             set {
1204                 ViewState["Target"] = value;
1205             }
1206         }
1207 
1208 
1209         protected override HtmlTextWriterTag TagKey {
1210             get {
1211                 return HtmlTextWriterTag.Table;
1212             }
1213         }
1214 
1215         #region events
1216 
1217         /// <devdoc>
1218         ///     Triggered when an item has been clicked.
1219         /// </devdoc>
1220         [WebCategory("Behavior")]
1221         [WebSysDescription(SR.Menu_MenuItemClick)]
1222         public event MenuEventHandler MenuItemClick {
1223             add {
1224                 Events.AddHandler(_menuItemClickedEvent, value);
1225             }
1226             remove {
1227                 Events.RemoveHandler(_menuItemClickedEvent, value);
1228             }
1229         }
1230 
1231 
1232         /// <devdoc>
1233         ///     Triggered when a MenuItem has been databound.
1234         /// </devdoc>
1235         [WebCategory("Behavior")]
1236         [WebSysDescription(SR.Menu_MenuItemDataBound)]
1237         public event MenuEventHandler MenuItemDataBound {
1238             add {
1239                 Events.AddHandler(_menuItemDataBoundEvent, value);
1240             }
1241             remove {
1242                 Events.RemoveHandler(_menuItemDataBoundEvent, value);
1243             }
1244         }
1245         #endregion
1246 
AddAttributesToRender(HtmlTextWriter writer)1247         protected override void AddAttributesToRender(HtmlTextWriter writer) {
1248             VerifyRenderingInServerForm();
1249 
1250             string oldAccessKey = AccessKey;
1251             try {
1252                 AccessKey = String.Empty;
1253                 base.AddAttributesToRender(writer);
1254             }
1255             finally {
1256                 AccessKey = oldAccessKey;
1257             }
1258         }
1259 
1260         // returns true if the style contains a class name
AppendCssClassName(StringBuilder builder, MenuItemStyle style, bool hyperlink)1261         private static bool AppendCssClassName(StringBuilder builder, MenuItemStyle style, bool hyperlink) {
1262             bool containsClassName = false;
1263             if (style != null) {
1264                 // We have to merge with any CssClass specified on the Style itself
1265                 if (style.CssClass.Length != 0) {
1266                     builder.Append(style.CssClass);
1267                     builder.Append(' ');
1268                     containsClassName = true;
1269                 }
1270 
1271                 string className = (hyperlink ?
1272                     style.HyperLinkStyle.RegisteredCssClass :
1273                     style.RegisteredCssClass);
1274                 if (className.Length > 0) {
1275                     builder.Append(className);
1276                     builder.Append(' ');
1277                 }
1278             }
1279             return containsClassName;
1280         }
1281 
AppendMenuCssClassName(StringBuilder builder, SubMenuStyle style)1282         private static void AppendMenuCssClassName(StringBuilder builder, SubMenuStyle style) {
1283             if (style != null) {
1284                 // We have to merge with any CssClass specified on the Style itself
1285                 if (style.CssClass.Length != 0) {
1286                     builder.Append(style.CssClass);
1287                     builder.Append(' ');
1288                 }
1289                 string className = style.RegisteredCssClass;
1290                 if (className.Length > 0) {
1291                     builder.Append(className);
1292                     builder.Append(' ');
1293                 }
1294             }
1295         }
1296 
1297         private static T CacheGetItem<T>(List<T> cacheList, int index) where T : class {
1298             Debug.Assert(cacheList != null);
1299             if (index < cacheList.Count) return cacheList[index];
1300             return null;
1301         }
1302 
1303         private static void CacheSetItem<T>(List<T> cacheList, int index, T item) where T : class {
1304             if (cacheList.Count > index) {
1305                 cacheList[index] = item;
1306             }
1307             else {
1308                 for (int i = cacheList.Count; i < index; i++) {
1309                     cacheList.Add(null);
1310                 }
1311                 cacheList.Add(item);
1312             }
1313         }
1314 
CreateChildControls()1315         protected internal override void CreateChildControls() {
1316             Controls.Clear();
1317 
1318             if ((StaticItemTemplate != null) || (DynamicItemTemplate != null)) {
1319                 if (RequiresDataBinding &&
1320                     (!String.IsNullOrEmpty(DataSourceID) || DataSource != null)) {
1321                     EnsureDataBound();
1322                 }
1323                 else {
1324                     // Creating child controls from the Items tree
1325                     CreateChildControlsFromItems(/* dataBinding */ false);
1326                     ClearChildViewState();
1327                 }
1328             }
1329         }
1330 
1331 
CreateChildControlsFromItems(bool dataBinding)1332         private void CreateChildControlsFromItems(bool dataBinding) {
1333             if (StaticItemTemplate != null || DynamicItemTemplate != null) {
1334                 int childPosition = 0;
1335                 foreach (MenuItem child in Items) {
1336                     CreateTemplatedControls(StaticItemTemplate, child, childPosition++, 0, dataBinding);
1337                 }
1338             }
1339         }
1340 
1341         /// <devdoc>
1342         ///     Creates a menu node index that is unique for this menu
1343         /// </devdoc>
CreateItemIndex()1344         internal int CreateItemIndex() {
1345             return _nodeIndex++;
1346         }
1347 
CreateTemplatedControls(ITemplate template, MenuItem item, int position, int depth, bool dataBinding)1348         private void CreateTemplatedControls(ITemplate template, MenuItem item, int position, int depth, bool dataBinding) {
1349 
1350             if (template != null) {
1351                 MenuItemTemplateContainer container = new MenuItemTemplateContainer(position, item);
1352                 item.Container = (MenuItemTemplateContainer)container;
1353                 template.InstantiateIn(container);
1354                 Controls.Add(container);
1355                 if (dataBinding) {
1356                     container.DataBind();
1357                 }
1358             }
1359             int childPosition = 0;
1360             foreach (MenuItem child in item.ChildItems) {
1361                 int nextDepth = depth + 1;
1362                 if (template == DynamicItemTemplate) {
1363                     CreateTemplatedControls(DynamicItemTemplate, child, childPosition++, nextDepth, dataBinding);
1364                 }
1365                 else {
1366                     if (nextDepth < StaticDisplayLevels) {
1367                         CreateTemplatedControls(template, child, childPosition++, nextDepth, dataBinding);
1368                     }
1369                     else if (DynamicItemTemplate != null) {
1370                         CreateTemplatedControls(DynamicItemTemplate, child, childPosition++, nextDepth, dataBinding);
1371                     }
1372                 }
1373             }
1374         }
1375 
1376         /// Data bound controls should override PerformDataBinding instead
1377         /// of DataBind.  If DataBind if overridden, the OnDataBinding and OnDataBound events will
1378         /// fire in the wrong order.  However, for backwards compat on ListControl and AdRotator, we
1379         /// can't seal this method.  It is sealed on all new BaseDataBoundControl-derived controls.
DataBind()1380         public override sealed void DataBind() {
1381             base.DataBind();
1382         }
1383 
1384 
1385         /// <devdoc>
1386         ///     Databinds the specified node to the datasource
1387         /// </devdoc>
DataBindItem(MenuItem item)1388         private void DataBindItem(MenuItem item) {
1389             HierarchicalDataSourceView view = GetData(item.DataPath);
1390             // Do nothing if no datasource was set
1391             if (!IsBoundUsingDataSourceID && (DataSource == null)) {
1392                 return;
1393             }
1394 
1395             if (view == null) {
1396                 throw new InvalidOperationException(SR.GetString(SR.Menu_DataSourceReturnedNullView, ID));
1397             }
1398             IHierarchicalEnumerable enumerable = view.Select();
1399             item.ChildItems.Clear();
1400             if (enumerable != null) {
1401                 // If we're bound to a SiteMapDataSource, automatically select the node
1402                 if (IsBoundUsingDataSourceID) {
1403                     SiteMapDataSource siteMapDataSource = GetDataSource() as SiteMapDataSource;
1404                     if (siteMapDataSource != null) {
1405                         SiteMapNode currentNode = siteMapDataSource.Provider.CurrentNode;
1406                         if (currentNode != null) {
1407                             _currentSiteMapNodeUrl = currentNode.Url;
1408                         }
1409                     }
1410                 }
1411 
1412                 try {
1413                     DataBindRecursive(item, enumerable);
1414                 }
1415                 finally {
1416                     _currentSiteMapNodeUrl = null;
1417                 }
1418             }
1419         }
1420 
1421 
1422         /// <devdoc>
1423         ///     Databinds recursively, using the Menu's Bindings collection, until there is no more data.
1424         /// </devdoc>
DataBindRecursive(MenuItem node, IHierarchicalEnumerable enumerable)1425         private void DataBindRecursive(MenuItem node, IHierarchicalEnumerable enumerable) {
1426             // Since we are binding children, get the level below the current node's depth
1427             int depth = node.Depth + 1;
1428 
1429             // Don't databind beyond the maximum specified depth
1430             if ((MaximumDynamicDisplayLevels != -1) && (depth >= MaximumDepth)) {
1431                 return;
1432             }
1433 
1434             foreach (object item in enumerable) {
1435                 IHierarchyData data = enumerable.GetHierarchyData(item);
1436 
1437                 string text = null;
1438                 string value = null;
1439                 string navigateUrl = String.Empty;
1440                 string imageUrl = String.Empty;
1441                 string popOutImageUrl = String.Empty;
1442                 string separatorImageUrl = String.Empty;
1443                 string target = String.Empty;
1444                 bool enabled = true;
1445                 bool enabledSet = false;
1446                 bool selectable = true;
1447                 bool selectableSet = false;
1448                 string toolTip = String.Empty;
1449 
1450                 string dataMember = String.Empty;
1451 
1452                 dataMember = data.Type;
1453 
1454                 MenuItemBinding level = DataBindings.GetBinding(dataMember, depth);
1455 
1456                 if (level != null) {
1457 
1458                     PropertyDescriptorCollection props = TypeDescriptor.GetProperties(item);
1459 
1460                     // Bind Text, using the static value if necessary
1461                     string textField = level.TextField;
1462                     if (textField.Length > 0) {
1463                         PropertyDescriptor desc = props.Find(textField, true);
1464                         if (desc != null) {
1465                             object objData = desc.GetValue(item);
1466                             if (objData != null) {
1467                                 if (level.FormatString.Length > 0) {
1468                                     text = string.Format(CultureInfo.CurrentCulture, level.FormatString, objData);
1469                                 }
1470                                 else {
1471                                     text = objData.ToString();
1472                                 }
1473                             }
1474                         }
1475                         else {
1476                             throw new InvalidOperationException(SR.GetString(SR.Menu_InvalidDataBinding, textField, "TextField"));
1477                         }
1478                     }
1479 
1480                     if (String.IsNullOrEmpty(text) && !String.IsNullOrEmpty(level.Text)) {
1481                         text = level.Text;
1482                     }
1483 
1484                     // Bind Value, using the static value if necessary
1485                     string valueField = level.ValueField;
1486                     if (valueField.Length > 0) {
1487                         PropertyDescriptor desc = props.Find(valueField, true);
1488                         if (desc != null) {
1489                             object objData = desc.GetValue(item);
1490                             if (objData != null) {
1491                                 value = objData.ToString();
1492                             }
1493                         }
1494                         else {
1495                             throw new InvalidOperationException(SR.GetString(SR.Menu_InvalidDataBinding, valueField, "ValueField"));
1496                         }
1497                     }
1498 
1499                     if (String.IsNullOrEmpty(value) && !String.IsNullOrEmpty(level.Value)) {
1500                         value = level.Value;
1501                     }
1502 
1503                     // Bind Target, using the static value if necessary
1504                     string targetField = level.TargetField;
1505                     if (targetField.Length > 0) {
1506                         PropertyDescriptor desc = props.Find(targetField, true);
1507                         if (desc != null) {
1508                             object objData = desc.GetValue(item);
1509                             if (objData != null) {
1510                                 target = objData.ToString();
1511                             }
1512                         }
1513                         else {
1514                             throw new InvalidOperationException(SR.GetString(SR.Menu_InvalidDataBinding, targetField, "TargetField"));
1515                         }
1516                     }
1517 
1518                     if (String.IsNullOrEmpty(target)) {
1519                         target = level.Target;
1520                     }
1521 
1522                     // Bind ImageUrl, using the static value if necessary
1523                     string imageUrlField = level.ImageUrlField;
1524                     if (imageUrlField.Length > 0) {
1525                         PropertyDescriptor desc = props.Find(imageUrlField, true);
1526                         if (desc != null) {
1527                             object objData = desc.GetValue(item);
1528                             if (objData != null) {
1529                                 imageUrl = objData.ToString();
1530                             }
1531                         }
1532                         else {
1533                             throw new InvalidOperationException(SR.GetString(SR.Menu_InvalidDataBinding, imageUrlField, "ImageUrlField"));
1534                         }
1535                     }
1536 
1537                     if (String.IsNullOrEmpty(imageUrl)) {
1538                         imageUrl = level.ImageUrl;
1539                     }
1540 
1541                     // Bind NavigateUrl, using the static value if necessary
1542                     string navigateUrlField = level.NavigateUrlField;
1543                     if (navigateUrlField.Length > 0) {
1544                         PropertyDescriptor desc = props.Find(navigateUrlField, true);
1545                         if (desc != null) {
1546                             object objData = desc.GetValue(item);
1547                             if (objData != null) {
1548                                 navigateUrl = objData.ToString();
1549                             }
1550                         }
1551                         else {
1552                             throw new InvalidOperationException(SR.GetString(SR.Menu_InvalidDataBinding, navigateUrlField, "NavigateUrlField"));
1553                         }
1554                     }
1555 
1556                     if (String.IsNullOrEmpty(navigateUrl)) {
1557                         navigateUrl = level.NavigateUrl;
1558                     }
1559 
1560                     // Bind PopOutImageUrl, using the static value if necessary
1561                     string popOutImageUrlField = level.PopOutImageUrlField;
1562                     if (popOutImageUrlField.Length > 0) {
1563                         PropertyDescriptor desc = props.Find(popOutImageUrlField, true);
1564                         if (desc != null) {
1565                             object objData = desc.GetValue(item);
1566                             if (objData != null) {
1567                                 popOutImageUrl = objData.ToString();
1568                             }
1569                         }
1570                         else {
1571                             throw new InvalidOperationException(SR.GetString(SR.Menu_InvalidDataBinding, popOutImageUrlField, "PopOutImageUrlField"));
1572                         }
1573                     }
1574 
1575                     if (String.IsNullOrEmpty(popOutImageUrl)) {
1576                         popOutImageUrl = level.PopOutImageUrl;
1577                     }
1578 
1579                     // Bind SeperatorImageUrl, using the static value if necessary
1580                     string separatorImageUrlField = level.SeparatorImageUrlField;
1581                     if (separatorImageUrlField.Length > 0) {
1582                         PropertyDescriptor desc = props.Find(separatorImageUrlField, true);
1583                         if (desc != null) {
1584                             object objData = desc.GetValue(item);
1585                             if (objData != null) {
1586                                 separatorImageUrl = objData.ToString();
1587                             }
1588                         }
1589                         else {
1590                             throw new InvalidOperationException(SR.GetString(SR.Menu_InvalidDataBinding, separatorImageUrlField, "SeparatorImageUrlField"));
1591                         }
1592                     }
1593 
1594                     if (String.IsNullOrEmpty(separatorImageUrl)) {
1595                         separatorImageUrl = level.SeparatorImageUrl;
1596                     }
1597 
1598                     // Bind ToolTip, using the static value if necessary
1599                     string toolTipField = level.ToolTipField;
1600                     if (toolTipField.Length > 0) {
1601                         PropertyDescriptor desc = props.Find(toolTipField, true);
1602                         if (desc != null) {
1603                             object objData = desc.GetValue(item);
1604                             if (objData != null) {
1605                                 toolTip = objData.ToString();
1606                             }
1607                         }
1608                         else {
1609                             throw new InvalidOperationException(SR.GetString(SR.Menu_InvalidDataBinding, toolTipField, "ToolTipField"));
1610                         }
1611                     }
1612 
1613                     if (String.IsNullOrEmpty(toolTip)) {
1614                         toolTip = level.ToolTip;
1615                     }
1616 
1617                     // Bind Enabled, using the static value if necessary
1618                     string enabledField = level.EnabledField;
1619                     if (enabledField.Length > 0) {
1620                         PropertyDescriptor desc = props.Find(enabledField, true);
1621                         if (desc != null) {
1622                             object objData = desc.GetValue(item);
1623                             if (objData != null) {
1624                                 if (objData is bool) {
1625                                     enabled = (bool)objData;
1626                                     enabledSet = true;
1627                                 }
1628                                 else if (bool.TryParse(objData.ToString(), out enabled)) {
1629                                     enabledSet = true;
1630                                 }
1631                             }
1632                         }
1633                         else {
1634                             throw new InvalidOperationException(SR.GetString(SR.Menu_InvalidDataBinding, enabledField, "EnabledField"));
1635                         }
1636                     }
1637 
1638                     if (!enabledSet) {
1639                         enabled = level.Enabled;
1640                     }
1641 
1642                     // Bind Selectable, using the static value if necessary
1643                     string selectableField = level.SelectableField;
1644                     if (selectableField.Length > 0) {
1645                         PropertyDescriptor desc = props.Find(selectableField, true);
1646                         if (desc != null) {
1647                             object objData = desc.GetValue(item);
1648                             if (objData != null) {
1649                                 if (objData is bool) {
1650                                     selectable = (bool)objData;
1651                                     selectableSet = true;
1652                                 }
1653                                 else if (bool.TryParse(objData.ToString(), out selectable)) {
1654                                     selectableSet = true;
1655                                 }
1656                             }
1657                         }
1658                         else {
1659                             throw new InvalidOperationException(SR.GetString(SR.Menu_InvalidDataBinding, selectableField, "SelectableField"));
1660                         }
1661                     }
1662 
1663                     if (!selectableSet) {
1664                         selectable = level.Selectable;
1665                     }
1666                 }
1667                 else if (item is INavigateUIData) {
1668                     INavigateUIData navigateUIData = (INavigateUIData)item;
1669                     text = navigateUIData.Name;
1670                     value = navigateUIData.Value;
1671                     navigateUrl = navigateUIData.NavigateUrl;
1672                     if (String.IsNullOrEmpty(navigateUrl)) {
1673                         selectable = false;
1674                     }
1675                     toolTip = navigateUIData.Description;
1676                 }
1677 
1678                 if (text == null) {
1679                     text = item.ToString();
1680                 }
1681 
1682                 MenuItem newItem = null;
1683                 // Allow String.Empty for the text, but not null
1684                 if ((text != null) || (value != null)) {
1685                     newItem = new MenuItem(text, value, imageUrl, navigateUrl, target);
1686                 }
1687 
1688                 if (newItem != null) {
1689                     if (toolTip.Length > 0) {
1690                         newItem.ToolTip = toolTip;
1691                     }
1692                     if (popOutImageUrl.Length > 0) {
1693                         newItem.PopOutImageUrl = popOutImageUrl;
1694                     }
1695                     if (separatorImageUrl.Length > 0) {
1696                         newItem.SeparatorImageUrl = separatorImageUrl;
1697                     }
1698                     newItem.Enabled = enabled;
1699                     newItem.Selectable = selectable;
1700 
1701                     newItem.SetDataPath(data.Path);
1702                     newItem.SetDataBound(true);
1703 
1704                     node.ChildItems.Add(newItem);
1705 
1706                     if (String.Equals(data.Path, _currentSiteMapNodeUrl, StringComparison.OrdinalIgnoreCase)) {
1707                         newItem.Selected = true;
1708                     }
1709 
1710                     // Make sure we call user code if they've hooked the populate event
1711                     newItem.SetDataItem(data.Item);
1712                     OnMenuItemDataBound(new MenuEventArgs(newItem));
1713                     newItem.SetDataItem(null);
1714 
1715                     if (data.HasChildren && (depth < MaximumDepth)) {
1716                         IHierarchicalEnumerable newEnumerable = data.GetChildren();
1717                         if (newEnumerable != null) {
1718                             DataBindRecursive(newItem, newEnumerable);
1719                         }
1720                     }
1721                 }
1722             }
1723         }
1724 
EnsureDataBound()1725         protected override void EnsureDataBound() {
1726             base.EnsureDataBound();
1727             if (!_subControlsDataBound) {
1728                 foreach (Control ctrl in Controls) {
1729                     ctrl.DataBind();
1730                 }
1731                 _subControlsDataBound = true;
1732             }
1733         }
1734 
FindItem(string valuePath)1735         public MenuItem FindItem(string valuePath) {
1736             if (valuePath == null) {
1737                 return null;
1738             }
1739             return Items.FindItem(valuePath.Split(PathSeparator), 0);
1740         }
1741 
GetCssClassName(MenuItem item, bool hyperLink)1742         internal string GetCssClassName(MenuItem item, bool hyperLink) {
1743             bool discarded;
1744             return GetCssClassName(item, hyperLink, out discarded);
1745         }
1746 
GetCssClassName(MenuItem item, bool hyperlink, out bool containsClassName)1747         internal string GetCssClassName(MenuItem item, bool hyperlink, out bool containsClassName) {
1748             if (item == null) {
1749                 throw new ArgumentNullException("item");
1750             }
1751 
1752             containsClassName = false;
1753             int depth = item.Depth;
1754             string baseClassName = CacheGetItem<string>(
1755                 hyperlink ? CachedMenuItemHyperLinkClassNames : CachedMenuItemClassNames,
1756                 depth);
1757             if (CachedLevelsContainingCssClass.Contains(depth)) {
1758                 containsClassName = true;
1759             }
1760 
1761             if (!item.Selected && (baseClassName != null)) {
1762                 return baseClassName;
1763             }
1764 
1765             StringBuilder builder = new StringBuilder();
1766             if (baseClassName != null) {
1767                 if (!item.Selected) return baseClassName;
1768                 builder.Append(baseClassName);
1769                 builder.Append(' ');
1770             }
1771             else {
1772                 // No cached style, so build it
1773                 if (hyperlink) {
1774                     builder.Append(RootMenuItemStyle.RegisteredCssClass);
1775                     builder.Append(' ');
1776                 }
1777 
1778                 if (depth < StaticDisplayLevels) {
1779                     containsClassName |= AppendCssClassName(builder, _staticItemStyle, hyperlink);
1780                 }
1781                 else {
1782                     containsClassName |= AppendCssClassName(builder, _dynamicItemStyle, hyperlink);
1783                 }
1784                 if ((depth < LevelMenuItemStyles.Count) && (LevelMenuItemStyles[depth] != null)) {
1785                     containsClassName |= AppendCssClassName(builder, LevelMenuItemStyles[depth], hyperlink);
1786                 }
1787 
1788                 baseClassName = builder.ToString().Trim();
1789                 CacheSetItem<string>(
1790                     hyperlink ? CachedMenuItemHyperLinkClassNames : CachedMenuItemClassNames,
1791                     depth,
1792                     baseClassName);
1793 
1794                 if (containsClassName && !CachedLevelsContainingCssClass.Contains(depth)) {
1795                     CachedLevelsContainingCssClass.Add(depth);
1796                 }
1797             }
1798 
1799             if (item.Selected) {
1800                 if (depth < StaticDisplayLevels) {
1801                     containsClassName |= AppendCssClassName(builder, _staticSelectedStyle, hyperlink);
1802                 }
1803                 else {
1804                     containsClassName |= AppendCssClassName(builder, _dynamicSelectedStyle, hyperlink);
1805                 }
1806                 if ((depth < LevelSelectedStyles.Count) && (LevelSelectedStyles[depth] != null)) {
1807                     MenuItemStyle style = LevelSelectedStyles[depth];
1808                     containsClassName |= AppendCssClassName(builder, style, hyperlink);
1809                 }
1810                 return builder.ToString().Trim();
1811             }
1812             return baseClassName;
1813         }
1814 
1815         /// <devdoc>Used by GetDesignModeState to find the first dynamic submenu in Items</devdoc>
GetOneDynamicItem(MenuItem item)1816         private MenuItem GetOneDynamicItem(MenuItem item) {
1817             if (item.Depth >= StaticDisplayLevels) {
1818                 return item;
1819             }
1820             for (int i = 0; i < item.ChildItems.Count; i++) {
1821                 MenuItem result = GetOneDynamicItem(item.ChildItems[i]);
1822                 if (result != null) {
1823                     return result;
1824                 }
1825             }
1826             return null;
1827         }
1828 
1829 
1830         /// <internalonly/>
1831         /// <devdoc>
1832         /// The designer can get the static and the dynamic mode html by using the
1833         /// _getDesignTimeStaticHtml and _getDesignTimeDynamicHtml defined string keys
1834         /// of the dictionary.
1835         /// </devdoc>
1836         [SecurityPermission(SecurityAction.Demand, Unrestricted = true)]
GetDesignModeState()1837         protected override IDictionary GetDesignModeState() {
1838             IDictionary dictionary = base.GetDesignModeState();
1839             Debug.Assert(dictionary != null && DesignMode);
1840 
1841             CreateChildControls();
1842             foreach (Control c in Controls) {
1843                 c.DataBind();
1844             }
1845 
1846             // Create the html for the static part
1847             using (StringWriter staticHtmlBuilder = new StringWriter(CultureInfo.CurrentCulture)) {
1848                 using (HtmlTextWriter htmlWriter = GetDesignTimeWriter(staticHtmlBuilder)) {
1849                     Renderer.RenderBeginTag(htmlWriter, true);
1850                     Renderer.RenderContents(htmlWriter, true);
1851                     Renderer.RenderEndTag(htmlWriter, true);
1852                     dictionary[_getDesignTimeStaticHtml] = staticHtmlBuilder.ToString();
1853                 }
1854             }
1855             // Remember the static depth so we can lower it if necessary to make it faster and avoid overflows
1856             int oldStaticDisplayLevels = StaticDisplayLevels;
1857             try {
1858                 // Find a dynamic sub-menu
1859                 MenuItem dynamicSubMenu = GetOneDynamicItem(RootItem);
1860                 if (dynamicSubMenu == null) {
1861                     // We need to forge a whole new dynamic submenu
1862                     // First lower the static display levels
1863                     _dataBound = false;
1864                     StaticDisplayLevels = 1;
1865                     dynamicSubMenu = new MenuItem();
1866                     dynamicSubMenu.SetDepth(0);
1867                     dynamicSubMenu.SetOwner(this);
1868                     // Create a single dynamic submenu, with one submenu
1869                     string dummyText = SR.GetString(SR.Menu_DesignTimeDummyItemText);
1870                     for (int i = 0; i < 5; i++) {
1871                         MenuItem newItem = new MenuItem(dummyText);
1872                         if (DynamicItemTemplate != null) {
1873                             MenuItemTemplateContainer container = new MenuItemTemplateContainer(i, newItem);
1874                             newItem.Container = container;
1875                             DynamicItemTemplate.InstantiateIn(container);
1876                             container.Site = this.Site;
1877                             container.DataBind();
1878                         }
1879                         dynamicSubMenu.ChildItems.Add(newItem);
1880                     }
1881                     dynamicSubMenu.ChildItems[1].ChildItems.Add(new MenuItem());
1882                     // Delete cached styles to ensure consistency
1883                     _cachedLevelsContainingCssClass = null;
1884                     _cachedMenuItemStyles = null;
1885                     _cachedSubMenuStyles = null;
1886                     _cachedMenuItemClassNames = null;
1887                     _cachedMenuItemHyperLinkClassNames = null;
1888                     _cachedSubMenuClassNames = null;
1889                 }
1890                 else {
1891                     dynamicSubMenu = dynamicSubMenu.Parent;
1892                 }
1893                 // Create the html for the dynamic part
1894                 using (StringWriter dynamicHtmlBuilder = new StringWriter(CultureInfo.CurrentCulture)) {
1895                     using (HtmlTextWriter htmlWriter = GetDesignTimeWriter(dynamicHtmlBuilder)) {
1896                         // Render the control's position on the outer table
1897                         Attributes.AddAttributes(htmlWriter);
1898                         // Rendering a table around the div so that the designer sees it as a block
1899                         htmlWriter.RenderBeginTag(HtmlTextWriterTag.Table);
1900                         htmlWriter.RenderBeginTag(HtmlTextWriterTag.Tr);
1901                         htmlWriter.RenderBeginTag(HtmlTextWriterTag.Td);
1902 
1903                         dynamicSubMenu.Render(htmlWriter, true, false, false);
1904 
1905                         htmlWriter.RenderEndTag();
1906                         htmlWriter.RenderEndTag();
1907                         htmlWriter.RenderEndTag();
1908                         dictionary[_getDesignTimeDynamicHtml] = dynamicHtmlBuilder.ToString();
1909                     }
1910                 }
1911             }
1912             finally {
1913                 if (StaticDisplayLevels != oldStaticDisplayLevels) {
1914                     StaticDisplayLevels = oldStaticDisplayLevels;
1915                 }
1916             }
1917 
1918             return dictionary;
1919         }
1920 
GetDesignTimeWriter(StringWriter stringWriter)1921         private HtmlTextWriter GetDesignTimeWriter(StringWriter stringWriter) {
1922             if (_designTimeTextWriterType == null) {
1923                 return new HtmlTextWriter(stringWriter);
1924             }
1925             else {
1926                 Debug.Assert(_designTimeTextWriterType.IsSubclassOf(typeof(HtmlTextWriter)));
1927                 ConstructorInfo constructor = _designTimeTextWriterType.GetConstructor(new Type[] { typeof(TextWriter) });
1928                 if (constructor == null) {
1929                     return new HtmlTextWriter(stringWriter);
1930                 }
1931                 return (HtmlTextWriter)(constructor.Invoke(new object[] { stringWriter }));
1932             }
1933         }
1934 
1935 
1936         /// <devdoc>
1937         ///     Gets the URL for the specified image, properly pathing the image filename depending on which image it is
1938         /// </devdoc>
GetImageUrl(int index)1939         internal string GetImageUrl(int index) {
1940             if (ImageUrls[index] == null) {
1941                 switch (index) {
1942                     case ScrollUpImageIndex:
1943                         ImageUrls[index] = ScrollUpImageUrlInternal;
1944                         break;
1945                     case ScrollDownImageIndex:
1946                         ImageUrls[index] = ScrollDownImageUrlInternal;
1947                         break;
1948                     case PopOutImageIndex:
1949                         ImageUrls[index] = PopoutImageUrlInternal;
1950                         break;
1951                 }
1952 
1953                 ImageUrls[index] = ResolveClientUrl(ImageUrls[index]);
1954             }
1955 
1956             return ImageUrls[index];
1957         }
1958 
GetMenuItemStyle(MenuItem item)1959         internal MenuItemStyle GetMenuItemStyle(MenuItem item) {
1960             if (item == null) {
1961                 throw new ArgumentNullException("item");
1962             }
1963 
1964             int depth = item.Depth;
1965             MenuItemStyle typedStyle = CacheGetItem<MenuItemStyle>(CachedMenuItemStyles, depth);
1966 
1967             if (!item.Selected && typedStyle != null) return typedStyle;
1968 
1969             if (typedStyle == null) {
1970                 typedStyle = new MenuItemStyle();
1971                 typedStyle.CopyFrom(RootMenuItemStyle);
1972                 if (depth < StaticDisplayLevels) {
1973                     if (_staticItemStyle != null) {
1974                         TreeView.GetMergedStyle(typedStyle, _staticItemStyle);
1975                     }
1976                 }
1977                 else if (depth >= StaticDisplayLevels && _dynamicItemStyle != null) {
1978                     TreeView.GetMergedStyle(typedStyle, _dynamicItemStyle);
1979                 }
1980                 if ((depth < LevelMenuItemStyles.Count) && (LevelMenuItemStyles[depth] != null)) {
1981                     TreeView.GetMergedStyle(typedStyle, LevelMenuItemStyles[depth]);
1982                 }
1983                 CacheSetItem<MenuItemStyle>(CachedMenuItemStyles, depth, typedStyle);
1984             }
1985 
1986             if (item.Selected) {
1987                 MenuItemStyle selectedStyle = new MenuItemStyle();
1988                 selectedStyle.CopyFrom(typedStyle);
1989                 if (depth < StaticDisplayLevels) {
1990                     if (_staticSelectedStyle != null) {
1991                         TreeView.GetMergedStyle(selectedStyle, _staticSelectedStyle);
1992                     }
1993                 }
1994                 else if (depth >= StaticDisplayLevels && _dynamicSelectedStyle != null) {
1995                     TreeView.GetMergedStyle(selectedStyle, _dynamicSelectedStyle);
1996                 }
1997                 if (depth < LevelSelectedStyles.Count && LevelSelectedStyles[depth] != null) {
1998                     TreeView.GetMergedStyle(selectedStyle, LevelSelectedStyles[depth]);
1999                 }
2000                 return selectedStyle;
2001             }
2002             return typedStyle;
2003         }
2004 
GetSubMenuCssClassName(MenuItem item)2005         internal string GetSubMenuCssClassName(MenuItem item) {
2006             if (item == null) {
2007                 throw new ArgumentNullException("item");
2008             }
2009 
2010             int nextDepth = item.Depth + 1;
2011             string baseClassName = CacheGetItem<string>(CachedSubMenuClassNames, nextDepth);
2012             if (baseClassName != null) return baseClassName;
2013 
2014             StringBuilder builder = new StringBuilder();
2015             if (nextDepth < StaticDisplayLevels) {
2016                 AppendMenuCssClassName(builder, _staticMenuStyle);
2017             }
2018             else {
2019                 SubMenuStyle subMenuStyle = _panelStyle as SubMenuStyle;
2020                 if (subMenuStyle != null) {
2021                     AppendMenuCssClassName(builder, subMenuStyle);
2022                 }
2023                 AppendMenuCssClassName(builder, _dynamicMenuStyle);
2024             }
2025             if ((nextDepth < LevelSubMenuStyles.Count) && (LevelSubMenuStyles[nextDepth] != null)) {
2026                 SubMenuStyle style = LevelSubMenuStyles[nextDepth] as SubMenuStyle;
2027                 AppendMenuCssClassName(builder, style);
2028             }
2029 
2030             baseClassName = builder.ToString().Trim();
2031             CacheSetItem<string>(CachedSubMenuClassNames, nextDepth, baseClassName);
2032             return baseClassName;
2033         }
2034 
GetSubMenuStyle(MenuItem item)2035         internal SubMenuStyle GetSubMenuStyle(MenuItem item) {
2036             if (item == null) {
2037                 throw new ArgumentNullException("item");
2038             }
2039 
2040             int nextDepth = item.Depth + 1;
2041             SubMenuStyle subMenuStyle = CacheGetItem<SubMenuStyle>(CachedSubMenuStyles, nextDepth);
2042 
2043             if (subMenuStyle != null) return subMenuStyle;
2044 
2045             int staticDisplayLevels = StaticDisplayLevels;
2046             if (nextDepth >= staticDisplayLevels && !DesignMode) {
2047                 subMenuStyle = new PopOutPanel.PopOutPanelStyle(Panel);
2048             }
2049             else {
2050                 subMenuStyle = new SubMenuStyle();
2051             }
2052             if (nextDepth < staticDisplayLevels) {
2053                 if (_staticMenuStyle != null) {
2054                     subMenuStyle.CopyFrom(_staticMenuStyle);
2055                 }
2056             }
2057             else if (nextDepth >= staticDisplayLevels && _dynamicMenuStyle != null) {
2058                 subMenuStyle.CopyFrom(_dynamicMenuStyle);
2059             }
2060             if (_levelStyles != null &&
2061                 _levelStyles.Count > nextDepth &&
2062                 _levelStyles[nextDepth] != null) {
2063                 TreeView.GetMergedStyle(subMenuStyle, _levelStyles[nextDepth]);
2064             }
2065 
2066             CacheSetItem<SubMenuStyle>(CachedSubMenuStyles, nextDepth, subMenuStyle);
2067             return subMenuStyle;
2068         }
2069 
EnsureRootMenuStyle()2070         internal void EnsureRootMenuStyle() {
2071             if (_rootMenuItemStyle == null) {
2072                 _rootMenuItemStyle = new Style();
2073                 _rootMenuItemStyle.Font.CopyFrom(Font);
2074                 if (!ForeColor.IsEmpty) {
2075                     _rootMenuItemStyle.ForeColor = ForeColor;
2076                 }
2077                 // Not defaulting to black anymore for not entirely satisfying but reasonable reasons (VSWhidbey 356729)
2078                 if (!ControlStyle.IsSet(System.Web.UI.WebControls.Style.PROP_FONT_UNDERLINE)) {
2079                     _rootMenuItemStyle.Font.Underline = false;
2080                 }
2081             }
2082         }
2083 
LoadControlState(object savedState)2084         protected internal override void LoadControlState(object savedState) {
2085             Pair state = savedState as Pair;
2086             if (state == null) {
2087                 base.LoadControlState(savedState);
2088                 return;
2089             }
2090             base.LoadControlState(state.First);
2091 
2092             _selectedItem = null;
2093             if (state.Second != null) {
2094                 string path = state.Second as string;
2095                 if (path != null) {
2096                     _selectedItem = Items.FindItem(path.Split(TreeView.InternalPathSeparator), 0);
2097                 }
2098             }
2099         }
2100 
LoadViewState(object state)2101         protected override void LoadViewState(object state) {
2102             if (state != null) {
2103                 object[] savedState = (object[])state;
2104 
2105                 if (savedState[1] != null) {
2106                     ((IStateManager)StaticMenuItemStyle).LoadViewState(savedState[1]);
2107                 }
2108 
2109                 if (savedState[2] != null) {
2110                     ((IStateManager)StaticSelectedStyle).LoadViewState(savedState[2]);
2111                 }
2112 
2113                 if (savedState[3] != null) {
2114                     ((IStateManager)StaticHoverStyle).LoadViewState(savedState[3]);
2115                 }
2116 
2117                 if (savedState[4] != null) {
2118                     ((IStateManager)StaticMenuStyle).LoadViewState(savedState[4]);
2119                 }
2120 
2121                 if (savedState[5] != null) {
2122                     ((IStateManager)DynamicMenuItemStyle).LoadViewState(savedState[5]);
2123                 }
2124 
2125                 if (savedState[6] != null) {
2126                     ((IStateManager)DynamicSelectedStyle).LoadViewState(savedState[6]);
2127                 }
2128 
2129                 if (savedState[7] != null) {
2130                     ((IStateManager)DynamicHoverStyle).LoadViewState(savedState[7]);
2131                 }
2132 
2133                 if (savedState[8] != null) {
2134                     ((IStateManager)DynamicMenuStyle).LoadViewState(savedState[8]);
2135                 }
2136 
2137                 if (savedState[9] != null) {
2138                     ((IStateManager)LevelMenuItemStyles).LoadViewState(savedState[9]);
2139                 }
2140 
2141                 if (savedState[10] != null) {
2142                     ((IStateManager)LevelSelectedStyles).LoadViewState(savedState[10]);
2143                 }
2144 
2145                 if (savedState[11] != null) {
2146                     ((IStateManager)LevelSubMenuStyles).LoadViewState(savedState[11]);
2147                 }
2148 
2149                 if (savedState[12] != null) {
2150                     ((IStateManager)Items).LoadViewState(savedState[12]);
2151                     if (!String.IsNullOrEmpty(DataSourceID) || DataSource != null) {
2152                         _dataBound = true;
2153                     }
2154                 }
2155 
2156                 // Restore the core viewstate last because some property changes here could
2157                 // necessitate item rebinding (for example, MaximumDynamicDisplayLevels)
2158                 if (savedState[0] != null) {
2159                     base.LoadViewState(savedState[0]);
2160                 }
2161             }
2162         }
2163 
OnBubbleEvent(object source, EventArgs e)2164         protected override bool OnBubbleEvent(object source, EventArgs e) {
2165             MenuEventArgs me = e as MenuEventArgs;
2166             if (me != null && StringUtil.EqualsIgnoreCase(me.CommandName, MenuItemClickCommandName)) {
2167 
2168                 // Do not take any postback into account if the menu is disabled.
2169                 if (!IsEnabled) return true;
2170 
2171                 OnMenuItemClick(me);
2172                 if (AdapterInternal != null) {
2173                     MenuAdapter menuAdapter = AdapterInternal as MenuAdapter;
2174                     if (menuAdapter != null) {
2175                         MenuItem mi = me.Item;
2176                         // Need to tell the adapter about bubbled click events
2177                         // for the templated case if the item has children
2178                         if (mi != null &&
2179                             mi.ChildItems.Count > 0 &&
2180                             mi.Depth + 1 >= StaticDisplayLevels) {
2181 
2182                             menuAdapter.SetPath(me.Item.InternalValuePath);
2183                         }
2184                     }
2185                 }
2186                 RaiseBubbleEvent(this, e);
2187                 return true;
2188             }
2189             if (e is CommandEventArgs) {
2190                 RaiseBubbleEvent(this, e);
2191                 return true;
2192             }
2193             return false;
2194         }
2195 
OnDataBinding(EventArgs e)2196         protected override void OnDataBinding(EventArgs e) {
2197             EnsureChildControls();
2198             base.OnDataBinding(e);
2199         }
2200 
OnInit(EventArgs e)2201         protected internal override void OnInit(EventArgs e) {
2202             base.OnInit(e);
2203             Page.RegisterRequiresControlState(this);
2204         }
2205 
2206 
OnMenuItemClick(MenuEventArgs e)2207         protected virtual void OnMenuItemClick(MenuEventArgs e) {
2208             SetSelectedItem(e.Item);
2209             MenuEventHandler handler = (MenuEventHandler)Events[_menuItemClickedEvent];
2210             if (handler != null) {
2211                 handler(this, e);
2212             }
2213         }
2214 
2215 
2216         /// <devdoc>
2217         ///     Raises the MenuItemDataBound event
2218         /// </devdoc>
OnMenuItemDataBound(MenuEventArgs e)2219         protected virtual void OnMenuItemDataBound(MenuEventArgs e) {
2220             MenuEventHandler handler = (MenuEventHandler)Events[_menuItemDataBoundEvent];
2221             if (handler != null) {
2222                 handler(this, e);
2223             }
2224         }
2225 
2226         /// <devdoc>
2227         ///     Overridden to register for postback, and if client script is enabled, renders out
2228         ///     the necessary script and hidden field to function.
2229         /// </devdoc>
OnPreRender(EventArgs e)2230         protected internal override void OnPreRender(EventArgs e) {
2231             base.OnPreRender(e);
2232 
2233             if (Items.Count > 0) {
2234                 Renderer.PreRender(IsEnabled);
2235             }
2236         }
2237 
2238         /// <devdoc>
2239         ///     Overridden to register for postback, and if client script is enabled, renders out
2240         ///     the necessary script and hidden field to function.
2241         /// </devdoc>
OnPreRender(EventArgs e, bool registerScript)2242         internal void OnPreRender(EventArgs e, bool registerScript) {
2243             base.OnPreRender(e);
2244 
2245             if (Items.Count > 0) {
2246                 Renderer.PreRender(registerScript);
2247             }
2248         }
2249 
2250         /// <devdoc>
2251         ///     Overridden to create all the items based on the datasource provided
2252         /// </devdoc>
PerformDataBinding()2253         protected internal override void PerformDataBinding() {
2254             base.PerformDataBinding();
2255 
2256             DataBindItem(RootItem);
2257 
2258             if (!DesignMode && _dataBound &&
2259                 String.IsNullOrEmpty(DataSourceID) && DataSource == null) {
2260 
2261                 Items.Clear();
2262                 Controls.Clear();
2263                 ClearChildViewState();
2264                 TrackViewState();
2265                 ChildControlsCreated = true;
2266                 return;
2267             }
2268 
2269             if (!String.IsNullOrEmpty(DataSourceID) ||
2270                 DataSource != null) {
2271 
2272                 Controls.Clear();
2273                 ClearChildState();
2274                 TrackViewState();
2275 
2276                 CreateChildControlsFromItems(true);
2277                 ChildControlsCreated = true;
2278                 _dataBound = true;
2279             }
2280             else if (!_subControlsDataBound) {
2281                 foreach (Control ctrl in Controls) {
2282                     ctrl.DataBind();
2283                 }
2284             }
2285             _subControlsDataBound = true;
2286         }
2287 
Render(HtmlTextWriter writer)2288         protected internal override void Render(HtmlTextWriter writer) {
2289             VerifyRenderingInServerForm();
2290 
2291             if (Items.Count > 0) {
2292                 Renderer.RenderBeginTag(writer, false);
2293                 Renderer.RenderContents(writer, false);
2294                 Renderer.RenderEndTag(writer, false);
2295             }
2296         }
2297 
RenderBeginTag(HtmlTextWriter writer)2298         public override void RenderBeginTag(HtmlTextWriter writer) {
2299             Renderer.RenderBeginTag(writer, false);
2300         }
2301 
RenderContents(HtmlTextWriter writer)2302         protected internal override void RenderContents(HtmlTextWriter writer) {
2303             Renderer.RenderContents(writer, false);
2304         }
2305 
RenderEndTag(HtmlTextWriter writer)2306         public override void RenderEndTag(HtmlTextWriter writer) {
2307             Renderer.RenderEndTag(writer, false);
2308         }
2309 
ResetCachedStyles()2310         internal void ResetCachedStyles() {
2311             // Reset all these cached values so things can pick up changes in the designer
2312             if (_dynamicItemStyle != null) {
2313                 _dynamicItemStyle.ResetCachedStyles();
2314             }
2315             if (_staticItemStyle != null) {
2316                 _staticItemStyle.ResetCachedStyles();
2317             }
2318             if (_dynamicSelectedStyle != null) {
2319                 _dynamicSelectedStyle.ResetCachedStyles();
2320             }
2321             if (_staticSelectedStyle != null) {
2322                 _staticSelectedStyle.ResetCachedStyles();
2323             }
2324             if (_staticHoverStyle != null) {
2325                 _staticHoverHyperLinkStyle = new HyperLinkStyle(_staticHoverStyle);
2326             }
2327             if (_dynamicHoverStyle != null) {
2328                 _dynamicHoverHyperLinkStyle = new HyperLinkStyle(_dynamicHoverStyle);
2329             }
2330 
2331             foreach (MenuItemStyle style in LevelMenuItemStyles) {
2332                 style.ResetCachedStyles();
2333             }
2334 
2335             foreach (MenuItemStyle style in LevelSelectedStyles) {
2336                 style.ResetCachedStyles();
2337             }
2338 
2339             if (_imageUrls != null) {
2340                 for (int i = 0; i < _imageUrls.Length; i++) {
2341                     _imageUrls[i] = null;
2342                 }
2343             }
2344 
2345             _cachedPopOutImageUrl = null;
2346             _cachedScrollDownImageUrl = null;
2347             _cachedScrollUpImageUrl = null;
2348             _cachedLevelsContainingCssClass = null;
2349             _cachedMenuItemClassNames = null;
2350             _cachedMenuItemHyperLinkClassNames = null;
2351             _cachedMenuItemStyles = null;
2352             _cachedSubMenuClassNames = null;
2353             _cachedSubMenuStyles = null;
2354         }
2355 
SaveControlState()2356         protected internal override object SaveControlState() {
2357             object baseState = base.SaveControlState();
2358             if (_selectedItem != null) {
2359                 return new Pair(baseState, _selectedItem.InternalValuePath);
2360             }
2361             else {
2362                 return baseState;
2363             }
2364         }
2365 
SaveViewState()2366         protected override object SaveViewState() {
2367             object[] state = new object[13];
2368 
2369             state[0] = base.SaveViewState();
2370 
2371             bool hasViewState = (state[0] != null);
2372 
2373             if (_staticItemStyle != null) {
2374                 state[1] = ((IStateManager)_staticItemStyle).SaveViewState();
2375                 hasViewState |= (state[1] != null);
2376             }
2377 
2378             if (_staticSelectedStyle != null) {
2379                 state[2] = ((IStateManager)_staticSelectedStyle).SaveViewState();
2380                 hasViewState |= (state[2] != null);
2381             }
2382 
2383             if (_staticHoverStyle != null) {
2384                 state[3] = ((IStateManager)_staticHoverStyle).SaveViewState();
2385                 hasViewState |= (state[3] != null);
2386             }
2387 
2388             if (_staticMenuStyle != null) {
2389                 state[4] = ((IStateManager)_staticMenuStyle).SaveViewState();
2390                 hasViewState |= (state[4] != null);
2391             }
2392 
2393             if (_dynamicItemStyle != null) {
2394                 state[5] = ((IStateManager)_dynamicItemStyle).SaveViewState();
2395                 hasViewState |= (state[5] != null);
2396             }
2397 
2398             if (_dynamicSelectedStyle != null) {
2399                 state[6] = ((IStateManager)_dynamicSelectedStyle).SaveViewState();
2400                 hasViewState |= (state[6] != null);
2401             }
2402 
2403             if (_dynamicHoverStyle != null) {
2404                 state[7] = ((IStateManager)_dynamicHoverStyle).SaveViewState();
2405                 hasViewState |= (state[7] != null);
2406             }
2407 
2408             if (_dynamicMenuStyle != null) {
2409                 state[8] = ((IStateManager)_dynamicMenuStyle).SaveViewState();
2410                 hasViewState |= (state[8] != null);
2411             }
2412 
2413             if (_levelMenuItemStyles != null) {
2414                 state[9] = ((IStateManager)_levelMenuItemStyles).SaveViewState();
2415                 hasViewState |= (state[9] != null);
2416             }
2417 
2418             if (_levelSelectedStyles != null) {
2419                 state[10] = ((IStateManager)_levelSelectedStyles).SaveViewState();
2420                 hasViewState |= (state[10] != null);
2421             }
2422 
2423             if (_levelStyles != null) {
2424                 state[11] = ((IStateManager)_levelStyles).SaveViewState();
2425                 hasViewState |= (state[11] != null);
2426             }
2427 
2428             state[12] = ((IStateManager)Items).SaveViewState();
2429             hasViewState |= (state[12] != null);
2430 
2431             if (hasViewState) {
2432                 return state;
2433             }
2434             else {
2435                 return null;
2436             }
2437         }
2438 
2439         [SecurityPermission(SecurityAction.Demand, Unrestricted = true)]
SetDesignModeState(IDictionary data)2440         protected override void SetDesignModeState(IDictionary data) {
2441             if (data.Contains("DesignTimeTextWriterType")) {
2442                 Type writerType = data["DesignTimeTextWriterType"] as Type;
2443                 if (writerType != null && writerType.IsSubclassOf(typeof(HtmlTextWriter))) {
2444                     _designTimeTextWriterType = writerType;
2445                 }
2446             }
2447             base.SetDesignModeState(data);
2448         }
2449 
2450 
2451         /// <devdoc>
2452         /// Allows a derived Menu to set the DataBound proprety on a node
2453         /// </devdoc>
SetItemDataBound(MenuItem node, bool dataBound)2454         protected void SetItemDataBound(MenuItem node, bool dataBound) {
2455             node.SetDataBound(dataBound);
2456         }
2457 
2458 
2459         /// <devdoc>
2460         /// Allows a derived Menu to set the DataItem on a node
2461         /// </devdoc>
SetItemDataItem(MenuItem node, object dataItem)2462         protected void SetItemDataItem(MenuItem node, object dataItem) {
2463             node.SetDataItem(dataItem);
2464         }
2465 
2466 
2467         /// <devdoc>
2468         /// Allows a derived Menu to set the DataPath on a node
2469         /// </devdoc>
SetItemDataPath(MenuItem node, string dataPath)2470         protected void SetItemDataPath(MenuItem node, string dataPath) {
2471             node.SetDataPath(dataPath);
2472         }
2473 
SetSelectedItem(MenuItem node)2474         internal void SetSelectedItem(MenuItem node) {
2475             Debug.Assert(node == null || node.Owner == this);
2476 
2477             if (_selectedItem != node) {
2478                 if (node != null) {
2479                     if (node.Depth >= MaximumDepth) {
2480                         throw new InvalidOperationException(SR.GetString(SR.Menu_InvalidDepth));
2481                     }
2482                     if (!(node.IsEnabledNoOwner && node.Selectable)) {
2483                         throw new InvalidOperationException(SR.GetString(SR.Menu_InvalidSelection));
2484                     }
2485                 }
2486 
2487                 // Unselect the previously selected item
2488                 if ((_selectedItem != null) && (_selectedItem.Selected)) {
2489                     _selectedItem.SetSelected(false);
2490                 }
2491                 _selectedItem = node;
2492                 // Notify the new selected item that it's now selected
2493                 if ((_selectedItem != null) && !_selectedItem.Selected) {
2494                     _selectedItem.SetSelected(true);
2495                 }
2496             }
2497         }
2498 
2499 
2500         /// <internalonly/>
2501         /// <devdoc>
2502         ///    Marks the starting point to begin tracking and saving changes to the
2503         ///    control as part of the control viewstate.
2504         /// </devdoc>
TrackViewState()2505         protected override void TrackViewState() {
2506             base.TrackViewState();
2507 
2508             if (_staticItemStyle != null) {
2509                 ((IStateManager)_staticItemStyle).TrackViewState();
2510             }
2511             if (_staticSelectedStyle != null) {
2512                 ((IStateManager)_staticSelectedStyle).TrackViewState();
2513             }
2514             if (_staticHoverStyle != null) {
2515                 ((IStateManager)_staticHoverStyle).TrackViewState();
2516             }
2517             if (_staticMenuStyle != null) {
2518                 ((IStateManager)_staticMenuStyle).TrackViewState();
2519             }
2520             if (_dynamicItemStyle != null) {
2521                 ((IStateManager)_dynamicItemStyle).TrackViewState();
2522             }
2523             if (_dynamicSelectedStyle != null) {
2524                 ((IStateManager)_dynamicSelectedStyle).TrackViewState();
2525             }
2526             if (_dynamicHoverStyle != null) {
2527                 ((IStateManager)_dynamicHoverStyle).TrackViewState();
2528             }
2529             if (_dynamicMenuStyle != null) {
2530                 ((IStateManager)_dynamicMenuStyle).TrackViewState();
2531             }
2532             if (_levelMenuItemStyles != null) {
2533                 ((IStateManager)_levelMenuItemStyles).TrackViewState();
2534             }
2535             if (_levelSelectedStyles != null) {
2536                 ((IStateManager)_levelSelectedStyles).TrackViewState();
2537             }
2538             if (_levelStyles != null) {
2539                 ((IStateManager)_levelStyles).TrackViewState();
2540             }
2541             if (_bindings != null) {
2542                 ((IStateManager)_bindings).TrackViewState();
2543             }
2544 
2545             ((IStateManager)Items).TrackViewState();
2546         }
2547 
VerifyRenderingInServerForm()2548         internal void VerifyRenderingInServerForm() {
2549             if (Page != null) {
2550                 Page.VerifyRenderingInServerForm(this);
2551             }
2552         }
2553 
2554         #region IPostBackEventHandler implementation
2555 
2556         /// <internalonly/>
IPostBackEventHandler.RaisePostBackEvent(string eventArgument)2557         void IPostBackEventHandler.RaisePostBackEvent(string eventArgument) {
2558             RaisePostBackEvent(eventArgument);
2559         }
2560 
RaisePostBackEvent(string eventArgument)2561         protected internal virtual void RaisePostBackEvent(string eventArgument) {
2562             ValidateEvent(UniqueID, eventArgument);
2563 
2564             // Do not take any postback into account if the menu is disabled.
2565             if (!IsEnabled) return;
2566 
2567             EnsureChildControls();
2568             if (AdapterInternal != null) {
2569                 IPostBackEventHandler pbeh = AdapterInternal as IPostBackEventHandler;
2570                 if (pbeh != null) {
2571                     pbeh.RaisePostBackEvent(eventArgument);
2572                 }
2573             }
2574             else {
2575                 InternalRaisePostBackEvent(eventArgument);
2576             }
2577         }
2578 
InternalRaisePostBackEvent(string eventArgument)2579         internal void InternalRaisePostBackEvent(string eventArgument) {
2580             if (eventArgument.Length == 0) {
2581                 return;
2582             }
2583 
2584             // Get the path of the node specified in the eventArgument
2585             string nodePath = HttpUtility.HtmlDecode(eventArgument);
2586             // Check the number of separator characters in the argument (should not be more than the max depth)
2587             int matches = 0;
2588             for (int i = 0; i < nodePath.Length; i++) {
2589                 if (nodePath[i] == TreeView.InternalPathSeparator) {
2590                     if (++matches >= MaximumDepth) {
2591                         throw new InvalidOperationException(SR.GetString(SR.Menu_InvalidDepth));
2592                     }
2593                 }
2594             }
2595             // Find that node in the tree
2596             MenuItem node = Items.FindItem(nodePath.Split(TreeView.InternalPathSeparator), 0);
2597 
2598             if (node != null) {
2599                 OnMenuItemClick(new MenuEventArgs(node));
2600             }
2601         }
2602         #endregion
2603     }
2604 }
2605