EXTJS学习系列提高篇:第十一篇(转载)作者殷良胜,制作树形菜单之五
这个示例实现了有复选框的树形菜单,这个菜单的核心代码是我以前在网上无意中发现的,还好今天派上了用场。本例中不仅仅实现了复选框的树形菜单,而且使得在选中某个节点时,如果有子节点,那么就能够将所有的字节点全部选中,当然你也可以直接选择你希望的节点。但是,当你第一次选中某个有子节点但是子节点并没有展开的时候,就会出现一种例外,就是虽然你选中了这个节点,但该节点的所有子节点没有同时被选中,而如果该节点的子节点都已经展开,则是可以选中该节点对应的所有子节点的;还有第二种情况,就是即使你是在第一次选中某个有子节点但是子节点都已经展开的时候,该选中的节点下面的所有子节点都将被选中。有兴趣的朋友可以自己测试下以修改成适合自己项目的需求。同时,为了增加更好的效果,在本例中也实现了选中某个复选框节点后右键弹出菜单的操作。
下面遵循老传统,还是先看看几幅展示效果图吧,
现在把页面代码完整复制出来,代码较长,本想将关键代码贴出来,觉得那样看起来可能很不爽;或者将代码一段一段的展开或者关闭,但这样以后大家在复制代码的时候就不能够直接使用了,还需要编辑,我很讨厌这样的方式。还是代码全部展示最好。 对相关的不明白的属性可以查看Ext官方文档.具体后台代码和实体类代码同上一篇完全一样,那么在这里就不再列出,有兴趣的朋友可以参照上篇.为了方便大家的操作,我在展示树形菜单系列里面用的都是同一个数据表,同一个后台代码,树形菜单所展示的表的脚本请在树形菜单系列的第一篇里复制.
Code
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="index.aspx.cs" Inherits="Example_TreeAutoLoad_Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>无标题页</title>
<link rel="Stylesheet" type="text/css" href="ExtJS/resources/css/ext-all.css" />
<link rel="Stylesheet" type="text/css" href="ExtJS/resources/css/xtheme-purple.css" />
<script type="text/javascript" src="ExtJS/adapter/ext/ext-base.js"></script>
<script type="text/javascript" src="ExtJS/ext-all.js"></script>
<script type="text/javascript" src="ExtJS/ext-lang-zh_CN.js"></script>
<style type="text/css">
.panel_icon { background-image:url(images/first.gif)}
.center_icon { background-image:url(images/center.png)}
</style>
</head>
<body>
<form id="form1" runat="server">
<div>
<script type="text/javascript">
function makeTree()
{
var url = "Json2.aspx";
var loader = new Ext.tree.TreeLoader
({
http://www.cnblogs.com/mogen_yin/admin/url,
baseAttrs:
{
uiProvider: Ext.tree.TreeCheckNodeUI
}
});
var root = new Ext.tree.AsyncTreeNode
({
id:"0",
text:"系统管理",
leaf:false,
loader:loader,
expandable:true,
expanded:true
});
var tree = new Ext.tree.TreePanel
({
id:"tree",
root:root,
singleExpand:false,
autoHeight:true,
autoWidth:true,
frame:false,
checkModel:'multiple',
onlyLeafCheckable:false,
animate:true
});
var panelTree = new Ext.Panel
({
frame:true,
height:500,
title:"树形菜单之复选框示例",
items:tree,
renderTo:document.body,
collapsible:true
});
tree.on('checkchange', function(node, checked)
{
node.expand();
node.attributes.checked = checked;
node.eachChild(function(child)
{
child.ui.toggleCheck(checked);
child.attributes.checked = checked;
child.fireEvent('checkchange', child, checked);
});
}, tree);
tree.on("contextmenu",function(node,e)
{
var treeMenu = new Ext.menu.Menu
([
{xtype:"button",text:"打开",icon:"images/plugin.gif",pressed:true},
{xtype:"button",text:"添加",icon:"images/plugin.gif",pressed:true},
{xtype:"button",text:"编辑",icon:"images/plugin.gif",pressed:true},
{xtype:"button",text:"隐藏",icon:"images/plugin.gif",pressed:true},
{xtype:"button",text:"删除",icon:"images/plugin.gif",pressed:true}
]);
treeMenu.showAt(e.getPoint());
});
//下面的内容来自网上,如果朋友们看不懂就慢慢看吧,实际上我还没有看呢,但我调试了下功能是完整而且完美的
//实现的功能就是使树形菜单出现复选框
function Ext.tree.TreeCheckNodeUI()
{
this.checkModel = 'multiple';
this.onlyLeafCheckable = false;
Ext.tree.TreeCheckNodeUI.superclass.constructor.apply(this, arguments);
}
Ext.extend(Ext.tree.TreeCheckNodeUI, Ext.tree.TreeNodeUI,
{
renderElements : function(n, a, targetNode, bulkRender)
{
var tree = n.getOwnerTree();
this.checkModel = tree.checkModel || this.checkModel;
this.onlyLeafCheckable = tree.onlyLeafCheckable || false;
// add some indent caching, this helps performance when rendering a large tree
this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
//var cb = typeof a.checked == 'boolean';
var cb = (!this.onlyLeafCheckable || a.leaf);
var href = a.href ? a.href : Ext.isGecko ? "" : "#";
var buf = ['<li class="x-tree-node"><div ext:tree-node-id="',n.id,'" class="x-tree-node-el x-tree-node-leaf x-unselectable ', a.cls,'" unselectable="on">',
'<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
'<img src="', this.emptyIcon, '" class="x-tree-ec-icon x-tree-elbow" />',
'<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : '/>')) : '',
'<a hidefocus="on" class="x-tree-node-anchor" href="',href,'" tabIndex="1" ',
a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", '><span unselectable="on">',n.text,"</span></a></div>",
'<ul class="x-tree-node-ct" style="display:none;"></ul>',
"</li>"].join('');
var nel;
if(bulkRender !== true && n.nextSibling && (nel = n.nextSibling.ui.getEl()))
{
this.wrap = Ext.DomHelper.insertHtml("beforeBegin", nel, buf);
}
else
{
this.wrap = Ext.DomHelper.insertHtml("beforeEnd", targetNode, buf);
}
this.elNode = this.wrap.childNodes[0];
this.ctNode = this.wrap.childNodes[1];
var cs = this.elNode.childNodes;
this.indentNode = cs[0];
this.ecNode = cs[1];
this.iconNode = cs[2];
var index = 3;
if(cb)
{
this.checkbox = cs[3];
Ext.fly(this.checkbox).on('click', this.check.createDelegate(this,[null]));
index++;
}
this.anchor = cs[index];
this.textNode = cs[index].firstChild;
},
// private
check : function(checked)
{
var n = this.node;
var tree = n.getOwnerTree();
this.checkModel = tree.checkModel || this.checkModel;
if( checked === null )
{
checked = this.checkbox.checked;
}
else
{
this.checkbox.checked = checked;
}
n.attributes.checked = checked;
tree.fireEvent('check', n, checked);
if(!this.onlyLeafCheckable && this.checkModel == 'cascade')
{
var parentNode = n.parentNode;
if(parentNode !== null)
{
//this.parentCheck(parentNode,checked);
}
if( !n.expanded && !n.childrenRendered )
{
n.expand(false,false,this.childCheck);
}
else
{
this.childCheck(n);
}
}else if(this.checkModel == 'single')
{
var checkedNodes = tree.getChecked();
for(var i=0;i<checkedNodes.length;i++)
{
var node = checkedNodes[i];
if(node.id != n.id)
{
node.getUI().checkbox.checked = false;
node.attributes.checked = false;
tree.fireEvent('check', node, false);
}
}
}
},
// private
childCheck : function(node)
{
var a = node.attributes;
if(!a.leaf)
{
var cs = node.childNodes;
var csui;
for(var i = 0; i < cs.length; i++)
{
csui = cs[i].getUI();
if(csui.checkbox.checked ^ a.checked)
csui.check(a.checked);
}
}
},
// private
parentCheck : function(node ,checked)
{
var checkbox = node.getUI().checkbox;
if(typeof checkbox == 'undefined')return ;
if(!(checked ^ checkbox.checked))return;
if(!checked && this.childHasChecked(node))return;
checkbox.checked = checked;
node.attributes.checked = checked;
node.getOwnerTree().fireEvent('check', node, checked);
var parentNode = node.parentNode;
if( parentNode !== null){
this.parentCheck(parentNode,checked);
}
},
// private
childHasChecked : function(node)
{
var childNodes = node.childNodes;
if(childNodes || childNodes.length>0)
{
for(var i=0;i<childNodes.length;i++)
{
if(childNodes[i].getUI().checkbox.checked)
return true;
}
}
return false;
},
//private
toggleCheck : function(value)
{
var cb = this.checkbox;
if(cb)
{
var checked = (value === undefined ? !cb.checked : value);
this.check(checked);
}
}
});
}
</script>
<script type="text/javascript">
function ready()
{
makeTree();
}
Ext.onReady(ready);
</script>
</div>
</form>
</body>
</html>
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="index.aspx.cs" Inherits="Example_TreeAutoLoad_Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>无标题页</title>
<link rel="Stylesheet" type="text/css" href="ExtJS/resources/css/ext-all.css" />
<link rel="Stylesheet" type="text/css" href="ExtJS/resources/css/xtheme-purple.css" />
<script type="text/javascript" src="ExtJS/adapter/ext/ext-base.js"></script>
<script type="text/javascript" src="ExtJS/ext-all.js"></script>
<script type="text/javascript" src="ExtJS/ext-lang-zh_CN.js"></script>
<style type="text/css">
.panel_icon { background-image:url(images/first.gif)}
.center_icon { background-image:url(images/center.png)}
</style>
</head>
<body>
<form id="form1" runat="server">
<div>
<script type="text/javascript">
function makeTree()
{
var url = "Json2.aspx";
var loader = new Ext.tree.TreeLoader
({
http://www.cnblogs.com/mogen_yin/admin/url,
baseAttrs:
{
uiProvider: Ext.tree.TreeCheckNodeUI
}
});
var root = new Ext.tree.AsyncTreeNode
({
id:"0",
text:"系统管理",
leaf:false,
loader:loader,
expandable:true,
expanded:true
});
var tree = new Ext.tree.TreePanel
({
id:"tree",
root:root,
singleExpand:false,
autoHeight:true,
autoWidth:true,
frame:false,
checkModel:'multiple',
onlyLeafCheckable:false,
animate:true
});
var panelTree = new Ext.Panel
({
frame:true,
height:500,
title:"树形菜单之复选框示例",
items:tree,
renderTo:document.body,
collapsible:true
});
tree.on('checkchange', function(node, checked)
{
node.expand();
node.attributes.checked = checked;
node.eachChild(function(child)
{
child.ui.toggleCheck(checked);
child.attributes.checked = checked;
child.fireEvent('checkchange', child, checked);
});
}, tree);
tree.on("contextmenu",function(node,e)
{
var treeMenu = new Ext.menu.Menu
([
{xtype:"button",text:"打开",icon:"images/plugin.gif",pressed:true},
{xtype:"button",text:"添加",icon:"images/plugin.gif",pressed:true},
{xtype:"button",text:"编辑",icon:"images/plugin.gif",pressed:true},
{xtype:"button",text:"隐藏",icon:"images/plugin.gif",pressed:true},
{xtype:"button",text:"删除",icon:"images/plugin.gif",pressed:true}
]);
treeMenu.showAt(e.getPoint());
});
//下面的内容来自网上,如果朋友们看不懂就慢慢看吧,实际上我还没有看呢,但我调试了下功能是完整而且完美的
//实现的功能就是使树形菜单出现复选框
function Ext.tree.TreeCheckNodeUI()
{
this.checkModel = 'multiple';
this.onlyLeafCheckable = false;
Ext.tree.TreeCheckNodeUI.superclass.constructor.apply(this, arguments);
}
Ext.extend(Ext.tree.TreeCheckNodeUI, Ext.tree.TreeNodeUI,
{
renderElements : function(n, a, targetNode, bulkRender)
{
var tree = n.getOwnerTree();
this.checkModel = tree.checkModel || this.checkModel;
this.onlyLeafCheckable = tree.onlyLeafCheckable || false;
// add some indent caching, this helps performance when rendering a large tree
this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
//var cb = typeof a.checked == 'boolean';
var cb = (!this.onlyLeafCheckable || a.leaf);
var href = a.href ? a.href : Ext.isGecko ? "" : "#";
var buf = ['<li class="x-tree-node"><div ext:tree-node-id="',n.id,'" class="x-tree-node-el x-tree-node-leaf x-unselectable ', a.cls,'" unselectable="on">',
'<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
'<img src="', this.emptyIcon, '" class="x-tree-ec-icon x-tree-elbow" />',
'<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : '/>')) : '',
'<a hidefocus="on" class="x-tree-node-anchor" href="',href,'" tabIndex="1" ',
a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", '><span unselectable="on">',n.text,"</span></a></div>",
'<ul class="x-tree-node-ct" style="display:none;"></ul>',
"</li>"].join('');
var nel;
if(bulkRender !== true && n.nextSibling && (nel = n.nextSibling.ui.getEl()))
{
this.wrap = Ext.DomHelper.insertHtml("beforeBegin", nel, buf);
}
else
{
this.wrap = Ext.DomHelper.insertHtml("beforeEnd", targetNode, buf);
}
this.elNode = this.wrap.childNodes[0];
this.ctNode = this.wrap.childNodes[1];
var cs = this.elNode.childNodes;
this.indentNode = cs[0];
this.ecNode = cs[1];
this.iconNode = cs[2];
var index = 3;
if(cb)
{
this.checkbox = cs[3];
Ext.fly(this.checkbox).on('click', this.check.createDelegate(this,[null]));
index++;
}
this.anchor = cs[index];
this.textNode = cs[index].firstChild;
},
// private
check : function(checked)
{
var n = this.node;
var tree = n.getOwnerTree();
this.checkModel = tree.checkModel || this.checkModel;
if( checked === null )
{
checked = this.checkbox.checked;
}
else
{
this.checkbox.checked = checked;
}
n.attributes.checked = checked;
tree.fireEvent('check', n, checked);
if(!this.onlyLeafCheckable && this.checkModel == 'cascade')
{
var parentNode = n.parentNode;
if(parentNode !== null)
{
//this.parentCheck(parentNode,checked);
}
if( !n.expanded && !n.childrenRendered )
{
n.expand(false,false,this.childCheck);
}
else
{
this.childCheck(n);
}
}else if(this.checkModel == 'single')
{
var checkedNodes = tree.getChecked();
for(var i=0;i<checkedNodes.length;i++)
{
var node = checkedNodes[i];
if(node.id != n.id)
{
node.getUI().checkbox.checked = false;
node.attributes.checked = false;
tree.fireEvent('check', node, false);
}
}
}
},
// private
childCheck : function(node)
{
var a = node.attributes;
if(!a.leaf)
{
var cs = node.childNodes;
var csui;
for(var i = 0; i < cs.length; i++)
{
csui = cs[i].getUI();
if(csui.checkbox.checked ^ a.checked)
csui.check(a.checked);
}
}
},
// private
parentCheck : function(node ,checked)
{
var checkbox = node.getUI().checkbox;
if(typeof checkbox == 'undefined')return ;
if(!(checked ^ checkbox.checked))return;
if(!checked && this.childHasChecked(node))return;
checkbox.checked = checked;
node.attributes.checked = checked;
node.getOwnerTree().fireEvent('check', node, checked);
var parentNode = node.parentNode;
if( parentNode !== null){
this.parentCheck(parentNode,checked);
}
},
// private
childHasChecked : function(node)
{
var childNodes = node.childNodes;
if(childNodes || childNodes.length>0)
{
for(var i=0;i<childNodes.length;i++)
{
if(childNodes[i].getUI().checkbox.checked)
return true;
}
}
return false;
},
//private
toggleCheck : function(value)
{
var cb = this.checkbox;
if(cb)
{
var checked = (value === undefined ? !cb.checked : value);
this.check(checked);
}
}
});
}
</script>
<script type="text/javascript">
function ready()
{
makeTree();
}
Ext.onReady(ready);
</script>
</div>
</form>
</body>
</html>
再强调下为了方便大家的操作,我在展示树形菜单系列里面用的都是同一个数据表,同一个后台代码,树形菜单所展示的表的脚本请在树形菜单系列的第一篇里复制.