Chinaunix首页 | 论坛 | 博客
  • 博客访问: 9601154
  • 博文数量: 1227
  • 博客积分: 10026
  • 博客等级: 上将
  • 技术积分: 20273
  • 用 户 组: 普通用户
  • 注册时间: 2008-01-16 12:40
文章分类

全部博文(1227)

文章存档

2010年(1)

2008年(1226)

我的朋友

分类: C/C++

2008-04-02 15:10:38

下载本文示例代码
这是系列文章的第二篇,主要介绍如何在目录树中创建上下文菜单。本文中所使用的目录树结构就是我们在前面第一篇文章中所创建的目录树结构。在它的基础上引入上下文菜单特性。
在Windows应用程序中,窗口对象的上下文菜单无处不在,只要将鼠标指针移到某个应用程序的窗口对象上,然后单击右键就可以弹出该对象的上下文菜单。对于不同的应用程序,以及同一个应用程序中不同的窗口对象来说,其上下文菜单是不一样的。早期的IE浏览器没有让开发人员创建对象上下文菜单的能力。自从引入的文档对象模型(DOM)之后。使得开发人员在Web应用程序中创建上下文菜单成为可能。
本文将示范如何用XML和XSLT针对特定对象创建定制的上下文菜单,并且菜单的级数是没有限制的。其建立机制与树型目录的建立一样,通过特定的XSL风格页将定义好的目录树XML文件转换成满足要求的HTML推送给客户端浏览器(IE5.5+)显示。客户端负责处理所有对菜单的导航操作。
Windows中的上下文菜单可以适用于任何Web页面对象。本文所创建的菜单是针对我们在上一篇文章中所创建的目录树增加的新UI特性。
在上一篇文章里,我们曾提到过只要在描述目录树结构的XML文件的entity元素中加入一个 "oncontextmenu" 元素,然后在这个元素里请求一个定制的XML上下文菜单描述文件。便可以轻松实现目录树的上下文菜单。菜单的源代码可以从本文的链接下载。

图一 目录树上下文菜单运行例子

描述上下文菜单结构的XML 文件

与描述目录树结构的XML文件类似,这里所选则的上下文菜单格式能很好地适用于XSLT进行递归处理,从而满足和实现层次无限的上下文菜单的需要。
上下文菜单结构的XML文档有一个根元素"menu",此根元素下包含一个元素"entity"。然后在"entity"元素中定义"contents"子元素,整个上下文菜单的结构是通过在每一层entity元素的"contents"子元素中嵌套的entity元素来实现的。下面是"entity"元素中包含的关键所有元素或属性的清单。

名称 类型 描述
id 属性 上下文菜单或菜单选项的id号,唯一标示
description 元素 描述菜单或菜单选项的说明文本
onClick 元素 客户端触发onClick事件的函数名
image 元素 表示菜单选项的图像
contents 元素 包含entity子元素,其内容用以确定嵌套的entity元素是否有上下文子菜单

下面是描述上下文菜单结构的XML文档,当用户在目录树的某个节点单击右键,便会弹出在此节点定义菜单。每一个节点的上下文菜单可以是用不同的XML文件描述。当然,这些菜单可以是动态的,比如从数据库中读出记录进行处理。
      

  
    添加一期在线杂志
    images/add_small.gif
    
      
        一般文章
        images/spacer.gif
        
          
      数据库
      images/spacer.gif
      
      
   
     
       COM/COM+
       images/spacer.gif
       
       
     
        
      
      
        个人专栏
        images/spacer.gif
        
          
      wangjun专栏
      images/spacer.gif
      
      
   
     
       hangwire专栏
       images/spacer.gif
       
       
     
        
      
    
  


      
上面这个XML 文件名为"contextOnlineJnlFolder.xml",可以在下载代码中找到。下面我们来讨论如何在客户端浏览器中显示目录树结构中的上下文菜单。

XSLT 风格页

下面是应用到上下文菜单XML文档的标准的XSLT风格页:
      

position: absolute; background-color: #6699cc; border:1px solid #99ccff;
false #6699cc #99ccff #5389bc images/ images/ ;clean() contextHighlightRow(this); loadContextMenuSub(this) contextHighlightRow(this) background-color: #5389bc; border-top:1px solid #5389bc; border-bottom:1px solid #5389bc; border-left:1px solid #5389bc; padding-left: 4px; padding-right: 4px; padding-top: 4px; padding-bottom: 3px; cursor: default; font-family: Arial; font-size: 11px; font-weight: normal; color: white; background-color: #6699cc; border-top: 1px solid #6699cc; border-bottom: 1px solid #6699cc; padding-top: 2px; padding-bottom:2px; padding-left: 6px; padding-right: 8px; cursor: default; background-color: #6699cc; border-top: 1px solid #6699cc; border-bottom: 1px solid #6699cc; border-right: 1px solid #6699cc; padding-right: 5px; images/opensub.gif images/spacer.gif
position: absolute; background-color: #6699cc; border:1px solid #99ccff; display: none; Sub

图二 应用XSLT风格页显示的上下文菜单

用户端的操作

为了让目录树的上下文菜单按照我们思路运行,还需要在客户端做一些工作:例如加载菜单、加载子菜单、加亮菜单选项、清除打开的菜单等功能……。归纳起来就是下面这些脚本函数,这些函数的代码都在context.js文件中。

loadContextMenu 加载上下文菜单
loadContextMenuSub 加载上下文子菜单
contextHighlightRow 加亮菜单项
clean 清除打开的菜单项
returnContainer 辅助函数

这些函数的源代码如下:
      var appState = new applicationState()
function applicationState() {
  this.contextMenu = null
}

function loadContextMenu(path) {
  var xmlDoc
  var xslDoc
  var contextMenu

  if(path != "") {
    xmlDoc = new ActiveXObject(''''Microsoft.XMLDOM'''')
    xmlDoc.async = false;

    xslDoc = new ActiveXObject(''''Microsoft.XMLDOM'''')
    xslDoc.async = false;

    xmlDoc.load(path)
    xslDoc.load("context/context.xsl")

    if(appState.contextMenu != null) appState.contextMenu.removeNode(true)
  
    document.body.insertAdjacentHTML("beforeEnd", xmlDoc.documentElement.transformNode(xslDoc))
    contextMenu = document.body.childNodes(document.body.childNodes.length-1)

    contextMenu.style.left = window.event.x
    contextMenu.style.top = window.event.y

    appState.contextMenu = contextMenu
    window.event.cancelBubble = true
  }
}

function loadContextMenuSub(obj) {
  var contextMenu
  var parentMenu

  parentMenu = returnContainer(obj)
  contextMenu = document.all[obj.id + "Sub"]
  contextMenu.style.display = "block"
  contextMenu.style.top = obj.offsetTop + parentMenu.style.pixelTop
  contextMenu.style.left = obj.offsetWidth + parentMenu.style.pixelLeft
  parentMenu.subMenu = contextMenu
}

function contextHighlightRow(obj) {
  var parentMenu
  var subMenu
  var i

  parentMenu = returnContainer(obj)

  if(obj.selected == "false") {
    for(i=0; i < obj.childNodes.length; i++) {
      obj.childNodes(i).style.borderTop = "1px solid white"
      obj.childNodes(i).style.borderBottom = "1px solid white"

      if(obj.childNodes(i).cellIndex == 0) {
        obj.childNodes(i).style.borderLeft = "1px solid white"
      }
      else if (obj.childNodes(i).cellIndex == obj.cells.length-1) {
        obj.childNodes(i).style.borderRight = "1px solid white"
      }
    }

    if(parentMenu.subMenu != null && parentMenu != parentMenu.subMenu) {
      subMenu = parentMenu.subMenu

      while(subMenu != null) {
        subMenu.style.display = "none"
        subMenu = subMenu.subMenu
      }
    }
    obj.selected = "true"
  }
  else {
    for(i=0; i < obj.childNodes.length; i++) {
      if(i == 0) {
        obj.childNodes(i).style.borderTop = "1px solid " + obj.titlebar
        obj.childNodes(i).style.borderBottom = "1px solid " + obj.titlebar
      }
      else {
        obj.childNodes(i).style.borderTop = "1px solid " + obj.background
        obj.childNodes(i).style.borderBottom = "1px solid " + obj.background
      }
      
      if(obj.childNodes(i).cellIndex == 0) {
        obj.childNodes(i).style.borderLeft = "1px solid " + obj.titlebar
      }
      else if (obj.childNodes(i).cellIndex == obj.cells.length-1) {
        obj.childNodes(i).style.borderRight = "1px solid " + obj.background
      }
    }
    obj.selected = "false"
  }
}

function clean() {
  var contextMenu
  
  // remove and destroy context menu
  if(appState.contextMenu != null) {
    contextMenu = appState.contextMenu.removeNode(true)
    contextMenu = null
  }
}

function returnContainer(container) {
  while(container.tagName != "DIV") {
    container = container.parentNode  
  }
  return container
}      
菜单操作是很多Windows应用程序不可或缺的UI特性。希望通过本文提供的技术能进一步提高你的Web应用程序界面的质量。我们在下一篇文章中将继续上下文菜单的内容,讨论如何真正实现目录树上下文菜单的各种操作,如插入、修改、删除以及更改菜单项的名称等。(待续)
下载本文示例代码
阅读(1050) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~