中国IT动力,最新最全的IT技术教程
最新100篇 | 推荐100篇 | 专题100篇 | 排行榜 | 搜索 | 在线API文档
首 页 | 程序开发 | 操作系统 | 软件应用 | 图形图象 | 网络应用 | 精文荟萃 | 教育认证 | 硬件维护 | 未整理篇 | 站长教程
ASP JS PHP工程 ASP.NET 网站建设 UML J2EESUN .NET VC VB VFP 网络维护 数据库 DB2 SQL2000 Oracle Mysql
服务器 Win2000 Office C DreamWeaver FireWorks Flash PhotoShop 上网宝典 CorelDraw 协议大全 网络安全 微软认证
硬件维护  CPU  主板  硬盘  内存  显卡  显示器  键盘鼠标  声卡音箱  打印机  机箱电源  BIOS  网卡  C#  Java  Delphi  vs.net2005
  当前位置:> 程序开发 > 编程语言 > Visual C++ > XML
用XML和XSLT进行高级的Web UI设计(二)
作者:未知 时间:2005-07-20 14:21 出处:VC知识库 责编:chinaitpower
              摘要:用XML和XSLT进行高级的Web UI设计(二)

用XML和XSLT进行高级的Web UI设计(二)

编译:alpha2002

定制目录树的上下文菜单

本文要求读者熟悉JScript,MSXML,XSL/XSLT,DOM。

下载例子代码

这是系列文章的第二篇,主要介绍如何在目录树中创建上下文菜单。本文中所使用的目录树结构就是我们在前面第一篇文章中所创建的目录树结构。在它的基础上引入上下文菜单特性。
在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文件描述。当然,这些菜单可以是动态的,比如从数据库中读出记录进行处理。
      <?xml version="1.0" encoding="gb2312"?>
<menu>
  <entity id="c1">
    <description>添加一期在线杂志</description>
    <image>images/add_small.gif</image>
    <contents>
      <entity id="c2">
        <description>一般文章</description>
        <image>images/spacer.gif</image>
        <contents>
          <entity id="c2">
	     <description>数据库</description>
	     <image>images/spacer.gif</image>
	     <contents>
	     </contents>
	  </entity>
	    <entity id="c3">
	      <description>COM/COM+</description>
	      <image>images/spacer.gif</image>
	      <contents>
	      </contents>
	    </entity>
        </contents>
      </entity>
      <entity id="c3">
        <description>个人专栏</description>
        <image>images/spacer.gif</image>
        <contents>
          <entity id="c2">
	     <description>wangjun专栏</description>
	     <image>images/spacer.gif</image>
	     <contents>
	     </contents>
	  </entity>
	    <entity id="c3">
	      <description>hangwire专栏</description>
	      <image>images/spacer.gif</image>
	      <contents>
	      </contents>
	    </entity>
        </contents>
      </entity>
    </contents>
  </entity>
</menu>

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

XSLT 风格页

下面是应用到上下文菜单XML文档的标准的XSLT风格页:
      <xsl:stylesheet version="1.1"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
xmlns:dt="urn:schemas-microsoft-com:datatypes">
<xsl:template match="menu">
<div style="position: absolute;">
  <div onselectstart="return false" ondragstart="return false">
  <xsl:attribute name="STYLE">
    position: absolute;
    background-color: #6699cc;
    border:1px solid #99ccff;
  </xsl:attribute>
  <table border="0" cellspacing="0" cellpadding="1">
    <tr>
      <td>
        <table border="0" cellspacing="0" cellpadding="0">
          <xsl:apply-templates select="entity"/>
        </table>
      </td>
    </tr>
  </table>
  </div>
  <xsl:apply-templates select="entity/contents"/>
</div>
</xsl:template>

<xsl:template match="entity">
<TR>
<xsl:attribute name="selected">false</xsl:attribute>
<xsl:attribute name="background">#6699cc</xsl:attribute>
<xsl:attribute name="light">#99ccff</xsl:attribute>
<xsl:attribute name="titlebar">#5389bc</xsl:attribute>
<xsl:attribute name="image">images/<xsl:value-of select="image"/></xsl:attribute>
<xsl:attribute name="imageOpen">images/<xsl:value-of select="imageOpen"/></xsl:attribute>
<xsl:attribute name="id"><xsl:value-of select="@id"/></xsl:attribute>
<xsl:attribute name="ONCLICK"><xsl:value-of select="onClick"/>;clean()</xsl:attribute>
<xsl:attribute name="ONMOUSEOVER">
  contextHighlightRow(this);
  <xsl:if test="contents/node()[count(child::*)>0]">
    loadContextMenuSub(this)
  </xsl:if>
</xsl:attribute>
<xsl:attribute name="ONMOUSEOUT">contextHighlightRow(this)</xsl:attribute>
  <TD VALIGN="MIDDLE" ALIGN="CENTER" NOWRAP="true">
  <xsl:attribute name="ONCLICK"><xsl:value-of select="@onmousedown"/></xsl:attribute>
  <xsl:attribute name="STYLE">
    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;
  </xsl:attribute>
  <IMG BORDER="0" HEIGHT="15" WIDTH="15">
    <xsl:attribute name="SRC"><xsl:value-of select="image"/></xsl:attribute>
  </IMG></TD>
  <TD NOWRAP="true">
  <xsl:attribute name="ONCLICK"><xsl:value-of select="@onmousedown"/></xsl:attribute>
  <xsl:attribute name="STYLE">
    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;
  </xsl:attribute>
  <xsl:value-of select="description"/></TD>
  <TD VALIGN="middle" ALIGN="right" STYLE="padding-right: 6px;" NOWRAP="true">
  <xsl:attribute name="ONCLICK"><xsl:value-of select="@onmousedown"/></xsl:attribute>
  <xsl:attribute name="STYLE">
    background-color: #6699cc;
    border-top: 1px solid #6699cc;
    border-bottom: 1px solid #6699cc;
    border-right: 1px solid #6699cc;
    padding-right: 5px;
  </xsl:attribute>
  <IMG BORDER="0" WIDTH="4">
  <xsl:attribute name="SRC">
    <xsl:choose>
      <xsl:when test="contents/node()[count(child::*)>0]">
        images/opensub.gif
      </xsl:when>
      <xsl:otherwise>
        images/spacer.gif
      </xsl:otherwise>
    </xsl:choose>
  </xsl:attribute>
  </IMG></TD>
</TR>
</xsl:template>

<xsl:template match="contents">
  <xsl:if test="count(child::*)>0">
  <div onselectstart="return false" ondragstart="return false">
  <xsl:attribute name="STYLE">
    position: absolute;
    background-color: #6699cc;
    border:1px solid #99ccff;
    display: none;
  </xsl:attribute>
  <xsl:attribute name="ID"><xsl:value-of select="../@id"/>Sub</xsl:attribute>
  <table border="0" cellspacing="0" cellpadding="1">
    <tr>
      <td>
        <table border="0" cellspacing="0" cellpadding="0">
          <xsl:apply-templates select="entity"/>
        </table>
      </td>
    </tr>
  </table>
  </div>
  <xsl:apply-templates select="entity/contents"/>
  </xsl:if>
</xsl:template>
</xsl:stylesheet>


图二 应用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应用程序界面的质量。我们在下一篇文章中将继续上下文菜单的内容,讨论如何真正实现目录树上下文菜单的各种操作,如插入、修改、删除以及更改菜单项的名称等。(待续)
关闭本页
 
首页 | 投资与合作 | 服务条款 | 隐私政策 | 收藏本站 | 设为首页 | 新用户注册 | 免责声明 | 使用帮助
Copyright ©2005-2008 chinaitpower.com All rights reserved. www.chinaitpower.com 版权所有