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

类别:Asp 点击:0 评论:0 推荐:
Cutting Edge:为ASP.NET控件加入快捷菜单(3)英文原文:Adding a Context Menu to ASP.NET Controls
作者:Dino Esposito
翻译:刘瑞才
源码:CuttingEdge0502.exe

(续)控件的实现

我们接着要对链接按钮做一些调整,以使其正常工作。 也就是:把链接的宽度设成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方法返回一段用于调出快捷菜单的脚本。这样页面元素不需要定义成服务器控件也能具有所需的快捷菜单

标记绝对位置

这里开发的快捷菜单控件需要绝对位置功能。而这一功能并不是所有的浏览器都支持的。但是,一个支持复杂对象模式和丰富事件的浏览器应该具有高级的定位能力。

就我们关心的Internet Exploer而言,还有其它方式实现快捷菜单。我们不再使用在页面中移动Div的位置这一方法,我们可以创建一个弹出窗口并在指定的位置显示它。然后将DIV动态加载到弹出窗口中呈现为快捷菜单

当实现快捷菜单控件的时候,我首先选择了上述的创建并显示一个弹出窗口。我发现弹出窗口的一些好处,其中之一就是弹出窗口对象不需写代码就自动具有类似桌面快捷菜单的功能:在区域外单击或点击Esc键时自动隐藏。

但是,在Internet Exploer6.0的查看代码功能中出现问题。按照我的测试,在弹出对像中包含快捷菜单改变了页面的元素树并影响查看源代码窗口,不能显示。同样,我想让快捷菜单像交互ASP.NET控件一样可以提交当前页(回传视图状态和输入域)而不只是跳转到指定的URL。从内部弹出窗口提交到父窗口仍然需要更改页面级别的棘手的代码。(这在ASP.NET 2.0中利用跨页提交功能会更简单)。在MSDN的示例中,由于它仅仅时链接到一个外部URL所以它游刃有余。

ASP.NET回传是一个小技巧,在当前页面中使用绝对位置来维持快捷菜单不会改变页面的元素结点树。这样就没有上述的问题了。对于典型解除弹出菜单的事件捕获,你仍可使用鼠标事件捕获。这可能通过使用一系列的Internet Exploer DHTML方法来实现我,参见: How To Create a Mouse Capture Context Menu

(待续)

本文地址:http://com.8s8s.com/it/it6916.htm