为ASP.NET控件加入快捷菜单(3)

发表于:2007-06-30来源:作者:点击数: 标签:
控件的实现 ContextMenu控件的核心在于重写CreatechildControls方法。在这个方法中控件创建界面并在页面中写入所需的脚本。我们说过,ContexMenu控件的用户界面分为两部分——图形和脚本。我们先说图形。 CreateChildControls方法产生一个可以在页面移动的HT
     控件的实现
  
    ContextMenu控件的核心在于重写CreatechildControls方法。在这个方法中控件创建界面并在页面中写入所需的脚本。我们说过,ContexMenu控件的用户界面分为两部分——图形和脚本。我们先说图形。
  
    CreateChildControls方法产生一个可以在页面移动的HTML块,它就是所需的弹出菜单。照这样看来,快捷菜单就是包含一个table表单的<DIV> ,每一个菜单项就是这个table表单中的一行。使用table是由于一系列的开发点(象边框和浮动层)和它能很容易的扩展(例如添加侧边图象)所决定的。
  
  HtmlGenericControl div = new HtmlGenericControl("div");
  div.ID = "Root";
  div.Style["Display"] = "none";
  div.Style["position"] = "absolute";
  if (AutoHide)
  
   div.Attributes["onmouseleave"] = "this.style.display=’none’";
  
    我们使用层叠样式表(CSS)来隐藏最外层的<DIV>标签,并且用绝对位置标记这个<DIV>。如果自动隐藏可用,那么<DIV> 还得处理 mouse-leave(鼠标离开)事件来隐藏本身。那么 onmouseout和onmouseleave事件有什么不同呢?前者是当鼠标移动到一个新的元素上时发生,而后者是当鼠标移出绑定对象时发生。举例说明:你的鼠标在一个有两行的表单(table)上移动。当你在table的两行之间移动时,onmouseout事件就会发生;只有你的鼠标移动到table表单之外时onmouseleave事件才发生。
  
    Table包含和要显示的菜单项个数相同的行,每一行有一个单元格,每个单元格里放一个LinkButton对象。菜单通过一个循环创建:
  
  foreach(ContextMenuItem item in ContextMenuItems)
  {
   TableRow menuItem = new TableRow();
   menuTable.Rows.Add(menuItem);
   TableCell container = new TableCell();
   menuItem.Cells.Add(container);
   LinkButton button = new LinkButton();
   container.Controls.Add(button);
   ...
  }
  
    行的单元格有一组脚本操作——onmouseover和onmouseout——完成鼠标划过的效果。当鼠标划过时改变背景颜色。鼠标离开时恢复初始颜色。默认的背景色由从WebControl继承的background属性指定。高亮颜色由新的属性RolloverColor指定。
  
  string color = String.Format(ContextMenu.OnMouseOver, ColorTranslator.ToHtml(RolloverColor));
  container.Attributes["onmouseover"] = color;
  color = String.Format(ContextMenu.OnMouseOut, ColorTranslator.ToHtml(BackColor));
  container.Attributes["onmouseout"] = color;
  
    你需要把.net的System.Drawing.Color值转换成可用的HTML颜色。有意思的是,无论是Color类的ToString方法还是它的Name属性都不能在所有情况下返回对应的HTML颜色字符串,不知是不是有意这样的:(。Name属性基本可以实现这一功能,只有和种情况例外。当颜色不能和已知的颜色匹配时,这个属性返回颜色的RGB组,外加alpha通道值。要得到当前的HTML颜色,你必须移除alpha通道值(通常是开头的十六进制 ff 字符串)并使用#号替换它。幸运的是,System.Drawing.ColorTranslator类可以自动完成这项工作:)。
  
    我们接着要对链接按钮做一些调整,以使其正常工作。 也就是:把链接的宽度设成100%以确保当没有其它设置时,在整个行上光标都是手状。 同样的,要设置从对应的菜单项对象得到的文本(text),工具提示(tooltip),和命令名(command name)。最后为点击事件关联一个处理器。
  
  LinkButton button = new LinkButton();
  container.Controls.Add(button);
  button.Click += new EventHandler(ButtonClicked);
  button.Width = Unit.Percentage(100);
  button.ToolTip = item.Tooltip;
  button.Text = item.Text;
  button.CommandName = item.CommandName;
  
    回传时,事件源被识别为在ContextMenu命名容器内的LinkButton,并且得到处理回传事件的时机。点击事件的内部处理器将所有信息打包进一个新的ItemCommand 事件并引激活这个事件,参见下面的代码
  
  private void ButtonClicked(object sender, EventArgs e)
  {
   LinkButton button = sender as LinkButton;
   if (button != null)
   {
    CommandEventArgs args = new CommandEventArgs( button.CommandName, button.CommandArgument);
    OnItemCommand(args);
   }
  }
  protected virtual void OnItemCommand(CommandEventArgs e)
  {
   if (ItemCommand != null)
    ItemCommand(this, e);
  }
  
    页面上的代码将获得两部分内容:引发事件的ContextMenu实例;与点击项相关的command name。
  
    这时,table就是快捷菜单的用户界面。它一开始被放置在页面的任意位置,并且使用CSS样式表在视图中隐藏。在用户右击时,这段HTML代码块(使用绝对位置方式定位)将被显示成一个快捷菜单。JavaScript代码负责截取事件并把菜单移动动所需位置,参见如下代码:
  
  <script language="Javascript">
  function __showContextMenu(menu)
  {
   var menuOffset = 2 menu.style.left = window.event.x - menuOffset;
   menu.style.top = window.event.y - menuOffset;
   menu.style.display = "";
   window.event.cancelBubble = true; return false;
  }
  function __trapESC(menu)
  {
   var key = window.event.keyCode;
   if (key == 27)
   {
    menu.style.display = ’none’;
   }
  }
  </script>
  
    __showContextMenu函数设置快捷菜单对象的Top和Left属性,以使它在点击发生的位置显示。少量的偏移是确保当快捷菜单显示时,鼠标已经处于它的上面。这样也防止由于鼠标在菜单边缘的轻微移动而使菜单自动隐藏。鼠标事件的冒泡也必须被停止,这样在文档对象模型层次中高层的结点就不会捕获右击事件。
  
    那么由谁来调用__showContextMenu函数呢?答案是:浏览器。当浏览检测到有和HTML元素的onContextMenu事件相关联的函数时,它会调用这个函数。我们以前说过OnContextMenu事件是Internet Explorer特有的事件,Netscape浏览器不支持此事件。作为代替可以使用onmouseup事件来检测和处理松开鼠标右键的事件。
  
    快捷菜单也负责为每个控件或为注册快捷菜单的页面元素添加oncontextmenu事件处理器。我定义了两种方式使一个元素获取它的快捷菜单.BoundControls集合属性是一个数组。它由需要使用该快捷菜单的所有页面控件填充,并且完成填充的代码写在页面代码中.如下:
  
  void Page_Load(object sender, EventArgs e)
  {
   ContextMenu1.BoundControls.Add(Button1);
   ...
  }
  
    以上代码产生如下标记:
  
  <input type=submit ... oncontextmenu="__showContextMenu(...)" />
  
    当用户在控件上右击时会弹出快捷菜单。这种方法要求任何使用自定义快捷菜单的元素必须是服务器控件,有时这种要求是不合适的。例如:假使你想为一个图片使用自定义的快捷菜单。你必须把图片标记<img>写成 runat=server。其实没必要这样。看下面的例子:
  
  <img oncontextmenu="<% = ContextMenu1.GetMenuReference() %>" src="...">
  
    GetMenuReference方法返回一段用于调出快捷菜单的脚本。这样页面元素不需要定义成服务器控件也能具有所需的快捷菜单
  
  

原文转自:http://www.ltesting.net