1 //------------------------------------------------------------------------------ 2 // <copyright file="MenuRendererStandards.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.Drawing; 11 using System.Globalization; 12 using System.Linq; 13 using System.Web.Util; 14 15 public partial class Menu { 16 /// <devdoc>The standards-compliant Menu renderer</devdoc> 17 internal class MenuRendererStandards : MenuRenderer { 18 private string _dynamicPopOutUrl; 19 private string _staticPopOutUrl; 20 MenuRendererStandards(Menu menu)21 public MenuRendererStandards(Menu menu) : base(menu) { } 22 23 private string DynamicPopOutUrl { 24 get { 25 if (_dynamicPopOutUrl == null) { 26 _dynamicPopOutUrl = GetDynamicPopOutImageUrl(); 27 } 28 return _dynamicPopOutUrl; 29 } 30 } 31 32 protected virtual string SpacerImageUrl { 33 get { 34 return Menu.SpacerImageUrl; 35 } 36 } 37 38 private string StaticPopOutUrl { 39 get { 40 if (_staticPopOutUrl == null) { 41 _staticPopOutUrl = GetStaticPopOutImageUrl(); 42 } 43 return _staticPopOutUrl; 44 } 45 } 46 AddScriptReference()47 private void AddScriptReference() { 48 string key = "_registerMenu_" + Menu.ClientID; 49 string initScript = String.Format(CultureInfo.InvariantCulture, 50 "<script type='text/javascript'>" + 51 "new Sys.WebForms.Menu({{ element: '{0}', disappearAfter: {1}, orientation: '{2}', tabIndex: {3}, disabled: {4} }});" + 52 "</script>", 53 Menu.ClientID, 54 Menu.DisappearAfter, 55 Menu.Orientation.ToString().ToLowerInvariant(), 56 Menu.TabIndex, 57 (!Menu.IsEnabled).ToString().ToLowerInvariant()); 58 59 if (Menu.Page.ScriptManager != null) { 60 Menu.Page.ScriptManager.RegisterClientScriptResource(Menu.Page, typeof(Menu), "MenuStandards.js"); 61 Menu.Page.ScriptManager.RegisterStartupScript(Menu, typeof(MenuRendererStandards), key, initScript, false); 62 } 63 else { 64 Menu.Page.ClientScript.RegisterClientScriptResource(Menu.Page, typeof(Menu), "MenuStandards.js"); 65 Menu.Page.ClientScript.RegisterStartupScript(typeof(MenuRendererStandards), key, initScript); 66 } 67 } 68 AddStyleBlock()69 private void AddStyleBlock() { 70 if (Menu.IncludeStyleBlock) { 71 Menu.Page.Header.Controls.Add(CreateStyleBlock()); 72 } 73 } 74 CreateStyleBlock()75 private StyleBlock CreateStyleBlock() { 76 StyleBlock styleBlock = new StyleBlock(); 77 Style rootMenuItemStyle = Menu.RootMenuItemStyle; 78 79 // drop the font and forecolor from the control style, those are applied 80 // to the anchors directly with rootMenuItemStyle. 81 Style menuStyle = null; 82 if (!Menu.ControlStyle.IsEmpty) { 83 menuStyle = new Style(); 84 menuStyle.CopyFrom(Menu.ControlStyle); 85 // Relative sizes should not be multiplied (VSWhidbey 457610) 86 menuStyle.Font.Reset(); 87 menuStyle.ForeColor = Color.Empty; 88 } 89 90 // Menu surrounding DIV style -- without ForeColor or Font, 91 // those are applied directly to the '#Menu a' selector. 92 93 styleBlock.AddStyleDefinition("#{0}", Menu.ClientID) 94 .AddStyles(menuStyle); 95 96 // Image styles 97 98 styleBlock.AddStyleDefinition("#{0} img.icon", Menu.ClientID) 99 .AddStyle(HtmlTextWriterStyle.BorderStyle, "none") 100 .AddStyle(HtmlTextWriterStyle.VerticalAlign, "middle"); 101 102 styleBlock.AddStyleDefinition("#{0} img.separator", Menu.ClientID) 103 .AddStyle(HtmlTextWriterStyle.BorderStyle, "none") 104 .AddStyle(HtmlTextWriterStyle.Display, "block"); 105 106 if (Menu.Orientation == Orientation.Horizontal) { 107 styleBlock.AddStyleDefinition("#{0} img.horizontal-separator", Menu.ClientID) 108 .AddStyle(HtmlTextWriterStyle.BorderStyle, "none") 109 .AddStyle(HtmlTextWriterStyle.VerticalAlign, "middle"); 110 } 111 112 // Menu styles 113 114 styleBlock.AddStyleDefinition("#{0} ul", Menu.ClientID) 115 .AddStyle("list-style", "none") 116 .AddStyle(HtmlTextWriterStyle.Margin, "0") 117 .AddStyle(HtmlTextWriterStyle.Padding, "0") 118 .AddStyle(HtmlTextWriterStyle.Width, "auto"); 119 120 styleBlock.AddStyleDefinition("#{0} ul.static", Menu.ClientID) 121 .AddStyles(Menu._staticMenuStyle); 122 123 var ulDynamic = styleBlock.AddStyleDefinition("#{0} ul.dynamic", Menu.ClientID) 124 .AddStyles(Menu._dynamicMenuStyle) 125 .AddStyle(HtmlTextWriterStyle.ZIndex, "1"); 126 127 if (Menu.DynamicHorizontalOffset != 0) { 128 ulDynamic.AddStyle(HtmlTextWriterStyle.MarginLeft, Menu.DynamicHorizontalOffset.ToString(CultureInfo.InvariantCulture) + "px"); 129 } 130 131 if (Menu.DynamicVerticalOffset != 0) { 132 ulDynamic.AddStyle(HtmlTextWriterStyle.MarginTop, Menu.DynamicVerticalOffset.ToString(CultureInfo.InvariantCulture) + "px"); 133 } 134 135 if (Menu._levelStyles != null) { 136 int index = 1; 137 138 foreach (SubMenuStyle style in Menu._levelStyles) { 139 styleBlock.AddStyleDefinition("#{0} ul.level{1}", Menu.ClientID, index++) 140 .AddStyles(style); 141 } 142 } 143 144 // Menu item styles 145 146 // MenuItems have a 14px default right padding. 147 // This is necessary to prevent the default (and the typical) popout image from going under 148 // the menu item text when it is one of the longer items in the parent menu. 149 // It is 'px' based since its based on the image size not font size. 150 styleBlock.AddStyleDefinition("#{0} a", Menu.ClientID) 151 .AddStyle(HtmlTextWriterStyle.WhiteSpace, "nowrap") 152 .AddStyle(HtmlTextWriterStyle.Display, "block") 153 .AddStyles(rootMenuItemStyle); 154 155 var menuItemStatic = styleBlock.AddStyleDefinition("#{0} a.static", Menu.ClientID); 156 if ((Menu.Orientation == Orientation.Horizontal) && 157 ((Menu._staticItemStyle == null) || (Menu._staticItemStyle.HorizontalPadding.IsEmpty))) { 158 menuItemStatic.AddStyle(HtmlTextWriterStyle.PaddingLeft, "0.15em") 159 .AddStyle(HtmlTextWriterStyle.PaddingRight, "0.15em"); 160 } 161 menuItemStatic.AddStyles(Menu._staticItemStyle); 162 163 if (Menu._staticItemStyle != null) { 164 menuItemStatic.AddStyles(Menu._staticItemStyle.HyperLinkStyle); 165 } 166 167 if (!String.IsNullOrEmpty(StaticPopOutUrl)) { 168 styleBlock.AddStyleDefinition("#{0} a.popout", Menu.ClientID) 169 .AddStyle("background-image", "url(\"" + Menu.ResolveClientUrl(StaticPopOutUrl).Replace("\"", "\\\"") + "\")") 170 .AddStyle("background-repeat", "no-repeat") 171 .AddStyle("background-position", "right center") 172 .AddStyle(HtmlTextWriterStyle.PaddingRight, "14px"); 173 } 174 175 if (!String.IsNullOrEmpty(DynamicPopOutUrl)) { 176 // Check if dynamic popout is the same as the static one, so there's no need for a separate rule 177 if (DynamicPopOutUrl != StaticPopOutUrl) { 178 styleBlock.AddStyleDefinition("#{0} a.popout-dynamic", Menu.ClientID) 179 .AddStyle("background", "url(\"" + Menu.ResolveClientUrl(DynamicPopOutUrl).Replace("\"", "\\\"") + "\") no-repeat right center") 180 .AddStyle(HtmlTextWriterStyle.PaddingRight, "14px"); 181 } 182 } 183 184 var styleBlockStyles = styleBlock.AddStyleDefinition("#{0} a.dynamic", Menu.ClientID) 185 .AddStyles(Menu._dynamicItemStyle); 186 if (Menu._dynamicItemStyle != null) { 187 styleBlockStyles.AddStyles(Menu._dynamicItemStyle.HyperLinkStyle); 188 } 189 190 if (Menu._levelMenuItemStyles != null || Menu.StaticDisplayLevels > 1) { 191 int lastIndex = Menu.StaticDisplayLevels; 192 if (Menu._levelMenuItemStyles != null) { 193 lastIndex = Math.Max(lastIndex, Menu._levelMenuItemStyles.Count); 194 } 195 196 for (int index = 0; index < lastIndex; ++index) { 197 var style = styleBlock.AddStyleDefinition("#{0} a.level{1}", Menu.ClientID, index + 1); 198 199 if (index > 0 && index < Menu.StaticDisplayLevels) { 200 Unit indent = Menu.StaticSubMenuIndent; 201 202 // The default value of Menu.StaticSubMenuIndent is Unit.Empty, and the effective default value 203 // for list rendering is either 1em (vertical) or empty (horizontal). 204 if (indent.IsEmpty && Menu.Orientation == Orientation.Vertical) { 205 indent = new Unit(1, UnitType.Em); 206 } 207 208 if (!indent.IsEmpty && indent.Value != 0) { 209 double indentValue = indent.Value * index; 210 if (indentValue < Unit.MaxValue) { 211 indent = new Unit(indentValue, indent.Type); 212 } 213 else { 214 indent = new Unit(Unit.MaxValue, indent.Type); 215 } 216 217 style.AddStyle(HtmlTextWriterStyle.PaddingLeft, indent.ToString(CultureInfo.InvariantCulture)); 218 } 219 } 220 221 if (Menu._levelMenuItemStyles != null && index < Menu._levelMenuItemStyles.Count) { 222 var levelItemStyle = Menu._levelMenuItemStyles[index]; 223 style.AddStyles(levelItemStyle).AddStyles(levelItemStyle.HyperLinkStyle); 224 } 225 } 226 } 227 228 styleBlockStyles = styleBlock.AddStyleDefinition("#{0} a.static.selected", Menu.ClientID) 229 .AddStyles(Menu._staticSelectedStyle); 230 if (Menu._staticSelectedStyle != null) { 231 styleBlockStyles.AddStyles(Menu._staticSelectedStyle.HyperLinkStyle); 232 } 233 234 styleBlockStyles = styleBlock.AddStyleDefinition("#{0} a.dynamic.selected", Menu.ClientID) 235 .AddStyles(Menu._dynamicSelectedStyle); 236 if (Menu._dynamicSelectedStyle != null) { 237 styleBlockStyles.AddStyles(Menu._dynamicSelectedStyle.HyperLinkStyle); 238 } 239 240 styleBlock.AddStyleDefinition("#{0} a.static.highlighted", Menu.ClientID) 241 .AddStyles(Menu._staticHoverStyle); 242 243 styleBlock.AddStyleDefinition("#{0} a.dynamic.highlighted", Menu.ClientID) 244 .AddStyles(Menu._dynamicHoverStyle); 245 246 if (Menu._levelSelectedStyles != null) { 247 int index = 1; 248 249 foreach (MenuItemStyle style in Menu._levelSelectedStyles) { 250 styleBlock.AddStyleDefinition("#{0} a.selected.level{1}", Menu.ClientID, index++) 251 .AddStyles(style).AddStyles(style.HyperLinkStyle); 252 } 253 } 254 255 return styleBlock; 256 } 257 GetCssClass(int level, Style staticStyle, Style dynamicStyle, IList levelStyles)258 private string GetCssClass(int level, Style staticStyle, Style dynamicStyle, IList levelStyles) { 259 string result = "level" + level; 260 Style style; 261 262 if (level > Menu.StaticDisplayLevels) { 263 style = dynamicStyle; 264 } 265 else { 266 if (Menu.DesignMode) { 267 result += " static"; 268 if (Menu.Orientation == Orientation.Horizontal) { 269 result += " horizontal"; 270 } 271 } 272 273 style = staticStyle; 274 } 275 276 if (style != null && !String.IsNullOrEmpty(style.CssClass)) { 277 result += " " + style.CssClass; 278 } 279 280 if (levelStyles != null && levelStyles.Count >= level) { 281 Style levelStyle = (Style)levelStyles[level - 1]; 282 283 if (levelStyle != null && !String.IsNullOrEmpty(levelStyle.CssClass)) { 284 result += " " + levelStyle.CssClass; 285 } 286 } 287 288 return result; 289 } 290 GetDynamicPopOutImageUrl()291 protected virtual string GetDynamicPopOutImageUrl() { 292 string url = Menu.DynamicPopOutImageUrl; 293 if (String.IsNullOrEmpty(url) && Menu.DynamicEnableDefaultPopOutImage) { 294 url = Menu.GetImageUrl(Menu.PopOutImageIndex); 295 } 296 return url; 297 } 298 GetStaticPopOutImageUrl()299 protected virtual string GetStaticPopOutImageUrl() { 300 string url = Menu.StaticPopOutImageUrl; 301 if (String.IsNullOrEmpty(url) && Menu.StaticEnableDefaultPopOutImage) { 302 url = Menu.GetImageUrl(Menu.PopOutImageIndex); 303 } 304 return url; 305 } 306 GetMenuCssClass(int level)307 private string GetMenuCssClass(int level) { 308 return GetCssClass(level, Menu.StaticMenuStyle, Menu.DynamicMenuStyle, Menu._levelStyles); 309 } 310 GetMenuItemCssClass(MenuItem item, int level)311 private string GetMenuItemCssClass(MenuItem item, int level) { 312 // give the A the proper popout class 313 string cssClass = null; 314 if (ShouldHavePopOutImage(item)) { 315 if (level > Menu.StaticDisplayLevels) { 316 if (!String.IsNullOrEmpty(DynamicPopOutUrl)) { 317 cssClass = (DynamicPopOutUrl == StaticPopOutUrl) ? "popout" : "popout-dynamic"; 318 } 319 } 320 else if (!String.IsNullOrEmpty(StaticPopOutUrl)) { 321 cssClass = "popout"; 322 } 323 } 324 string levelCssClass = GetCssClass(level, Menu.StaticMenuItemStyle, Menu.DynamicMenuItemStyle, Menu._levelMenuItemStyles); 325 if (!String.IsNullOrEmpty(cssClass)) { 326 return cssClass + " " + levelCssClass; 327 } 328 else { 329 return levelCssClass; 330 } 331 } 332 GetPostBackEventReference(MenuItem item)333 protected virtual string GetPostBackEventReference(MenuItem item) { 334 return Menu.Page.ClientScript.GetPostBackEventReference(Menu, item.InternalValuePath, true); 335 } 336 IsChildPastMaximumDepth(MenuItem item)337 private bool IsChildPastMaximumDepth(MenuItem item) { 338 return (item.Depth + 1 >= Menu.MaximumDepth); 339 } 340 IsChildDepthDynamic(MenuItem item)341 private bool IsChildDepthDynamic(MenuItem item) { 342 return (item.Depth + 1 >= Menu.StaticDisplayLevels); 343 } 344 IsDepthDynamic(MenuItem item)345 private bool IsDepthDynamic(MenuItem item) { 346 // Depth is 0 based. StaticDisplayLevels is 1 based because it is a counter 347 // (1 means show "one level" statically -- so, show Depth 0 statically but not Depth 1). 348 // Therefore, it is dynamic if the item's depth is greater than or equal to the static levels. 349 // Depth = 2 (3rd level), StaticDisplayLevels = 3 ==> Static 350 // Depth = 2 (3rd level), StaticDisplayLevels = 2 ==> Dynamic 351 return (item.Depth >= Menu.StaticDisplayLevels); 352 } 353 IsDepthStatic(MenuItem item)354 private bool IsDepthStatic(MenuItem item) { 355 return !IsDepthDynamic(item); 356 } 357 PreRender(bool registerScript)358 public override void PreRender(bool registerScript) { 359 if (Menu.DesignMode || Menu.Page == null) { 360 return; 361 } 362 363 if (Menu.IncludeStyleBlock && Menu.Page.Header == null) { 364 throw new InvalidOperationException(SR.GetString(SR.NeedHeader, "Menu.IncludeStyleBlock")); 365 } 366 367 AddScriptReference(); // We always need our script, even if we're disabled, because the script sets our styles 368 AddStyleBlock(); 369 } 370 RenderBeginTag(HtmlTextWriter writer, bool staticOnly)371 public override void RenderBeginTag(HtmlTextWriter writer, bool staticOnly) { 372 ControlRenderingHelper.WriteSkipLinkStart(writer, Menu.RenderingCompatibility, Menu.DesignMode, Menu.SkipLinkText, SpacerImageUrl, Menu.ClientID); 373 374 if (Menu.DesignMode && Menu.IncludeStyleBlock) { 375 // Need to render style block in design mode, since it won't be present 376 CreateStyleBlock().Render(writer); 377 } 378 379 // Add expando attributes 380 if (Menu.HasAttributes) { 381 foreach (string key in Menu.Attributes.Keys) { 382 writer.AddAttribute(key, Menu.Attributes[key]); 383 } 384 } 385 386 // CSS class, including disabled class if it's set 387 string cssClass = Menu.CssClass ?? ""; 388 if (!Menu.Enabled) { 389 cssClass = (cssClass + " " + DisabledCssClass).Trim(); 390 } 391 if (!String.IsNullOrEmpty(cssClass)) { 392 writer.AddAttribute(HtmlTextWriterAttribute.Class, cssClass); 393 } 394 395 // Need to simulate the float done by Javascript when we're in design mode 396 if (Menu.DesignMode) { 397 writer.AddStyleAttribute("float", "left"); 398 } 399 400 writer.AddAttribute(HtmlTextWriterAttribute.Id, Menu.ClientID); 401 writer.RenderBeginTag(HtmlTextWriterTag.Div); 402 } 403 RenderContents(HtmlTextWriter writer, bool staticOnly)404 public override void RenderContents(HtmlTextWriter writer, bool staticOnly) { 405 RenderItems(writer, staticOnly || Menu.DesignMode || !Menu.Enabled, Menu.Items, 1, !String.IsNullOrEmpty(Menu.AccessKey)); 406 } 407 RenderEndTag(HtmlTextWriter writer, bool staticOnly)408 public override void RenderEndTag(HtmlTextWriter writer, bool staticOnly) { 409 writer.RenderEndTag(); 410 411 // Need to simulate the clear done by Javascript when we're in design mode 412 if (Menu.DesignMode) { 413 writer.AddAttribute(HtmlTextWriterAttribute.Style, "clear: left"); 414 writer.RenderBeginTag(HtmlTextWriterTag.Div); 415 writer.RenderEndTag(); 416 } 417 418 ControlRenderingHelper.WriteSkipLinkEnd(writer, Menu.DesignMode, Menu.SkipLinkText, Menu.ClientID); 419 } 420 RenderItem(HtmlTextWriter writer, MenuItem item, int level, string cssClass, bool needsAccessKey)421 private bool RenderItem(HtmlTextWriter writer, MenuItem item, int level, string cssClass, bool needsAccessKey) { 422 RenderItemPreSeparator(writer, item); 423 424 if (Menu.DesignMode && Menu.Orientation == Orientation.Horizontal) { 425 writer.AddStyleAttribute(HtmlTextWriterStyle.WhiteSpace, "nowrap"); 426 } 427 needsAccessKey = RenderItemLinkAttributes(writer, item, level, cssClass, needsAccessKey); 428 writer.RenderBeginTag(HtmlTextWriterTag.A); 429 RenderItemIcon(writer, item); 430 item.RenderText(writer); 431 // popout image is in the A's background css 432 writer.RenderEndTag(); // </a> 433 434 RenderItemPostSeparator(writer, item); 435 436 return needsAccessKey; 437 } 438 RenderItemIcon(HtmlTextWriter writer, MenuItem item)439 private void RenderItemIcon(HtmlTextWriter writer, MenuItem item) { 440 if (String.IsNullOrEmpty(item.ImageUrl) || !item.NotTemplated()) { 441 return; 442 } 443 444 writer.AddAttribute(HtmlTextWriterAttribute.Src, Menu.ResolveClientUrl(item.ImageUrl)); 445 writer.AddAttribute(HtmlTextWriterAttribute.Alt, item.ToolTip); 446 writer.AddAttribute(HtmlTextWriterAttribute.Title, item.ToolTip); 447 writer.AddAttribute(HtmlTextWriterAttribute.Class, "icon"); 448 writer.RenderBeginTag(HtmlTextWriterTag.Img); 449 writer.RenderEndTag(); 450 } 451 RenderItemLinkAttributes(HtmlTextWriter writer, MenuItem item, int level, string cssClass, bool needsAccessKey)452 private bool RenderItemLinkAttributes(HtmlTextWriter writer, MenuItem item, int level, string cssClass, bool needsAccessKey) { 453 if (!String.IsNullOrEmpty(item.ToolTip)) { 454 writer.AddAttribute(HtmlTextWriterAttribute.Title, item.ToolTip); 455 } 456 457 // Bail out for for disabled or non-selectable menu items 458 if (!item.Enabled || !Menu.Enabled) { 459 writer.AddAttribute(HtmlTextWriterAttribute.Class, cssClass + " " + DisabledCssClass); 460 return needsAccessKey; 461 } 462 if (!item.Selectable) { 463 writer.AddAttribute(HtmlTextWriterAttribute.Class, cssClass); 464 return needsAccessKey; 465 } 466 467 // Selected 468 if (item.Selected) { 469 cssClass += " selected"; 470 } 471 writer.AddAttribute(HtmlTextWriterAttribute.Class, cssClass); 472 473 // Attach the access key to the first link we render 474 if (needsAccessKey) { 475 writer.AddAttribute(HtmlTextWriterAttribute.Accesskey, Menu.AccessKey); 476 } 477 478 // Postback... 479 if (String.IsNullOrEmpty(item.NavigateUrl)) { 480 writer.AddAttribute(HtmlTextWriterAttribute.Href, "#"); 481 writer.AddAttribute(HtmlTextWriterAttribute.Onclick, GetPostBackEventReference(item)); 482 } 483 // ...or direct link 484 else { 485 writer.AddAttribute(HtmlTextWriterAttribute.Href, Menu.ResolveClientUrl(item.NavigateUrl)); 486 487 string target = item.Target; 488 if (String.IsNullOrEmpty(target)) { 489 target = Menu.Target; 490 } 491 if (!String.IsNullOrEmpty(target)) { 492 writer.AddAttribute(HtmlTextWriterAttribute.Target, target); 493 } 494 } 495 496 return false; 497 } 498 RenderItemPostSeparator(HtmlTextWriter writer, MenuItem item)499 private void RenderItemPostSeparator(HtmlTextWriter writer, MenuItem item) { 500 string separatorImageUrl = item.SeparatorImageUrl; 501 if (String.IsNullOrEmpty(separatorImageUrl)) { 502 separatorImageUrl = IsDepthStatic(item) 503 ? Menu.StaticBottomSeparatorImageUrl 504 : Menu.DynamicBottomSeparatorImageUrl; 505 } 506 507 if (!String.IsNullOrEmpty(separatorImageUrl)) { 508 RenderItemSeparatorImage(writer, item, separatorImageUrl); 509 } 510 } 511 RenderItemPreSeparator(HtmlTextWriter writer, MenuItem item)512 private void RenderItemPreSeparator(HtmlTextWriter writer, MenuItem item) { 513 string separatorImageUrl = IsDepthStatic(item) 514 ? Menu.StaticTopSeparatorImageUrl 515 : Menu.DynamicTopSeparatorImageUrl; 516 517 if (!String.IsNullOrEmpty(separatorImageUrl)) { 518 RenderItemSeparatorImage(writer, item, separatorImageUrl); 519 } 520 } 521 RenderItemSeparatorImage(HtmlTextWriter writer, MenuItem item, string separatorImageUrl)522 private void RenderItemSeparatorImage(HtmlTextWriter writer, MenuItem item, string separatorImageUrl) { 523 if (Menu.RenderingCompatibility >= VersionUtil.Framework45) { 524 // Dev10 #867750, Dev11 #436206: We need to be consistent with other controls by calling ResolveClientUrl, 525 // but we need to hide this behavior behind a compat switch so that upgrading to 4.5 doesn't break 526 // customers who have implemented their own manual workaround. 527 separatorImageUrl = Menu.ResolveClientUrl(separatorImageUrl); 528 } 529 530 writer.AddAttribute(HtmlTextWriterAttribute.Src, separatorImageUrl); 531 writer.AddAttribute(HtmlTextWriterAttribute.Alt, String.Empty); 532 writer.AddAttribute(HtmlTextWriterAttribute.Class, 533 IsDepthStatic(item) && Menu.Orientation == Orientation.Horizontal ? "horizontal-separator" : "separator"); 534 writer.RenderBeginTag(HtmlTextWriterTag.Img); 535 writer.RenderEndTag(); 536 } 537 RenderItems(HtmlTextWriter writer, bool staticOnly, MenuItemCollection items, int level, bool needsAccessKey)538 private void RenderItems(HtmlTextWriter writer, bool staticOnly, MenuItemCollection items, int level, bool needsAccessKey) { 539 if (level == 1 || level > Menu.StaticDisplayLevels) { // Render a <UL> to start, and for all dynamic descendents 540 if (Menu.DesignMode && Menu.Orientation == Orientation.Horizontal) { 541 writer.AddStyleAttribute("float", "left"); 542 } 543 writer.AddAttribute(HtmlTextWriterAttribute.Class, GetMenuCssClass(level)); 544 writer.RenderBeginTag(HtmlTextWriterTag.Ul); 545 } 546 547 foreach (MenuItem item in items) { 548 if (Menu.DesignMode && Menu.Orientation == Orientation.Horizontal) { 549 writer.AddStyleAttribute("float", "left"); 550 writer.AddStyleAttribute(HtmlTextWriterStyle.WhiteSpace, "nowrap"); 551 } 552 writer.RenderBeginTag(HtmlTextWriterTag.Li); 553 554 needsAccessKey = RenderItem(writer, item, level, GetMenuItemCssClass(item, level), needsAccessKey); 555 556 if (level < Menu.StaticDisplayLevels) { // Close off <LI> if we (and our direct descendents) are static 557 writer.RenderEndTag(); 558 } 559 560 if (item.ChildItems.Count > 0 && !IsChildPastMaximumDepth(item) && item.Enabled) { 561 if (level < Menu.StaticDisplayLevels || !staticOnly) { 562 RenderItems(writer, staticOnly, item.ChildItems, level + 1, needsAccessKey); 563 } 564 } 565 566 if (level >= Menu.StaticDisplayLevels) { // Close off <LI> if we (or our direct descendents) are dynamic 567 writer.RenderEndTag(); 568 } 569 } 570 571 if (level == 1 || level > Menu.StaticDisplayLevels) { 572 writer.RenderEndTag(); 573 } 574 } 575 ShouldHavePopOutImage(MenuItem item)576 private bool ShouldHavePopOutImage(MenuItem item) { 577 return (item.ChildItems.Count > 0) && IsChildDepthDynamic(item) && !IsChildPastMaximumDepth(item); 578 } 579 } 580 } 581 582 internal class StyleBlock : Control { 583 List<StyleBlockStyles> _styles = new List<StyleBlockStyles>(); 584 AddStyleDefinition(string selector)585 public StyleBlockStyles AddStyleDefinition(string selector) { 586 StyleBlockStyles result = new StyleBlockStyles(selector, this); 587 _styles.Add(result); 588 return result; 589 } 590 AddStyleDefinition(string selectorFormat, params object[] args)591 public StyleBlockStyles AddStyleDefinition(string selectorFormat, params object[] args) { 592 return AddStyleDefinition(String.Format(CultureInfo.InvariantCulture, selectorFormat, args)); 593 } 594 Render(HtmlTextWriter writer)595 protected internal override void Render(HtmlTextWriter writer) { 596 if (_styles.Any(s => !s.Empty)) { 597 writer.AddAttribute(HtmlTextWriterAttribute.Type, "text/css"); 598 writer.RenderBeginTag(HtmlTextWriterTag.Style); 599 writer.WriteLine("/* <![CDATA[ */"); 600 601 foreach (var style in _styles.Where(s => !s.Empty)) { 602 style.Render(writer); 603 } 604 605 writer.Write("/* ]]> */"); 606 writer.RenderEndTag(); 607 } 608 } 609 } 610 611 internal class StyleBlockStyles { 612 private string _selector; 613 private StyleBlock _styleControl; 614 private CssStyleCollection _styles = new CssStyleCollection(); 615 StyleBlockStyles(string selector, StyleBlock styleControl)616 public StyleBlockStyles(string selector, StyleBlock styleControl) { 617 _selector = selector; 618 _styleControl = styleControl; 619 } 620 621 public bool Empty { 622 get { return _styles.Count == 0; } 623 } 624 AddStyle(HtmlTextWriterStyle styleName, string value)625 public StyleBlockStyles AddStyle(HtmlTextWriterStyle styleName, string value) { 626 _styles.Add(styleName, value); 627 return this; 628 } 629 AddStyle(string styleName, string value)630 public StyleBlockStyles AddStyle(string styleName, string value) { 631 _styles.Add(styleName, value); 632 return this; 633 } 634 AddStyles(Style style)635 public StyleBlockStyles AddStyles(Style style) { 636 if (style != null) { 637 AddStyles(style.GetStyleAttributes(_styleControl)); 638 } 639 return this; 640 } 641 AddStyles(CssStyleCollection styles)642 public StyleBlockStyles AddStyles(CssStyleCollection styles) { 643 if (styles != null) { 644 foreach (string key in styles.Keys) { 645 _styles.Add(key, styles[key]); 646 } 647 } 648 return this; 649 } 650 Render(HtmlTextWriter writer)651 public void Render(HtmlTextWriter writer) { 652 writer.WriteLine("{0} {{ {1} }}", _selector, _styles.Value); 653 } 654 } 655 } 656 657