TreeView 上的上下文菜单






4.80/5 (12投票s)
Microsoft Treeview 控件在节点上的事件捕获

引言
树形结构是当今交互式 Web 开发的一部分。这些结构允许我们在网页上显示层级信息。要实现这样的结构,没有什么比使用 Microsoft 在 ASP.NET 2.0 中附带的树视图控件更容易的了。这个树控件能够使用层级数据源(如 XML 文件)然后显示信息。
客户端用户可以展开和折叠节点,并点击节点向服务器发送请求。如今对用户交互的需求远高于树视图控件所能提供的。如果程序员能够捕获其他几种客户端事件,从而为用户提供树形结构上更高级的功能,那么这个控件将具有更大的灵活性。
在本文中,我将展示:
- 如何捕获树节点上的右键点击事件
- 如何在右键点击时生成自定义上下文菜单
背景
我们知道,所有 Microsoft 的服务器端控件在浏览器上都会被渲染成 HTML/DHTML 与 JavaScript 的组合。如果我们可以将 JavaScript 与树视图控件渲染的 HTML 结合起来,我们就能完成我们的任务。
树视图 HTML 渲染
程序员可以决定借助 Visual Studio 中的可用工具来创建树视图,或者在源代码中编写树视图。这是我们将在示例中使用的树视图:

ASP 代码
<asp:TreeView ID="TreeView1" runat="server" BackColor="#FFFFC0" Height="180px"
Width="172px">
<Nodes>
<asp:TreeNode Text="Root" Value="sroot">
<asp:TreeNode Text="Parent 1" Value="sParent 1">
<asp:TreeNode Text="this is a test" Value="sLeaf 1" >
</asp:TreeNode>
<asp:TreeNode Text="Leaf 2" Value="sLeaf2">
</asp:TreeNode>
</asp:TreeNode>
<asp:TreeNode Text="Parent 2" Value="sParent2">
<asp:TreeNode Text="Leaf 1" Value="sLeaf 1">
</asp:TreeNode>
<asp:TreeNode Text="Leaf 2" Value="sLeaf 2">
</asp:TreeNode>
</asp:TreeNode>
</asp:TreeNode>
</Nodes>
</asp:TreeView>
在浏览器上查看时,整个树被渲染成一个带有 JavaScript 的嵌套 HTML TABLE
和 DIV
。一个典型的树节点...
<asp:TreeNode Text="this is a test" Value="sLeaf 1" ></asp:TreeNode>
...被渲染为:
<table cellpadding="0" cellspacing="0" style="border-width:0;">
<tr>
<td>
<div style="width:20px;height:1px"></div>
</td>
<td>
<div style="width:20px;height:1px"></div>
</td>
<td>
<img
src="http://www.mydomain.com/xTreeView/
WebResource.axd?d=EiQ7rX7B8TmWKbku-S5G9ciFFidk-r05cDK5-
8iUkl41&t=633045505810781250" alt="" />
</td>
<td style="white-space:nowrap;">
<a class="TreeView1_0"
href="javascript:__doPostBack('TreeView1',
'ssroot\\sParent 1\\sLeaf 1')"
onclick="TreeView_SelectNode(TreeView1_Data, this,
'TreeView1t2');" id="TreeView1t2"> this is a test
</a>
</td>
</tr>
</table>
技巧
正如我们所见,节点文本“this is a test”被渲染在一个锚点 <a>
标签内。有许多支持 JavaScript 的文本格式化标签可以嵌套在锚点标签内。例如,粗体标签 <b>
。如果我们可以将节点文本包裹在可以嵌套于 <a></a>
标签内的标签中,HTML 仍然是正确的格式,这样节点的 HTML 渲染就会像这样:
<a class="TreeView1_0"
href="javascript:__doPostBack('TreeView1','ssroot\\sParent
1\\sLeaf 1')" onclick="TreeView_SelectNode(TreeView1_Data,
this,'TreeView1t2');" id="TreeView1t2">
<b> this is atest</b>
</a>
粗体标签也支持许多浏览器事件,如点击、双击、鼠标悬停等。任何事件都可以在标签中使用,但我们感兴趣的是 oncontextmenu
事件。当用户在浏览器上右键点击以调出上下文菜单时,这个事件就会触发。因此,在我们的标签中,我们可以在这个事件上调用任何 JavaScript 函数。这样,我们的标签现在看起来像这样:
<b oncontextmenu="return showmenuie5(event)" > this is a test</b>
通过将完整的 HTML 作为节点文本写入,可以轻松实现这种 HTML 渲染。因此,在树代码中,节点看起来像这样:
<asp:TreeNode Text="<b id='b1' oncontextmenu="return
showmenuie5(event)" > this is a test</b>"
Value="sLeaf 1" >
</asp:TreeNode>
或者,您可以在节点的 text 属性中写入 HTML:
<b oncontextmenu="return showmenuie5(event)" > this is a test</b>
构建菜单
一旦我们能够捕获 contextmenu
事件,我们就希望生成自己的菜单,而不是默认的浏览器上下文菜单。我们可以在 DIV
标签内编写任何 HTML 组合。这个 DIV
分区/区域可以借助 JavaScript 在文档中四处移动。
我们也可以使用 ASP panel 控件。当这个控件被渲染时,它会被渲染成一个具有相同 ID(即 panel 控件的 ID)的 DIV
分区。因此,我们可以安全地使用这个 ID 来编写我们的 JavaScript 函数。使用 panel 可以更简单地构建菜单,并通过 ASP.NET 2.0 的特性来控制菜单对象。我们在本例中使用的菜单是:

在 ASP 代码中,我们希望添加更多功能以使菜单更具交互性:
<asp:Panel ID="Panel2" runat="server" display:none BorderColor="Black"
CssClass="skin0" onMouseover="highlightie5(event)"
onMouseout="lowlightie5(event)" onClick="jumptoie5(event)" >
<div class="menuitems"><asp:LinkButton ID="LinkButton1" runat="server"
CssClass="menuitems" >New Node</asp:LinkButton></div>
<div class="menuitems"><asp:LinkButton ID="LinkButton2" runat="server"
CssClass="menuitems">Edit Node</asp:LinkButton></div>
<hr />
<div class="menuitems"><asp:LinkButton ID="LinkButton3" runat="server"
CssClass="menuitems">Delete Node</asp:LinkButton></div>
<hr />
<div class="menuitems"><asp:LinkButton ID="LinkButton4" runat="server"
CssClass="menuitems">FAQS</asp:LinkButton></div>
<div class="menuitems"><asp:LinkButton ID="LinkButton5" runat="server"
CssClass="menuitems">Online Help</asp:LinkButton></div>
<hr />
<div class="menuitems"><asp:LinkButton ID="LinkButton6" runat="server"
CssClass="menuitems">Email Me</asp:LinkButton></div>
</asp:Panel>
JavaScript 概述
在我们开始为上下文菜单函数编写 JavaScript 之前,我想概述一些 JavaScript 对象及其属性,这些将帮助我们实现右键菜单。
Document:Document 是许多其他对象的父对象,例如 "images"、"forms" 等。 |
|
|
一个跨浏览器(IE5+/NS6+)的 DOM 方法,用于通过元素的 ID 属性访问页面上的任何元素。 |
|
分别指定窗口内容区域的宽度和高度(以像素为单位)。可读写。不包括工具栏、滚动条等。 NS4 和 NS6 的等效项是:
|
|
分别返回一个整数,表示当前文档从窗口左上角水平和垂直滚动的像素值。NS4 和 NS6+、IE4+ 的等效项是 |
Event:Event 对象(IE5+/NS6+)跟踪页面上发生的各种事件,例如用户移动鼠标或点击链接,并允许程序员对这些事件做出反应。 |
|
|
返回事件发生时相对于窗口左上角的鼠标坐标。 |
|
设置为 true 以阻止事件冒泡。 NS 的等效项是 |
|
检索对象的宽度和高度,相对于由 offsetParent 属性指定的布局或坐标父级。 |
Style:DOM 的 Style 对象允许您动态更改 CSS 属性的值,无论这些属性是内联定义还是通过外部样式表定义。更改会立即反映在页面上。 |
事件冒泡
Internet Explorer 4.0x+ 最初将事件导向其预定目标。例如,如果一个按钮被点击,点击事件就被导向该按钮。如果为该对象定义了事件处理程序,事件就会调用它。如果没有定义事件处理程序来处理该事件,或者事件处理程序没有返回 false(以取消事件),事件将继续传递给父对象进行处理。事件会沿着对象层次结构向上冒泡,直到被处理,或者直到达到最顶层,即 document 对象。
我们能够捕获 oncontextmenu
事件,并借助我们的 JavaScript,我们也能够生成一个自定义菜单。然而,与此同时,我们需要阻止浏览器默认的菜单弹出。Internet Explorer 4.0x+ 新事件模型中最重要的特性是事件冒泡机制。为了取消特定事件的冒泡,我们必须将事件对象的 cancelBubble
属性设置为 true
。
window.event.cancelBubble = true;
cancelBubble
是一个读写属性,接受一个布尔值。它的默认值是 false
,表示事件应照常在层次结构中向上冒泡。然而,如果明确设置为 true
,事件就不会冒泡,从而阻止层次结构中的下一个事件处理程序接收到该事件。
另一种阻止事件冒泡的方法是在其事件处理程序脚本或事件处理函数中返回 false
来完全取消该事件。然而,与取消事件冒泡不同,如果您取消事件本身,与该事件相关的默认操作将不会发生。
JavaScript 函数 showmenuie5(event)
var ie5=document.all&&document.getElementById
var ns6=document.getElementById&&!document.all
if (ie5||ns6)
var menuobj=document.getElementById("Panel2")
function showmenuie5(e){
//Find out how close the mouse is to the corner of the window
var rightedge=ie5? document.body.clientWidth-event.clientX :
window.innerWidth-e.clientX
var bottomedge=ie5? document.body.clientHeight-event.clientY :
window.innerHeight-e.clientY
//if the horizontal distance isn't enough to accomodate the width of
//the context menu
if (rightedge<menuobj.offsetWidth)
//move the horizontal position of the menu to the left by it's width
menuobj.style.left=ie5?
document.body.scrollLeft+event.clientX-menuobj.offsetWidth :
window.pageXOffset+e.clientX-menuobj.offsetWidth
else
//position the horizontal position of the menu where the mouse was clicked
menuobj.style.left=ie5? document.body.scrollLeft+event.clientX :
window.pageXOffset+e.clientX
//same concept with the vertical position
if (bottomedge<menuobj.offsetHeight)
menuobj.style.top=ie5?
document.body.scrollTop+event.clientY-menuobj.offsetHeight :
window.pageYOffset+e.clientY-menuobj.offsetHeight
else
menuobj.style.top=ie5? document.body.scrollTop+event.clientY :
window.pageYOffset+e.clientY
if(ie5)
window.event.cancelBubble = true;
else if(ns6)
e.stopPropagation();
menuobj.style.visibility="visible"
return false
}
输出

尽管本文演示了如何在树节点上捕获右键点击事件并生成菜单,但本文中的概念可以应用于网页上许多其他的用户交互效果,例如在链接上实现鼠标悬停效果以显示小信息对话框,网页上的可拖动对话框等。
历史
- 2007年5月26日 -- 原始版本发布