|
//------------------------------------------------------------------------------
// <copyright file="MenuItem.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
namespace System.Web.UI.WebControls {
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Design;
using System.Globalization;
using System.IO;
using System.Text;
using System.Web.UI;
using System.Web.Util;
/// <devdoc>
/// Provides a hierarchical menu item for use in the Menu class
/// </devdoc>
[ParseChildren(true, "ChildItems")]
public sealed class MenuItem : IStateManager, ICloneable {
private static readonly Unit HorizontalDefaultSpacing = Unit.Pixel(3);
private bool _isTrackingViewState;
private StateBag _viewState;
private MenuItemCollection _childItems;
private Menu _owner;
private MenuItem _parent;
private int _selectDesired;
private object _dataItem;
private MenuItemTemplateContainer _container;
private int _index;
internal string _id = string.Empty;
private string _valuePath;
private string _internalValuePath;
private int _depth = -2;
private bool _isRoot;
/// <devdoc>
/// Constructs a new MenuItem without a text or value
/// </devdoc>
public MenuItem() {
_selectDesired = 0;
}
/// <devdoc>
/// Constructs a new MenuItem with the specified owner Menu
/// </devdoc>
internal MenuItem(Menu owner, bool isRoot)
: this() {
_owner = owner;
_isRoot = isRoot;
}
/// <devdoc>
/// Constructs a new MenuItem with the specified text
/// </devdoc>
public MenuItem(string text)
: this(text, null, null, null, null) {
}
/// <devdoc>
/// Constructs a new MenuItem with the specified text, and value
/// </devdoc>
public MenuItem(string text, string value)
: this(text, value, null, null, null) {
}
/// <devdoc>
/// Constructs a new MenuItem with the specified text, value, and image URL
/// </devdoc>
public MenuItem(string text, string value, string imageUrl)
: this(text, value, imageUrl, null, null) {
}
/// <devdoc>
/// Constructs a new MenuItem with the specified text, value, image URL and navigateUrl
/// </devdoc>
public MenuItem(string text, string value, string imageUrl, string navigateUrl)
: this(text, value, imageUrl, navigateUrl, null) {
}
/// <devdoc>
/// Constructs a new MenuItem with the specified text, value, image URL, navigation URL, and target.
/// </devdoc>
public MenuItem(string text, string value, string imageUrl, string navigateUrl, string target)
: this() {
if (text != null) {
Text = text;
}
if (value != null) {
Value = value;
}
if (!String.IsNullOrEmpty(imageUrl)) {
ImageUrl = imageUrl;
}
if (!String.IsNullOrEmpty(navigateUrl)) {
NavigateUrl = navigateUrl;
}
if (!String.IsNullOrEmpty(target)) {
Target = target;
}
}
/// <devdoc>
/// Gets the collection of children items parented to this MenuItem
/// </devdoc>
[Browsable(false)]
[MergableProperty(false)]
[PersistenceMode(PersistenceMode.InnerDefaultProperty)]
public MenuItemCollection ChildItems {
get {
if (_childItems == null) {
_childItems = new MenuItemCollection(this);
}
return _childItems;
}
}
internal MenuItemTemplateContainer Container {
get {
return _container;
}
set {
_container = value;
}
}
/// <devdoc>
/// Gets whether this item was created through databinding
/// </devdoc>
[Browsable(false)]
[DefaultValue(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public bool DataBound {
get {
object o = ViewState["DataBound"];
if (o == null) {
return false;
}
return (bool)o;
}
}
/// <devdoc>
/// Gets path to the data to which this item is bound.
/// </devdoc>
[Browsable(false)]
[DefaultValue("")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public string DataPath {
get {
object s = ViewState["DataPath"];
if (s == null) {
return String.Empty;
}
return (string)s;
}
}
/// <devdoc>
/// Gets the depth of the menu item.
/// </devdoc>
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public int Depth {
get {
// -2 means not set yet, -1 means root
if (_depth == -2) {
if (_isRoot) {
return -1;
}
if (Parent != null) {
_depth = Parent.Depth + 1;
}
else {
return 0;
}
}
return _depth;
}
}
/// <devdoc>
/// Gets the data item for the menu item.
/// </devdoc>
[Browsable(false)]
[DefaultValue(null)]
public object DataItem {
get {
return _dataItem;
}
}
[Browsable(true)]
[DefaultValue(true)]
[WebSysDescription(SR.MenuItem_Enabled)]
public bool Enabled {
get {
object o = ViewState["Enabled"];
return (o == null ? true : (bool)o);
}
set {
ViewState["Enabled"] = value;
}
}
internal string FormattedText {
get {
if (_owner.StaticItemFormatString.Length > 0 && Depth < _owner.StaticDisplayLevels) {
return String.Format(CultureInfo.CurrentCulture, _owner.StaticItemFormatString, Text);
}
else if (_owner.DynamicItemFormatString.Length > 0 && Depth >= _owner.StaticDisplayLevels) {
return String.Format(CultureInfo.CurrentCulture, _owner.DynamicItemFormatString, Text);
}
else {
return Text;
}
}
}
internal string Id {
get {
if (_id.Length == 0) {
Index = _owner.CreateItemIndex();
_id = _owner.ClientID + 'n' + Index;
}
return _id;
}
}
/// <devdoc>
/// Gets and sets the image URl to be rendered for this item
/// </devdoc>
[DefaultValue("")]
[Editor("System.Web.UI.Design.ImageUrlEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor))]
[UrlProperty()]
[WebSysDescription(SR.MenuItem_ImageUrl)]
public string ImageUrl {
get {
object s = ViewState["ImageUrl"];
if (s == null) {
return String.Empty;
}
return (string)s;
}
set {
ViewState["ImageUrl"] = value;
}
}
/// <devdoc>
/// Gets and sets the unique index for the menu item
/// </devdoc>
internal int Index {
get {
return _index;
}
set {
_index = value;
}
}
internal string InternalValuePath {
get {
if (_internalValuePath != null) {
return _internalValuePath;
}
if (_parent != null) {
// StringBuilder.Insert is expensive, but we need to build starting from the end.
// First build a list, then build the string starting from the end of the list.
List<string> pathParts = new List<string>();
pathParts.Add(TreeView.Escape(Value));
MenuItem parent = _parent;
while ((parent != null) && !parent._isRoot) {
if (parent._internalValuePath != null) {
pathParts.Add(parent._internalValuePath);
break;
}
else {
pathParts.Add(TreeView.Escape(parent.Value));
}
parent = parent._parent;
}
pathParts.Reverse();
_internalValuePath = String.Join(TreeView.InternalPathSeparator.ToString(), pathParts.ToArray());
return _internalValuePath;
}
else {
return String.Empty;
}
}
}
internal bool IsEnabled {
get {
return IsEnabledNoOwner && Owner.IsEnabled;
}
}
internal bool IsEnabledNoOwner {
get {
MenuItem current = this;
while (current != null) {
if (!current.Enabled) {
return false;
}
current = current.Parent;
}
return true;
}
}
/// <devdoc>
/// Gets and sets the URL to navigate to when the item is clicked
/// </devdoc>
[DefaultValue("")]
[Editor("System.Web.UI.Design.UrlEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor))]
[UrlProperty()]
[WebSysDescription(SR.MenuItem_NavigateUrl)]
public string NavigateUrl {
get {
object s = ViewState["NavigateUrl"];
if (s == null) {
return String.Empty;
}
return (string)s;
}
set {
ViewState["NavigateUrl"] = value;
}
}
/// <devdoc>
/// Gets the owner Menu for this MenuItem, if there is one
/// </devdoc>
internal Menu Owner {
get {
return _owner;
}
}
/// <devdoc>
/// Gets the parent MenuItem
/// </devdoc>
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public MenuItem Parent {
get {
if ((_parent == null) || _parent._isRoot) {
return null;
}
return _parent;
}
}
/// <devdoc>
/// Gets and sets the image URl to be rendered as a pop-out icon for this item if it has children
/// </devdoc>
[DefaultValue("")]
[Editor("System.Web.UI.Design.ImageUrlEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor))]
[UrlProperty()]
[WebSysDescription(SR.MenuItem_PopOutImageUrl)]
public string PopOutImageUrl {
get {
object s = ViewState["PopOutImageUrl"];
if (s == null) {
return String.Empty;
}
return (string)s;
}
set {
ViewState["PopOutImageUrl"] = value;
}
}
[Browsable(true)]
[DefaultValue(true)]
[WebSysDescription(SR.MenuItem_Selectable)]
public bool Selectable {
get {
object o = ViewState["Selectable"];
return (o == null ? true : (bool)o);
}
set {
ViewState["Selectable"] = value;
}
}
/// <devdoc>
/// Gets and sets the selected state
/// </devdoc>
[Browsable(true)]
[DefaultValue(false)]
[WebSysDescription(SR.MenuItem_Selected)]
public bool Selected {
get {
object o = ViewState["Selected"];
if (o == null) {
return false;
}
return (bool)o;
}
set {
SetSelected(value);
NotifyOwnerSelected();
}
}
/// <devdoc>
/// Gets and sets the image URl to be rendered as a separator for this item
/// </devdoc>
[DefaultValue("")]
[Editor("System.Web.UI.Design.ImageUrlEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor))]
[UrlProperty()]
[WebSysDescription(SR.MenuItem_SeparatorImageUrl)]
public string SeparatorImageUrl {
get {
object s = ViewState["SeparatorImageUrl"];
if (s == null) {
return String.Empty;
}
return (string)s;
}
set {
ViewState["SeparatorImageUrl"] = value;
}
}
/// <devdoc>
/// Gets and sets the target window that the MenuItem will browse to if selected
/// </devdoc>
[DefaultValue("")]
[WebSysDescription(SR.MenuItem_Target)]
public string Target {
get {
object s = ViewState["Target"];
if (s == null) {
return String.Empty;
}
return (string)s;
}
set {
ViewState["Target"] = value;
}
}
/// <devdoc>
/// Gets and sets the display text
/// </devdoc>
[DefaultValue("")]
[Localizable(true)]
[WebSysDescription(SR.MenuItem_Text)]
public string Text {
get {
object s = ViewState["Text"];
if (s == null) {
s = ViewState["Value"];
if (s == null) {
return String.Empty;
}
}
return (string)s;
}
set {
ViewState["Text"] = value;
}
}
/// <devdoc>
/// Gets and sets the MenuItem tooltip
/// </devdoc>
[DefaultValue("")]
[Localizable(true)]
[WebSysDescription(SR.MenuItem_ToolTip)]
public string ToolTip {
get {
object s = ViewState["ToolTip"];
if (s == null) {
return String.Empty;
}
return (string)s;
}
set {
ViewState["ToolTip"] = value;
}
}
/// <devdoc>
/// Gets and sets the value
/// </devdoc>
[DefaultValue("")]
[Localizable(true)]
[WebSysDescription(SR.MenuItem_Value)]
public string Value {
get {
object s = ViewState["Value"];
if (s == null) {
s = ViewState["Text"];
if (s == null) {
return String.Empty;
}
}
return (string)s;
}
set {
ViewState["Value"] = value;
ResetValuePathRecursive();
}
}
/// <devdoc>
/// Gets the full path of the MenuItem
/// </devdoc>
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public string ValuePath {
get {
if (_valuePath != null) {
return _valuePath;
}
if (_parent != null) {
string parentPath = _parent.ValuePath;
_valuePath = ((parentPath.Length == 0) && (_parent.Depth == -1)) ?
Value : parentPath + _owner.PathSeparator + Value;
return _valuePath;
}
else {
return String.Empty;
}
}
}
/// <devdoc>
/// The state for this MenuItem
/// </devdoc>
private StateBag ViewState {
get {
if (_viewState == null) {
_viewState = new StateBag();
if (_isTrackingViewState) {
((IStateManager)_viewState).TrackViewState();
}
}
return _viewState;
}
}
internal string GetExpandImageUrl() {
if (ChildItems.Count > 0) {
if (PopOutImageUrl.Length != 0) {
return _owner.ResolveClientUrl(PopOutImageUrl);
}
else {
if (Depth < _owner.StaticDisplayLevels) {
if (_owner.StaticPopOutImageUrl.Length != 0) {
return _owner.ResolveClientUrl(_owner.StaticPopOutImageUrl);
}
else if (_owner.StaticEnableDefaultPopOutImage) {
return _owner.GetImageUrl(Menu.PopOutImageIndex);
}
}
else {
if (_owner.DynamicPopOutImageUrl.Length != 0) {
return _owner.ResolveClientUrl(_owner.DynamicPopOutImageUrl);
}
else if (_owner.DynamicEnableDefaultPopOutImage) {
return _owner.GetImageUrl(Menu.PopOutImageIndex);
}
}
}
}
return String.Empty;
}
internal bool NotTemplated() {
return ((_owner.StaticItemTemplate == null || Depth >= _owner.StaticDisplayLevels) &&
(_owner.DynamicItemTemplate == null || Depth < _owner.StaticDisplayLevels));
}
private void NotifyOwnerSelected() {
object o = ViewState["Selected"];
bool value = (o == null ? false : (bool)o);
// If the owner hasn't been set, remember that we want to select this item
// when the owner is determined
if (_owner == null) {
_selectDesired = (value ? +1 : -1);
return;
}
else if (value) {
// Set the Menu's selected item to this one
_owner.SetSelectedItem(this);
}
else if (this == _owner.SelectedItem) {
_owner.SetSelectedItem(null);
}
}
/// <devdoc>
/// Renders the contents of the item and its children.
/// </devdoc>
internal void Render(HtmlTextWriter writer, bool enabled, bool staticOnly) {
Render(writer, enabled, staticOnly, true);
}
/// <devdoc>
/// Renders the contents of the item and its children.
/// </devdoc>
internal void Render(HtmlTextWriter writer, bool enabled, bool staticOnly, bool recursive) {
enabled = enabled && Enabled;
// If children exist, maybe render them
int nextDepth = Depth + 1;
if (ChildItems.Count > 0 && nextDepth < _owner.MaximumDepth) {
// <table cellpadding="0" cellspacing="0" border="0">
// Find the right style:
SubMenuStyle subMenuStyle = _owner.GetSubMenuStyle(this);
string styleClass = null;
if (_owner.Page != null && _owner.Page.SupportsStyleSheets) {
styleClass = _owner.GetSubMenuCssClassName(this);
}
if (nextDepth >= _owner.StaticDisplayLevels) {
// The submenu is dynamic
if (!staticOnly && enabled && !(_owner.DesignMode && recursive)) {
// Not recreating a panel each time: panel is created and configured only once from Menu.Panel.
PopOutPanel panel = _owner.Panel;
if (_owner.Page != null && _owner.Page.SupportsStyleSheets) {
panel.ScrollerClass = _owner.GetCssClassName(ChildItems[0], false);
panel.ScrollerStyle = null;
}
else {
panel.ScrollerClass = null;
panel.ScrollerStyle = _owner.GetMenuItemStyle(ChildItems[0]);
}
if (_owner.Page != null && _owner.Page.SupportsStyleSheets) {
panel.CssClass = styleClass;
panel.SetInternalStyle(null);
}
else if (!subMenuStyle.IsEmpty) {
panel.CssClass = String.Empty;
panel.SetInternalStyle(subMenuStyle);
}
else {
panel.CssClass = String.Empty;
panel.SetInternalStyle(null);
panel.BackColor = Color.Empty;
}
panel.ID = Id + "Items";
panel.RenderBeginTag(writer);
writer.AddAttribute(HtmlTextWriterAttribute.Border, "0");
writer.AddAttribute(HtmlTextWriterAttribute.Cellpadding, "0");
writer.AddAttribute(HtmlTextWriterAttribute.Cellspacing, "0");
writer.RenderBeginTag(HtmlTextWriterTag.Table);
for (int i = 0; i < ChildItems.Count; i++) {
ChildItems[i].RenderItem(writer, i, enabled, Orientation.Vertical);
}
writer.RenderEndTag(); // Table
panel.RenderEndTag(writer);
if (recursive) {
for (int i = 0; i < ChildItems.Count; i++) {
ChildItems[i].Render(writer, enabled, false);
}
}
}
}
else {
// The submenu is static
writer.AddAttribute(HtmlTextWriterAttribute.Border, "0");
writer.AddAttribute(HtmlTextWriterAttribute.Cellpadding, "0");
writer.AddAttribute(HtmlTextWriterAttribute.Cellspacing, "0");
writer.AddAttribute(HtmlTextWriterAttribute.Width, "100%");
if (_owner.Page != null && _owner.Page.SupportsStyleSheets) {
if (styleClass != null && styleClass.Length > 0) {
writer.AddAttribute(HtmlTextWriterAttribute.Class, styleClass);
}
}
else {
subMenuStyle.AddAttributesToRender(writer);
}
writer.RenderBeginTag(HtmlTextWriterTag.Table);
if (_owner.Orientation == Orientation.Horizontal) {
// Render one global tr as items won't render any
writer.RenderBeginTag(HtmlTextWriterTag.Tr);
}
bool isNextStatic = (nextDepth + 1 < _owner.StaticDisplayLevels);
bool isNextInRange = (nextDepth + 1 < _owner.MaximumDepth);
for (int i = 0; i < ChildItems.Count; i++) {
if (recursive
&& ChildItems[i].ChildItems.Count != 0
&& ((enabled && ChildItems[i].Enabled) || isNextStatic)
&& isNextInRange) {
// If the next items are dynamic, we want to render the div inside the item's
// td so that we don't generate a tr that contains only absolute positioned
// divs, which would cause a gap to appear (VSWhidbey 354884)
if (isNextStatic) {
ChildItems[i].RenderItem(writer, i, enabled, _owner.Orientation);
if (_owner.Orientation == Orientation.Vertical) {
writer.RenderBeginTag(HtmlTextWriterTag.Tr);
writer.RenderBeginTag(HtmlTextWriterTag.Td);
ChildItems[i].Render(writer, enabled, staticOnly);
writer.RenderEndTag(); //td
writer.RenderEndTag(); //tr
}
else {
writer.RenderBeginTag(HtmlTextWriterTag.Td);
ChildItems[i].Render(writer, enabled, staticOnly);
writer.RenderEndTag(); //td
}
}
else {
ChildItems[i].RenderItem(writer, i, enabled, _owner.Orientation, staticOnly);
}
}
else {
ChildItems[i].RenderItem(writer, i, enabled, _owner.Orientation);
}
}
if (_owner.Orientation == Orientation.Horizontal) {
// Render global /tr
writer.RenderEndTag();
}
writer.RenderEndTag(); //table
if (!isNextStatic && !staticOnly && recursive && isNextInRange) {
for (int i = 0; i < ChildItems.Count; i++) {
if (ChildItems[i].ChildItems.Count != 0
&& ((enabled && ChildItems[i].Enabled))) {
// The next items are dynamic, so we want to render the div outside the menu table
// so that we don't generate a tr that contains only absolute positioned
// divs, which would cause a gap to appear (VSWhidbey 354884)
ChildItems[i].Render(writer, enabled, false, true);
}
}
}
}
}
}
/// <devdoc>
/// Renders the contents of the item but not its children.
/// </devdoc>
internal void RenderItem(HtmlTextWriter writer, int position, bool enabled, Orientation orientation) {
RenderItem(writer, position, enabled, orientation, false);
}
internal void RenderItem(HtmlTextWriter writer, int position, bool enabled, Orientation orientation, bool staticOnly) {
enabled = enabled && Enabled;
int depth = Depth;
MenuItemStyle mergedStyle = _owner.GetMenuItemStyle(this);
int depthPlusOne = Depth + 1;
bool staticTopSeparator = (depth < _owner.StaticDisplayLevels) && (_owner.StaticTopSeparatorImageUrl.Length != 0);
bool dynamicTopSeparator = (depth >= _owner.StaticDisplayLevels) && (_owner.DynamicTopSeparatorImageUrl.Length != 0);
// The separator is in a separate td in the vertical case, in a separate tr otherwise.
if (staticTopSeparator || dynamicTopSeparator) {
if (orientation == Orientation.Vertical) {
writer.RenderBeginTag(HtmlTextWriterTag.Tr);
}
writer.RenderBeginTag(HtmlTextWriterTag.Td);
if (staticTopSeparator) {
writer.AddAttribute(HtmlTextWriterAttribute.Src, _owner.ResolveClientUrl(_owner.StaticTopSeparatorImageUrl));
}
else {
writer.AddAttribute(HtmlTextWriterAttribute.Src, _owner.ResolveClientUrl(_owner.DynamicTopSeparatorImageUrl));
}
writer.AddAttribute(HtmlTextWriterAttribute.Alt, String.Empty);
writer.RenderBeginTag(HtmlTextWriterTag.Img);
writer.RenderEndTag(); // Img
writer.RenderEndTag(); // Td
if (orientation == Orientation.Vertical) {
writer.RenderEndTag(); // Tr
}
}
// Top spacing
if ((mergedStyle != null) && !mergedStyle.ItemSpacing.IsEmpty && ((depth != 0) || (position != 0))) {
RenderItemSpacing(writer, mergedStyle.ItemSpacing, orientation);
}
if (!staticOnly && _owner.Enabled) {
if (depthPlusOne > _owner.StaticDisplayLevels) {
// Only the last static level and dynamic levels need hover behavior.
// And then only if they are selectable or have children.
if ((Selectable && Enabled) || ChildItems.Count != 0) {
writer.AddAttribute("onmouseover", "Menu_HoverDynamic(this)");
RenderItemEvents(writer);
}
else {
// dynamic disabled or unselectable items without children still need to maintain the menu open
writer.AddAttribute("onmouseover", "Menu_HoverDisabled(this)");
writer.AddAttribute("onmouseout", "Menu_Unhover(this)");
}
}
else if (depthPlusOne == _owner.StaticDisplayLevels) {
// Here's for the last static level
if ((Selectable && Enabled) || ChildItems.Count != 0) {
writer.AddAttribute("onmouseover", "Menu_HoverStatic(this)");
RenderItemEvents(writer);
}
}
else if (Selectable && Enabled) {
// Other nodes need hover styles but no expand
writer.AddAttribute("onmouseover", "Menu_HoverRoot(this)");
RenderItemEvents(writer);
}
}
// Tooltip
if (ToolTip.Length != 0) {
writer.AddAttribute(HtmlTextWriterAttribute.Title, ToolTip);
}
// Set the id
writer.AddAttribute(HtmlTextWriterAttribute.Id, Id);
if (orientation == Orientation.Vertical) {
// <tr>
writer.RenderBeginTag(HtmlTextWriterTag.Tr);
}
writer.RenderBeginTag(HtmlTextWriterTag.Td);
// Set the style
if (_owner.Page != null && _owner.Page.SupportsStyleSheets) {
string styleClass = _owner.GetCssClassName(this, false);
if (styleClass.Trim().Length > 0) {
writer.AddAttribute(HtmlTextWriterAttribute.Class, styleClass);
}
}
else if (mergedStyle != null) {
mergedStyle.AddAttributesToRender(writer);
}
writer.AddAttribute(HtmlTextWriterAttribute.Cellpadding, "0");
writer.AddAttribute(HtmlTextWriterAttribute.Cellspacing, "0");
writer.AddAttribute(HtmlTextWriterAttribute.Border, "0");
writer.AddAttribute(HtmlTextWriterAttribute.Width, "100%");
writer.RenderBeginTag(HtmlTextWriterTag.Table);
writer.RenderBeginTag(HtmlTextWriterTag.Tr);
if (!_owner.ItemWrap) {
writer.AddStyleAttribute(HtmlTextWriterStyle.WhiteSpace, "nowrap");
}
if (orientation == Orientation.Vertical) {
writer.AddStyleAttribute(HtmlTextWriterStyle.Width, "100%");
}
writer.RenderBeginTag(HtmlTextWriterTag.Td);
if (_owner.Page != null && _owner.Page.SupportsStyleSheets) {
bool applyInlineBorder;
string styleClass = _owner.GetCssClassName(this, true, out applyInlineBorder);
if (styleClass.Trim().Length > 0) {
writer.AddAttribute(HtmlTextWriterAttribute.Class, styleClass);
if (applyInlineBorder) {
// Add inline style to force the border to none to override any CssClass (VSWhidbey 336610)
writer.AddStyleAttribute(HtmlTextWriterStyle.BorderStyle, "none");
// And an inline font-size of 1em to avoid squaring relative font sizes by applying them twice
writer.AddStyleAttribute(HtmlTextWriterStyle.FontSize, "1em");
}
}
}
else {
if (mergedStyle != null) {
mergedStyle.HyperLinkStyle.AddAttributesToRender(writer);
}
}
string accessKey = _owner.AccessKey;
if (enabled && Selectable) {
// If there is a navigation url on this item, set up the navigation stuff
if (NavigateUrl.Length > 0) {
writer.AddAttribute(HtmlTextWriterAttribute.Href, _owner.ResolveClientUrl(NavigateUrl));
// Use the MenuItem Target if it has one, the Menu's if it doesn't
string target = ViewState["Target"] as string;
if (target == null) {
target = _owner.Target;
}
if (target.Length > 0) {
writer.AddAttribute(HtmlTextWriterAttribute.Target, target);
}
}
// Otherwise, write out a postback that will select the item
else {
writer.AddAttribute(HtmlTextWriterAttribute.Href,
_owner.Page.ClientScript.GetPostBackClientHyperlink(_owner, InternalValuePath, true, true));
}
// AccessKey
if (!_owner.AccessKeyRendered && (accessKey.Length != 0)) {
writer.AddAttribute(HtmlTextWriterAttribute.Accesskey, accessKey, true);
_owner.AccessKeyRendered = true;
}
}
else {
// Span if disabled or not selectable
if (!enabled) {
writer.AddAttribute(HtmlTextWriterAttribute.Disabled, "true");
}
else if ((ChildItems.Count != 0) && (depthPlusOne >= _owner.StaticDisplayLevels)) {
// For accessibility reasons, we want the a to have an href even if it's not selectable
// because we want it to be focusable if it has dynamic children.
writer.AddAttribute(HtmlTextWriterAttribute.Href, "#");
writer.AddStyleAttribute(HtmlTextWriterStyle.Cursor, "text");
// AccessKey
if (!_owner.AccessKeyRendered && (accessKey.Length != 0)) {
writer.AddAttribute(HtmlTextWriterAttribute.Accesskey, accessKey, true);
_owner.AccessKeyRendered = true;
}
}
}
if (depth != 0 && depth < _owner.StaticDisplayLevels) {
Unit indent = _owner.StaticSubMenuIndent;
// In 4.0, the default value of Menu.StaticSubMenuIndent was changed from 16px to Unit.Empty,
// since the table and list rendering modes need to have different effective default values.
// To maintain back compat, the effective default value for table rendering is 16px.
// Dev10 Bug 741543
if (indent.IsEmpty) {
indent = Unit.Pixel(16);
}
if (indent.Value != 0) {
double indentValue = indent.Value * depth;
if (indentValue < Unit.MaxValue) {
indent = new Unit(indentValue, indent.Type);
}
else {
indent = new Unit(Unit.MaxValue, indent.Type);
}
writer.AddStyleAttribute("margin-left", indent.ToString(CultureInfo.InvariantCulture));
}
}
// <a href=href>
// We're rendering an A tag in all cases so that the client script can always find the items by tag name
writer.RenderBeginTag(HtmlTextWriterTag.A);
// Render out the item icon, if it is set and if the item is not templated
if (ImageUrl.Length > 0 && NotTemplated()) {
// <img>
writer.AddAttribute(HtmlTextWriterAttribute.Src, _owner.ResolveClientUrl(ImageUrl));
writer.AddAttribute(HtmlTextWriterAttribute.Alt, ToolTip);
writer.AddStyleAttribute(HtmlTextWriterStyle.BorderStyle, "none");
writer.AddStyleAttribute("vertical-align", "middle");
writer.RenderBeginTag(HtmlTextWriterTag.Img);
writer.RenderEndTag();
}
// Item text
RenderText(writer);
// </a> or </span>
writer.RenderEndTag();
bool shouldRenderExpandImage = ((depthPlusOne >= _owner.StaticDisplayLevels) &&
(depthPlusOne < _owner.MaximumDepth));
string expandImageUrl = (shouldRenderExpandImage ? GetExpandImageUrl() : String.Empty);
// Render some space if horizontal
bool needsSpace = false;
if ((orientation == Orientation.Horizontal) &&
(depth < _owner.StaticDisplayLevels) &&
(!shouldRenderExpandImage || (expandImageUrl.Length == 0)) &&
((mergedStyle == null) || mergedStyle.ItemSpacing.IsEmpty)) {
if (((Depth + 1) < _owner.StaticDisplayLevels) && (ChildItems.Count != 0)) {
// next level is static too and exists, so we're not the last static
needsSpace = true;
}
else {
// Static item with no static children.
// We need to check if there are any static items at any level after this one.
// This is done by checking if any ancestor is not last.
// This walk should be marginally executed as multiple static display levels
// don't make sense on a horizontal menu.
MenuItem parent = this;
while (parent != null) {
if (((parent.Parent == null) &&
(_owner.Items.Count != 0) &&
(parent != _owner.Items[_owner.Items.Count - 1])) ||
((parent.Parent != null) &&
(parent.Parent.ChildItems.Count != 0) &&
(parent != parent.Parent.ChildItems[parent.Parent.ChildItems.Count - 1]))) {
needsSpace = true;
break;
}
parent = parent.Parent;
}
}
}
// </td>
writer.RenderEndTag();
if (shouldRenderExpandImage && expandImageUrl.Length > 0) {
// <td>
writer.AddStyleAttribute(HtmlTextWriterStyle.Width, "0");
writer.RenderBeginTag(HtmlTextWriterTag.Td);
// <img>
writer.AddAttribute(HtmlTextWriterAttribute.Src, expandImageUrl);
writer.AddStyleAttribute(HtmlTextWriterStyle.BorderStyle, "none");
writer.AddStyleAttribute(HtmlTextWriterStyle.VerticalAlign, "middle");
if (depth < _owner.StaticDisplayLevels) {
writer.AddAttribute(HtmlTextWriterAttribute.Alt,
String.Format(CultureInfo.CurrentCulture, _owner.StaticPopOutImageTextFormatString, Text));
}
else if (depth >= _owner.StaticDisplayLevels) {
writer.AddAttribute(HtmlTextWriterAttribute.Alt,
String.Format(CultureInfo.CurrentCulture, _owner.DynamicPopOutImageTextFormatString, Text));
}
writer.RenderBeginTag(HtmlTextWriterTag.Img);
writer.RenderEndTag();
// </td>
writer.RenderEndTag();
}
writer.RenderEndTag(); // </tr>
writer.RenderEndTag(); // </table>
writer.RenderEndTag(); // </td>
if (orientation == Orientation.Vertical) {
writer.RenderEndTag(); // </tr>
}
// Bottom (or right) item spacing
// We do not render the spacing on the last static item or on the last item of a dynamic submenu
if ((mergedStyle != null) && !mergedStyle.ItemSpacing.IsEmpty) {
RenderItemSpacing(writer, mergedStyle.ItemSpacing, orientation);
}
else if (needsSpace) {
RenderItemSpacing(writer, HorizontalDefaultSpacing, orientation);
}
// Bottom separator
bool separator = (SeparatorImageUrl.Length != 0);
bool staticBottomSeparator = (depth < _owner.StaticDisplayLevels) && (_owner.StaticBottomSeparatorImageUrl.Length != 0);
bool dynamicBottomSeparator = (depth >= _owner.StaticDisplayLevels) && (_owner.DynamicBottomSeparatorImageUrl.Length != 0);
if (separator || staticBottomSeparator || dynamicBottomSeparator) {
if (orientation == Orientation.Vertical) {
writer.RenderBeginTag(HtmlTextWriterTag.Tr);
}
writer.RenderBeginTag(HtmlTextWriterTag.Td);
if (separator) {
writer.AddAttribute(HtmlTextWriterAttribute.Src, _owner.ResolveClientUrl(SeparatorImageUrl));
}
else if (staticBottomSeparator) {
writer.AddAttribute(HtmlTextWriterAttribute.Src, _owner.ResolveClientUrl(_owner.StaticBottomSeparatorImageUrl));
}
else {
writer.AddAttribute(HtmlTextWriterAttribute.Src, _owner.ResolveClientUrl(_owner.DynamicBottomSeparatorImageUrl));
}
writer.AddAttribute(HtmlTextWriterAttribute.Alt, String.Empty);
writer.RenderBeginTag(HtmlTextWriterTag.Img);
writer.RenderEndTag(); // Img
writer.RenderEndTag(); // Td
if (orientation == Orientation.Vertical) {
writer.RenderEndTag(); // Tr
}
}
}
private void RenderItemEvents(HtmlTextWriter writer) {
writer.AddAttribute("onmouseout", "Menu_Unhover(this)");
if (_owner.IsNotIE) {
writer.AddAttribute("onkeyup", "Menu_Key(event)");
}
else {
writer.AddAttribute("onkeyup", "Menu_Key(this)");
}
}
private void RenderItemSpacing(HtmlTextWriter writer, Unit spacing, Orientation orientation) {
if (orientation == Orientation.Vertical) {
writer.AddStyleAttribute(HtmlTextWriterStyle.Height,
spacing.ToString(CultureInfo.InvariantCulture));
writer.RenderBeginTag(HtmlTextWriterTag.Tr);
writer.RenderBeginTag(HtmlTextWriterTag.Td);
writer.RenderEndTag();
writer.RenderEndTag();
}
else {
writer.AddStyleAttribute(HtmlTextWriterStyle.Width,
spacing.ToString(CultureInfo.InvariantCulture));
writer.RenderBeginTag(HtmlTextWriterTag.Td);
writer.RenderEndTag();
}
}
internal void RenderText(HtmlTextWriter writer) {
if (Container != null &&
((_owner.StaticItemTemplate != null && Depth < _owner.StaticDisplayLevels) ||
(_owner.DynamicItemTemplate != null && Depth >= _owner.StaticDisplayLevels))) {
Container.RenderControl(writer);
}
else {
writer.Write(FormattedText);
}
}
internal void ResetValuePathRecursive() {
if (_valuePath != null) {
_valuePath = null;
foreach (MenuItem child in ChildItems) {
child.ResetValuePathRecursive();
}
}
}
/// <devdoc>
/// Marks this item as a databound item
/// </devdoc>
internal void SetDataBound(bool dataBound) {
ViewState["DataBound"] = dataBound;
}
/// <devdoc>
/// Sets the data item for use by the user in databinding
/// </devdoc>
internal void SetDataItem(object dataItem) {
_dataItem = dataItem;
}
/// <devdoc>
/// Sets the data path for use by the Menu in databinding
/// </devdoc>
internal void SetDataPath(string dataPath) {
ViewState["DataPath"] = dataPath;
}
internal void SetDepth(int depth) {
_depth = depth;
}
internal void SetDirty() {
ViewState.SetDirty(true);
if (ChildItems.Count > 0) {
ChildItems.SetDirty();
}
}
/// <devdoc>
/// Sets the owner Menu of this item.
/// </devdoc>
internal void SetOwner(Menu owner) {
_owner = owner;
if (_selectDesired == +1) {
_selectDesired = 0;
Selected = true;
}
else if (_selectDesired == -1) {
_selectDesired = 0;
Selected = false;
}
foreach (MenuItem item in ChildItems) {
item.SetOwner(_owner);
}
}
/// <devdoc>
/// Sets the parent MenuItem of the item
/// </devdoc>
internal void SetParent(MenuItem parent) {
_parent = parent;
SetPath(null);
}
internal void SetPath(string newPath) {
_internalValuePath = newPath;
_depth = -2;
}
internal void SetSelected(bool value) {
ViewState["Selected"] = value;
// If the owner hasn't been set, remember that we want to select this node
// when the owner is determined
if (_owner == null) {
_selectDesired = (value ? +1 : -1);
}
}
#region IStateManager implementation
/// <internalonly/>
bool IStateManager.IsTrackingViewState {
get {
return _isTrackingViewState;
}
}
/// <internalonly/>
void IStateManager.LoadViewState(object state) {
object[] itemState = (object[])state;
if (itemState != null) {
if (itemState[0] != null) {
((IStateManager)ViewState).LoadViewState(itemState[0]);
}
// We need to call the selected setter so that the owner can be notified
// N.B. The treeview does not need to do that
// because it's loading the state of its node differently because of the richer client-side behavior.
NotifyOwnerSelected();
if (itemState[1] != null) {
((IStateManager)ChildItems).LoadViewState(itemState[1]);
}
}
}
/// <internalonly/>
object IStateManager.SaveViewState() {
object[] state = new object[2];
if (_viewState != null) {
state[0] = ((IStateManager)_viewState).SaveViewState();
}
if (_childItems != null) {
state[1] = ((IStateManager)_childItems).SaveViewState();
}
if ((state[0] == null) && (state[1] == null)) {
return null;
}
return state;
}
/// <internalonly/>
void IStateManager.TrackViewState() {
_isTrackingViewState = true;
if (_viewState != null) {
((IStateManager)_viewState).TrackViewState();
}
if (_childItems != null) {
((IStateManager)_childItems).TrackViewState();
}
}
#endregion
#region ICloneable implementation
/// <internalonly/>
object ICloneable.Clone() {
MenuItem newItem = new MenuItem();
newItem.Enabled = Enabled;
newItem.ImageUrl = ImageUrl;
newItem.NavigateUrl = NavigateUrl;
newItem.PopOutImageUrl = PopOutImageUrl;
newItem.Selectable = Selectable;
newItem.Selected = Selected;
newItem.SeparatorImageUrl = SeparatorImageUrl;
newItem.Target = Target;
newItem.Text = Text;
newItem.ToolTip = ToolTip;
newItem.Value = Value;
return newItem;
}
#endregion
}
public sealed class MenuItemTemplateContainer : Control, IDataItemContainer {
private int _itemIndex;
private object _dataItem;
public MenuItemTemplateContainer(int itemIndex, MenuItem dataItem) {
_itemIndex = itemIndex;
_dataItem = dataItem;
}
public object DataItem {
get {
return _dataItem;
}
set {
_dataItem = value;
}
}
public int ItemIndex {
get {
return _itemIndex;
}
}
protected override bool OnBubbleEvent(object source, EventArgs e) {
CommandEventArgs ce = e as CommandEventArgs;
if (ce != null) {
if (ce is MenuEventArgs) {
RaiseBubbleEvent(this, ce);
}
else {
MenuEventArgs args = new MenuEventArgs((MenuItem)_dataItem, source, ce);
RaiseBubbleEvent(this, args);
}
return true;
}
return false;
}
object IDataItemContainer.DataItem {
get {
return _dataItem;
}
}
int IDataItemContainer.DataItemIndex {
get {
return ItemIndex;
}
}
int IDataItemContainer.DisplayIndex {
get {
return ItemIndex;
}
}
}
}
|