公司项目中用到了dev treeList 控件,用来显示分类列表,在同事录入数据时候抱怨不能对分类进行排序,于是今天花了近一天的时间终于研究出来了,如何利用MySql 存储过程来实现分类的上下移动操作。
数据库结构:
sort列便是用来对最低一级的类进行排序的依据。
首先,在数据库中定义,上移,下移操作的两个存储过程:
CREATE DEFINER=`root`@`%` PROCEDURE `cate_up`( IN cate_id INT, sorts int, pids int, presort int ) begin select sort,pid from rs_goods_class_headquarters where rs_goods_class_headquarters.id=cate_id into sorts,pids; select max(sort) from rs_goods_class_headquarters where rs_goods_class_headquarters.pid=pids and rs_goods_class_headquarters.sort<sorts and rs_goods_class_headquarters.status=0 into presort; if sorts >1 then update rs_goods_class_headquarters set rs_goods_class_headquarters.sort=sorts where rs_goods_class_headquarters.pid =pids and rs_goods_class_headquarters.sort=presort and rs_goods_class_headquarters.status=0; update rs_goods_class_headquarters set rs_goods_class_headquarters.sort=presort where rs_goods_class_headquarters.id=cate_id; end if; end
CREATE DEFINER=`root`@`%` PROCEDURE `cate_down`( IN cate_id INT, sorts int, pids int, presort int ) begin select sort,pid from rs_goods_class_headquarters where rs_goods_class_headquarters.id=cate_id into sorts,pids; select min(sort) from rs_goods_class_headquarters where rs_goods_class_headquarters.pid=pids and rs_goods_class_headquarters.sort>sorts and rs_goods_class_headquarters.status=0 into presort; if (sorts >0 && presort is not null) then update rs_goods_class_headquarters set rs_goods_class_headquarters.sort=sorts where rs_goods_class_headquarters.pid =pids and rs_goods_class_headquarters.sort=presort and rs_goods_class_headquarters.status=0; update rs_goods_class_headquarters set rs_goods_class_headquarters.sort=presort where rs_goods_class_headquarters.id=cate_id; end if; end
cate_up 用来操作上移,cate_down用来操作下移
上移的原理就是找到相同父类id最小的那个类,然后调换他们的sort值,例子中的status=0是启用的类,status=1代表已经删除,不在树中显示。
下移的原理类似。
需要说明的是,TreeList控件在绑定数据源的时候总是会自己把焦点节点选中在返回的数据源的第一行的值,这给我造成了很大的麻烦,想了各种办法阻止它自动把焦点放在第一行,好在的时终于在最后找到了方法:
在FocuseNodeChanged事件中,
//设置选中的节点的id作为pid if (tlMain.FocusedNode.GetValue("id") != null) { GoodsManagerData.ClassNo = tlMain.FocusedNode.GetValue("class_No").ToString(); GoodsManagerData.pid = tlMain.FocusedNode.GetValue("id").ToString(); GoodsManagerData.name = tlMain.FocusedNode.GetValue("class_name").ToString(); //存储已经选中的行,如果选中的是第一个节点(这里因为已经对数据源排序,应该以数据源中的第一行为准) if (tlMain.FocusedNode.GetValue("id").ToString()!=dtCategory.Rows[0]["id"].ToString()) { focusedIndex = tlMain.FocusedNode.GetValue("class_name").ToString(); } LoadGoodsInfos(); }
判断一下,如果是数据源中的第一行的值,则不给全局变量focuseIndex赋值,该值是我用来自己指定那个值应该被选中的。
private void LoadCategoryData() { lblMessage.Visible = false; //解绑数据 tlMain.Nodes.Clear(); tlMain.DataSource = null; tlMain.DataSource = dtCategory;//数据绑定的时候,会默认将第一个设置为焦点,这回造成很多不必要的麻烦。 tlMain.Columns["class_No"].Visible = false; tlMain.Columns["class_name"].Caption = "类别"; tlMain.Columns["status"].Visible = false; tlMain.Columns["map"].Visible = false; tlMain.Columns["sort"].Visible = false; tlMain.KeyFieldName = "id"; tlMain.ParentFieldName = "pid"; SetFocuseNode(focusedIndex); }
如上面的代码,在tlMain.DataSource=dtCategory这句代码中,TreeList控件会自动聚焦,应用上面的代码后不让它在focuseNodeChanged事件中给foucsedIndex赋值,然后再帮顶完数据集后,自己手动指定那个Node被选中:
private void SetFocuseNode(string category) { //if (tlMain.FocusedNode != null) { SetFocusedNode(tlMain.Nodes, category); } } /// <summary> /// 设置焦点Node /// </summary> /// <param name="nodes"></param> private void SetFocusedNode(TreeListNodes nodes,string str) { //这里的Nodes属性只是同级的Node foreach (TreeListNode node in nodes) { if (node.GetValue("class_name").ToString() == str) { tlMain.FocusedNode = node; if (node.HasChildren) { node.Expanded = true; } } else if (node.HasChildren) { node.Expanded = false; SetFocusedNode(node.Nodes,str); } } }
这里有一点需要说明的是:本来我想只在有子节点的node展开树形,方便定于上移的Node而已,但没想到TreeList控件总是自己展开了一个节点,一直也没找到为什么,比较郁闷,所以,想了个办法,在所有不是我要找的节点都把他们的Expanded 属性设置为false,这样就达到了自己目的。
虽然问题解决了,但还是比较纳闷为什么TreeList会自己展开某一个节点,还有,没找到哪个属性能取消TreeList自动聚焦到第一个节点的方法,如果有高手知道如何解决我的这两个疑惑,还望不吝赐教。