MenuListRenderer.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  1. //
  2. // Authors:
  3. // Marek Habersack <[email protected]>
  4. //
  5. // (C) 2010 Novell, Inc (http://novell.com)
  6. //
  7. //
  8. // Permission is hereby granted, free of charge, to any person obtaining
  9. // a copy of this software and associated documentation files (the
  10. // "Software"), to deal in the Software without restriction, including
  11. // without limitation the rights to use, copy, modify, merge, publish,
  12. // distribute, sublicense, and/or sell copies of the Software, and to
  13. // permit persons to whom the Software is furnished to do so, subject to
  14. // the following conditions:
  15. //
  16. // The above copyright notice and this permission notice shall be
  17. // included in all copies or substantial portions of the Software.
  18. //
  19. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  20. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  21. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  22. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  23. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  24. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  25. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  26. //
  27. using System;
  28. using System.Collections;
  29. using System.Collections.Generic;
  30. using System.Drawing;
  31. using System.Globalization;
  32. using System.Text;
  33. using System.Web;
  34. using System.Web.UI;
  35. using System.Web.UI.HtmlControls;
  36. namespace System.Web.UI.WebControls
  37. {
  38. sealed class MenuListRenderer : BaseMenuRenderer
  39. {
  40. bool haveDynamicPopOut;
  41. public override HtmlTextWriterTag Tag {
  42. get { return HtmlTextWriterTag.Div; }
  43. }
  44. public MenuListRenderer (Menu owner)
  45. : base (owner)
  46. {
  47. }
  48. public override void PreRender (Page page, HtmlHead head, ClientScriptManager csm, string cmenu, StringBuilder script)
  49. {
  50. Menu owner = Owner;
  51. script.AppendFormat ("new Sys.WebForms.Menu ({{ element: '{0}', disappearAfter: {1}, orientation: '{2}', tabIndex: {3}, disabled: {4} }});",
  52. owner.ClientID,
  53. ClientScriptManager.GetScriptLiteral (owner.DisappearAfter),
  54. owner.Orientation.ToString ().ToLowerInvariant (),
  55. ClientScriptManager.GetScriptLiteral (owner.TabIndex),
  56. (!owner.Enabled).ToString ().ToLowerInvariant ());
  57. Type mt = typeof (Menu);
  58. if (!csm.IsClientScriptIncludeRegistered (mt, "MenuModern.js")) {
  59. string url = csm.GetWebResourceUrl (mt, "MenuModern.js");
  60. csm.RegisterClientScriptInclude (mt, "MenuModern.js", url);
  61. }
  62. if (!owner.IncludeStyleBlock)
  63. return;
  64. if (head == null)
  65. throw new InvalidOperationException ("Using Menu.IncludeStyleBlock requires Page.Header to be non-null (e.g. <head runat=\"server\" />).");
  66. StyleBlock block = new StyleBlock (owner.ClientID);
  67. Style style = owner.ControlStyle;
  68. bool horizontal = owner.Orientation == Orientation.Horizontal;
  69. if (style != null)
  70. block.RegisterStyle (style);
  71. // #MenuId img.icon { border-style:none;vertical-align:middle; }
  72. block.RegisterStyle (HtmlTextWriterStyle.BorderStyle, "none", "img.icon")
  73. .Add (HtmlTextWriterStyle.VerticalAlign, "middle");
  74. // #MenuId img.separator { border-style:none;display:block; }
  75. block.RegisterStyle (HtmlTextWriterStyle.BorderStyle, "none", "img.separator")
  76. .Add (HtmlTextWriterStyle.Display, "block");
  77. // #MenuId img.horizontal-separator { border-style:none;vertical-align:middle; }
  78. if (horizontal)
  79. block.RegisterStyle (HtmlTextWriterStyle.BorderStyle, "none", "img.horizontal-separator")
  80. .Add (HtmlTextWriterStyle.VerticalAlign, "middle");
  81. // #MenuId ul { list-style:none;margin:0;padding:0;width:auto; }
  82. block.RegisterStyle (HtmlTextWriterStyle.ListStyleType, "none", "ul")
  83. .Add (HtmlTextWriterStyle.Margin, "0")
  84. .Add (HtmlTextWriterStyle.Padding, "0")
  85. .Add (HtmlTextWriterStyle.Width, "auto");
  86. SubMenuStyle sms = owner.StaticMenuStyleInternal;
  87. if (sms != null) {
  88. // #MenuId ul.static { ... }
  89. block.RegisterStyle (sms, "ul.static");
  90. }
  91. // #MenuId ul.dynamic { ...; z-index:1; ... }
  92. NamedCssStyleCollection css = block.RegisterStyle ("ul.dynamic");
  93. sms = owner.DynamicMenuStyleInternal;
  94. if (sms != null) {
  95. sms.ForeColor = Color.Empty;
  96. css.Add (sms);
  97. }
  98. css.Add (HtmlTextWriterStyle.ZIndex, "1");
  99. int num = owner.DynamicHorizontalOffset;
  100. if (num != 0)
  101. css.Add (HtmlTextWriterStyle.MarginLeft, num + "px");
  102. num = owner.DynamicVerticalOffset;
  103. if (num != 0)
  104. css.Add (HtmlTextWriterStyle.MarginTop, num + "px");
  105. // BUG: rendering of LevelSubMenuStyles throws InvalidCastException on .NET
  106. // but I suspect the code it is supposed to generate is as follows:
  107. //
  108. // #MenuId ul.levelX { ... }
  109. //
  110. // so we will just ignore the bug and go with the above code.
  111. RenderLevelStyles (block, num, owner.LevelSubMenuStyles, "ul.level");
  112. // #MenuId a { text-decoration:none;white-space:nowrap;display:block; }
  113. block.RegisterStyle (HtmlTextWriterStyle.TextDecoration, "none", "a")
  114. .Add (HtmlTextWriterStyle.WhiteSpace, "nowrap")
  115. .Add (HtmlTextWriterStyle.Display, "block");
  116. // #MenuId a.static { ... }
  117. RenderAnchorStyle (block, owner.StaticMenuItemStyleInternal, "a.static");
  118. // #MenuId a.popout { background-image:url("...");background-repeat:no-repeat;background-position:right center;padding-right:14px; }
  119. bool needDynamicPopOut = false;
  120. string str = owner.StaticPopOutImageUrl;
  121. css = null;
  122. string urlFormat = "url(\"{0}\")";
  123. if (String.IsNullOrEmpty (str)) {
  124. if (owner.StaticEnableDefaultPopOutImage)
  125. css = block.RegisterStyle (HtmlTextWriterStyle.BackgroundImage, String.Format (urlFormat, GetArrowResourceUrl (owner)), "a.popout");
  126. else
  127. needDynamicPopOut = true;
  128. } else {
  129. css = block.RegisterStyle (HtmlTextWriterStyle.BackgroundImage, String.Format (urlFormat, str), "a.popout");
  130. needDynamicPopOut = true;
  131. }
  132. if (css != null)
  133. css.Add ("background-repeat", "no-repeat")
  134. .Add ("background-position", "right center")
  135. .Add (HtmlTextWriterStyle.PaddingRight, "14px");
  136. // #MenuId a.popout-dynamic { background:url("...") no-repeat right center;padding-right:14px; }
  137. str = owner.DynamicPopOutImageUrl;
  138. bool haveDynamicUrl = !String.IsNullOrEmpty (str);
  139. css = null;
  140. if (needDynamicPopOut || haveDynamicUrl) {
  141. urlFormat = "url(\"{0}\") no-repeat right center";
  142. if (!haveDynamicUrl) {
  143. if (owner.DynamicEnableDefaultPopOutImage)
  144. css = block.RegisterStyle (HtmlTextWriterStyle.BackgroundImage, String.Format (urlFormat, GetArrowResourceUrl (owner)), "a.popout-dynamic");
  145. } else
  146. css = block.RegisterStyle (HtmlTextWriterStyle.BackgroundImage, String.Format (urlFormat, str), "a.popout-dynamic");
  147. }
  148. if (css != null) {
  149. haveDynamicPopOut = true;
  150. css.Add (HtmlTextWriterStyle.PaddingRight, "14px");
  151. }
  152. // #MenuId a.dynamic { ... }
  153. RenderAnchorStyle (block, owner.DynamicMenuItemStyleInternal, "a.dynamic");
  154. num = owner.StaticDisplayLevels;
  155. Unit ssmi = owner.StaticSubMenuIndent;
  156. string unitName;
  157. double indent;
  158. if (ssmi == Unit.Empty) {
  159. unitName = "em";
  160. indent = 1;
  161. } else {
  162. unitName = Unit.GetExtension (ssmi.Type);
  163. indent = ssmi.Value;
  164. }
  165. // #MenuId a.levelX { ... }
  166. RenderLevelStyles (block, num, owner.LevelMenuItemStyles, "a.level", unitName, indent);
  167. // #MenuId a.selected.levelX { ... }
  168. RenderLevelStyles (block, num, owner.LevelSelectedStyles, "a.selected.level");
  169. // #MenuId a.static.selected { ...;text-decoration:none; }
  170. RenderAnchorStyle (block, owner.StaticSelectedStyleInternal, "a.static.selected");
  171. // #MenuId a.dynamic.selected { ...;text-decoration:none;border-style:none; }
  172. RenderAnchorStyle (block, owner.DynamicSelectedStyleInternal, "a.dynamic.selected");
  173. // #MenuId a.static.highlighted { ... }
  174. style = owner.StaticHoverStyleInternal;
  175. if (style != null)
  176. block.RegisterStyle (style, "a.static.highlighted");
  177. // #MenuId a.dynamic.highlighted { ... }
  178. style = owner.DynamicHoverStyleInternal;
  179. if (style != null)
  180. block.RegisterStyle (style, "a.dynamic.highlighted");
  181. head.Controls.Add (block);
  182. }
  183. public override void RenderBeginTag (HtmlTextWriter writer, string skipLinkText)
  184. {
  185. Menu owner = Owner;
  186. // <a href="#ID_SkipLink">
  187. writer.AddAttribute (HtmlTextWriterAttribute.Href, "#" + owner.ClientID + "_SkipLink");
  188. writer.RenderBeginTag (HtmlTextWriterTag.A);
  189. // <img alt="" height="0" width="0" src="" style="border-width:0px;"/>
  190. writer.AddAttribute (HtmlTextWriterAttribute.Alt, skipLinkText);
  191. Page page = owner.Page;
  192. ClientScriptManager csm = page != null ? page.ClientScript : new ClientScriptManager (null);
  193. writer.AddAttribute (HtmlTextWriterAttribute.Src, csm.GetWebResourceUrl (typeof (SiteMapPath), "transparent.gif"));
  194. writer.AddAttribute (HtmlTextWriterAttribute.Width, "0");
  195. writer.AddAttribute (HtmlTextWriterAttribute.Height, "0");
  196. writer.AddStyleAttribute (HtmlTextWriterStyle.BorderWidth, "0px");
  197. writer.RenderBeginTag (HtmlTextWriterTag.Img);
  198. writer.RenderEndTag ();
  199. writer.RenderEndTag (); // </a>
  200. }
  201. public override void RenderEndTag (HtmlTextWriter writer)
  202. {
  203. }
  204. public override void AddAttributesToRender (HtmlTextWriter writer)
  205. {
  206. // do nothing
  207. }
  208. public override void RenderContents (HtmlTextWriter writer)
  209. {
  210. Menu owner = Owner;
  211. MenuItemCollection items = owner.Items;
  212. owner.RenderMenu (writer, items, owner.Orientation == Orientation.Vertical, false, 0, items.Count > 1);
  213. }
  214. public override void RenderMenuBeginTag (HtmlTextWriter writer, bool dynamic, int menuLevel)
  215. {
  216. if (dynamic || menuLevel == 0) {
  217. var style = new SubMenuStyle ();
  218. AddCssClass (style, "level" + (menuLevel + 1));
  219. FillMenuStyle (null, dynamic, menuLevel, style);
  220. style.AddAttributesToRender (writer);
  221. writer.RenderBeginTag (HtmlTextWriterTag.Ul);
  222. }
  223. }
  224. public override void RenderMenuEndTag (HtmlTextWriter writer, bool dynamic, int menuLevel)
  225. {
  226. if (dynamic || menuLevel == 0)
  227. base.RenderMenuEndTag (writer, dynamic, menuLevel);
  228. }
  229. public override void RenderMenuBody (HtmlTextWriter writer, MenuItemCollection items, bool vertical, bool dynamic, bool notLast)
  230. {
  231. Menu owner = Owner;
  232. int count = items.Count;
  233. var oc = new OwnerContext (this);
  234. for (int n = 0; n < count; n++) {
  235. MenuItem item = items [n];
  236. Adapters.MenuAdapter adapter = owner.Adapter as Adapters.MenuAdapter;
  237. if (adapter != null)
  238. adapter.RenderItem (writer, item, n);
  239. else
  240. RenderMenuItem (writer, item, vertical, (n + 1) == count ? notLast : true, n == 0, oc);
  241. }
  242. }
  243. protected override void RenderMenuItem (HtmlTextWriter writer, MenuItem item, bool vertical, bool notLast, bool isFirst, OwnerContext oc)
  244. {
  245. Menu owner = Owner;
  246. string clientID = oc.ClientID;
  247. bool displayChildren = owner.DisplayChildren (item);
  248. bool isDynamicItem = IsDynamicItem (owner, item);
  249. int itemLevel = item.Depth + 1;
  250. string str;
  251. writer.RenderBeginTag (HtmlTextWriterTag.Li);
  252. if (isDynamicItem)
  253. RenderSeparatorImage (owner, writer, oc.DynamicTopSeparatorImageUrl, true);
  254. else
  255. RenderSeparatorImage (owner, writer, oc.StaticTopSeparatorImageUrl, true);
  256. var linkStyle = new Style ();
  257. if (displayChildren && (isDynamicItem || itemLevel >= oc.StaticDisplayLevels))
  258. AddCssClass (linkStyle, isDynamicItem && haveDynamicPopOut ? "popout-dynamic" : "popout");
  259. AddCssClass (linkStyle, "level" + itemLevel);
  260. MenuItemStyleCollection levelStyles = oc.LevelMenuItemStyles;
  261. if (levelStyles != null && levelStyles.Count >= itemLevel) {
  262. MenuItemStyle style = levelStyles [itemLevel - 1];
  263. string cssClass = style.CssClass;
  264. if (!String.IsNullOrEmpty (cssClass))
  265. AddCssClass (linkStyle, cssClass);
  266. }
  267. if (owner.SelectedItem == item)
  268. AddCssClass (linkStyle, "selected");
  269. str = item.ToolTip;
  270. if (!String.IsNullOrEmpty (str))
  271. writer.AddAttribute ("title", str);
  272. linkStyle.AddAttributesToRender (writer);
  273. RenderItemHref (owner, writer, item);
  274. writer.RenderBeginTag (HtmlTextWriterTag.A);
  275. owner.RenderItemContent (writer, item, isDynamicItem);
  276. writer.RenderEndTag ();
  277. str = item.SeparatorImageUrl;
  278. if (String.IsNullOrEmpty (str)) {
  279. if (isDynamicItem)
  280. str = oc.DynamicBottomSeparatorImageUrl;
  281. else
  282. str = oc.StaticBottomSeparatorImageUrl;
  283. }
  284. RenderSeparatorImage (owner, writer, str, true);
  285. // if (itemLevel == 1)
  286. // writer.RenderEndTag (); // </li>
  287. if (displayChildren)
  288. owner.RenderMenu (writer, item.ChildItems, vertical, isDynamicItem, itemLevel, notLast);
  289. if (itemLevel > 1)
  290. writer.RenderEndTag (); // </li>
  291. }
  292. public override bool IsDynamicItem (Menu owner, MenuItem item)
  293. {
  294. if (owner == null)
  295. throw new ArgumentNullException ("owner");
  296. if (item == null)
  297. throw new ArgumentNullException ("item");
  298. return item.Depth + 1 >= Owner.StaticDisplayLevels;
  299. }
  300. NamedCssStyleCollection RenderAnchorStyle (StyleBlock block, Style style, string styleName)
  301. {
  302. if (style == null || block == null)
  303. return null;
  304. style.AlwaysRenderTextDecoration = true;
  305. NamedCssStyleCollection css = block.RegisterStyle (style, styleName);
  306. if (style.BorderStyle == BorderStyle.NotSet)
  307. css.Add (HtmlTextWriterStyle.BorderStyle, "none");
  308. return css;
  309. }
  310. void RenderLevelStyles (StyleBlock block, int num, IList levelStyles, string name, string unitName = null, double indent = 0)
  311. {
  312. int stylesCount = levelStyles != null ? levelStyles.Count : 0;
  313. bool haveStyles = stylesCount > 0;
  314. if (!haveStyles || block == null)
  315. return;
  316. NamedCssStyleCollection css;
  317. Style style;
  318. bool haveIndent = !String.IsNullOrEmpty (unitName) && indent != 0;
  319. for (int i = 0; i < stylesCount; i++) {
  320. if ((i == 0 && !haveStyles))
  321. continue;
  322. css = block.RegisterStyle (name + (i + 1));
  323. if (haveStyles && stylesCount > i) {
  324. style = levelStyles [i] as Style;
  325. if (style != null) {
  326. style.AlwaysRenderTextDecoration = true;
  327. css.CopyFrom (style.GetStyleAttributes (null));
  328. }
  329. }
  330. if (haveIndent && i > 0 && i < num) {
  331. css.Add (HtmlTextWriterStyle.PaddingLeft, indent.ToString (CultureInfo.InvariantCulture) + unitName);
  332. indent += indent;
  333. }
  334. }
  335. }
  336. }
  337. }