1.加图标和事件
上次已经通过DataSimulateTreeLevelField属性控制了树节点的显示,不用看也知道就是给指定列数据前面加个图标的HTML
可以在SimulateTreeHeper类里看到实现方法,不多说了,原理就是循环行累加上图片就可以了,注意图片的ID和样式
/// <summary> /// 给树赋值 /// </summary> private void SetValueTree() { if (EnableTree) { foreach (GridRow row in Rows) { //下级行数 string Nextindex = GetNextIndex(row, Rows); //默认图标 string iconname = "page.png"; //图片ID,点击用,绑定事件 string imgid = ClientID+"_tree_icon_" + row.RowIndex; //父节点图标 if (Nextindex != "") { iconname = "folder.png"; } //加入图标和ID,注意加过的就不加了 if (row.Values[FindColumn(TreeName).ColumnIndex].ToString().IndexOf(ClientID+"_tree_icon_") < 0) { row.Values[FindColumn(TreeName).ColumnIndex] = "<img id=\"" + imgid + "\" src=\"..\\res\\icon\\" + iconname + "\" width=\"16\" height=\"16\" style=\"margin-top:-5px;margin-bottom:-2px;cursor: pointer\"/>" + row.Values[1]; } } } } /// <summary> /// 得到下级行号 /// </summary> /// <param name="row">本节点</param> /// <param name="Rows">集合</param> /// <returns>集合以,隔开</returns> private string GetNextIndex(GridRow row, GridRowCollection Rows) { string topindex = ""; int pridindex = FindColumn(TreeDataParentIDField).ColumnIndex; int idindex = FindColumn(TreeDataIDField).ColumnIndex; foreach (GridRow gr in Rows) { //父ID等于本ID添加到集合 if (gr.Values[pridindex].ToString() != "" && gr.Values[pridindex].ToString() == row.Values[idindex].ToString()) { topindex += topindex == "" ? gr.RowIndex.ToString() : "," + gr.RowIndex.ToString(); } } return topindex; }
这里有个方法是得到下级的行号,这是个核心方法。
在AfterDataBind里加上就可以了
下面就是点击方法,用JQ实现的,从后台输出
/// <summary> /// 点击事件只给有子集的点击事件 /// </summary> private void TreeClick() { StringBuilder sbx = new StringBuilder(); foreach (GridRow row in Rows) { //有下级菜单才绑定事件 string Nextindex = GetNextIndex(row, Rows); if (Nextindex != "") { string imgid = ClientID + "_tree_icon_" + row.RowIndex; sbx.Append("$('#" + imgid + "').click(function(){F.customEvent(\"GridTreeClick_" + row.RowIndex + "\");});"); } } PageContext.RegisterStartupScript(sbx.ToString()); }
还是用到了F.customEvent方法回发后台,其实可以用doPostBack方法直接在Grid.cs里执行,但是考虑到会执行其他方法还是给放到页面的cs文件了,
grid_tree.cs
protected void PageManager1_CustomEvent(object sender, CustomEventArgs e) { if (e.EventArgument.IndexOf("GridTreeClick_") >= 0) { string rowindex = e.EventArgument.ToString().Split('_')[1]; Grid1.DoTreeClick(rowindex); } }
可以看到DoTreeClick就是点击树图标的方法,这个方法 实现收起和展开。
2.实现展开和收起
在DoTreeClick之前先要有第一次加载方法。
首先要先定义一个参数,这个参数记录应该删除的行号,前面已经说是通过删除行实现收起和展开的
private static string _moveindex;//移除的数据 /// <summary> /// 移除的数据 /// </summary> public static string moveindex { get { return _moveindex; } set { _moveindex = value; } }
可以看到这个参数是static的不会平白消失,FineUI控制属性的Ajax回发是在构造函数里,
有兴趣的可以研究下,我还没太懂
先写一个第一次加载的方法,实现绑定了所有数据但只显示第一层根节点。
/// <summary> /// 第一次显示数方法 /// </summary> private void ShowTree() { //初始化树 if (EnableTree) { List<string> movelist = new List<string>(); //循环行 foreach (GridRow row in Rows) { //得到层级 int lub = GetLevelNub(row, Rows); if (lub != 0) { //子集删除 movelist.Add(row.RowIndex.ToString()); } } //排序(重要) 从大到小排序,不排序会删除1后造成2变成1 movelist.Sort(delegate(string str1, string str2) { return int.Parse(str2) - int.Parse(str1); }); string MoveRowIndex = ""; foreach (string s in movelist) { MoveRowIndex += MoveRowIndex == "" ? s : "," + s; } if (MoveRowIndex != null) { //放入缓存记录已经消除的行 moveindex = MoveRowIndex; string js = "" + "var store =" + XID + ".getStore();" + "var rows = [" + MoveRowIndex + "];" + "Ext.Array.each(rows, function (rowIndex, index) {" + " store.removeAt(rowIndex);" + "});" //刷新行号,(重要) + XID + ".view.refresh();"; PageContext.RegisterStartupScript(js); } //绑定树点击事件 TreeClick(); } }
逻辑就是得到删除的行号,排序,缓存,js实现,注意刷新行号方法和XID的意义。
加上方法TreeClick方法写在了ShowTree里
下面就可以做展开和收起了,再建一个参数记录展开的行号,专业版实例里可以通过属性控制指定列的展开,我控制这个参数可以实现(这个方法没有写只是理论上的...)
private static List<string> _RowCollapsed;//展开集合 /// <summary> /// 展开集合 /// </summary> public static List<string> RowCollapsed { get { return _RowCollapsed; } set { _RowCollapsed = value; } }
其实这里可以留扣了,逻辑说出来也不复杂,就都一次写出来吧
/// <summary> /// 点击树事件 /// </summary> /// <param name="treenode">点击的节点(行号)</param> public void DoTreeClick(string treenode) { if (EnableTree) { StringBuilder sb = new StringBuilder(); List<string> movelist = null; if (movelist == null) { //集合 if (RowCollapsed == null) { RowCollapsed = new List<string>(); } //每次点击更改集合,移出和新增 if (RowCollapsed.Contains(treenode)) { RowCollapsed.Remove(treenode); } else { RowCollapsed.Add(treenode); } movelist = new List<string>(moveindex.Split(',')); } int PIDindex = FindColumn(TreeDataParentIDField).ColumnIndex; int IDindex = FindColumn(TreeDataIDField).ColumnIndex; //得到下级菜单 string nextindex = GetNextIndex(Rows[Convert.ToInt32(treenode)], Rows); if (nextindex != "") { string[] s = nextindex.Split(','); for (int i = 0; i < s.Length; i++) { GridRow dr = Rows[Convert.ToInt32(s[i])]; string rowindex = dr.RowIndex.ToString(); //收起动作循环子集收起 if (!RowCollapsed.Contains(treenode)) { movelist.Add(rowindex); DoNextTreeClick(rowindex, ref movelist, false); } //展开循环子集展开 else { movelist.Remove(rowindex); if (RowCollapsed.Contains(rowindex)) { DoNextTreeClick(rowindex, ref movelist, true); } } } moveindex = ""; if (movelist.Count >= 2) { movelist.Sort(delegate(string str1, string str2) { return int.Parse(str2 == "" ? "0" : str2) - int.Parse(str1 == "" ? "0" : str1); }); } foreach (string ss in movelist) { moveindex += moveindex == "" ? ss : "," + ss; } if (moveindex != null) { string js = "" //+"F.ready(function(){" + "var store =F('" + ClientID + "').getStore();" + "var rows = [" + moveindex + "];" + "Ext.Array.each(rows, function (rowIndex, index) {" + " store.removeAt(rowIndex);" + "});" //+ XID + ".f_loadData();" + "F('" + ClientID + "').view.refresh();"; //+ "});"; //sb.Append(js); sb.Append("F('" + ClientID + "').f_loadData();"); sb.Append(js); } PageContext.RegisterStartupScript(sb.ToString()); } TreeClick(); } } /// <summary> /// 循环子集 /// </summary> /// <param name="treeindex"></param> /// <param name="movelist"></param> private void DoNextTreeClick(string treeindex, ref List<string> movelist, bool Collapsed) { if (EnableTree) { StringBuilder sb = new StringBuilder(); int PIDindex = FindColumn(TreeDataParentIDField).ColumnIndex; int IDindex = FindColumn(TreeDataIDField).ColumnIndex; //得到下一菜单 string nextindex = GetNextIndex(Rows[Convert.ToInt32(treeindex)], Rows); if (nextindex != "") { string[] s = nextindex.Split(','); for (int i = 0; i < s.Length; i++) { GridRow dr = Rows[Convert.ToInt32(s[i])]; string rowindex = dr.RowIndex.ToString(); //展开 if (movelist.Contains(rowindex) && Collapsed) { movelist.Remove(rowindex); } //收起 if (!Collapsed && !movelist.Contains(rowindex)) { movelist.Add(rowindex); } //展开子集在展开集合中则执行该子集的展开 if (Collapsed && RowCollapsed.Contains(rowindex)) { DoNextTreeClick(rowindex, ref movelist, true); } } } } }
就是点击展开子集,这里判断子集已经展开就继续展开子集,收起要收起该子集的所有子集,主要是参数逻辑的计算。至此功能全部实现
下面应该写图标的展开和收起的改变,全部的前台实现,及其他编辑选择锁定列的测试和修改