引言
近日,在工作中遇到 treeView控件 要只是 展开或收起节点实在是太单调了,如果能像window那样的拖动效果就好了,于是乎就开始折腾如何让他能实现拖拽的效果......
正文
都说万事开头难, (就像我现在思考怎么写...#^.^#), 一开始我就在表的设计上难住了,在数据库中怎样去保存 动态 拖拽的 树节点呢???
思考中...........................
继续思考.......................
在不看我下面的实现方法之前,有兴趣可以思考一下哦。
您有想法了吗?
我的实现方式是:设计了两个字段就OK了,一个NodeId,parentId,为了能够看得出效果,我附加了一个 Title 树节点的标题,
NodeId不用讲( PRIMARY KEY AUTOINCREMENT) parentId用来保存 拖动节点 所在的 父级节点NodeId 即可,似乎有些绕,举例:比如不管我怎么拖动节点a,节点a肯定会位于某个节点(就叫节点b 吧)之下,只要用节点a 的parentId保存 节点b 的NodeId即可。
数据库
当然要是您有更好的实现方式,可以请留言,共同学习。
在实现拖动效果前,需要知道treeView控件的一个属性和三个事件:
属性:
1.AllowDrop 是否允许控件拖放操作,是为 true;否则为 false。默认为 false。(一定要设置为true啊~)
事件:
- ItemDrag事件(当用户开始拖动节点时发生。)
- DragEnter事件(在将对象拖入控件的边界时发生。)
- DragDrop事件(在完成拖放操作时发生。)
好了,了解这些之后我们就可以开始码代码了~
1 public void IniTable() 2 { 3 try 4 { 5 MessageBox.Show(ComPath); 6 //如果不存在数据库文件,则创建该数据库文件 7 if (!System.IO.File.Exists(ComPath)) 8 { 9 SQLiteDBHelper.CreateDB(ComPath); 10 SQLiteDBHelper db = new SQLiteDBHelper(ComPath); 11 string sql = "CREATE TABLE TTree(NodeId integer NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,Title varchar(50),parentId varchar(20) ) "; 12 //sql += " CREATE TABLE TContent (NodeId integer NOT NULL,Title varchar(50) )"; 13 db.ExecuteNonQuery(sql, null); 14 } 15 16 } 17 catch (Exception) 18 { 19 20 throw; 21 } 22 23 }
注:数据库使用的sqllite, 有一个问题一直没有弄明白,是不是sqllite不支持 sql = "creat table a(...) creat table b(...)";
string sql = "CREATE TABLE TTree(NodeId integer NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,Title varchar(50),parentId varchar(20) ) ";
sql += " CREATE TABLE TContent (NodeId integer NOT NULL,Title varchar(50) )";
db.ExecuteNonQuery(sql, null);
这样的语句,会报一个 create附近有错误 ,中间加了一个 go 还是 会报 go附近错误??!!不得解~
1 #region 加载树 2 public void IniTreeView() 3 { 4 treeView1.Nodes.Clear(); 5 //添加根节点 6 7 string strSQL; 8 strSQL = "select * from TTree where parentId = 0"; 9 SQLiteDBHelper db = new SQLiteDBHelper(ComPath); 10 DataTable dt = db.ExecuteDataTable(strSQL,null); 11 12 if (dt.Rows.Count > 0) 13 { 14 DataView tmpDV = new DataView(dt); 15 if (tmpDV.Count > 0) 16 { 17 foreach (DataRowView myView in tmpDV) 18 { 19 string strTreeNode = myView["Title"].ToString().Trim(); 20 TreeNode tmp = new TreeNode(strTreeNode, 0, 1); 21 tmp.Tag = myView["NodeId"]; 22 treeView1.Nodes.Add(tmp); 23 treeView1.SelectedNode = tmp; 24 ChildTreeView(tmp, myView); //递归填充树子节点 25 } 26 } 27 } 28 } 29 30 private void ChildTreeView(TreeNode ParentNode, DataRowView ParentRow) 31 { 32 33 string strNode; 34 string sParentNode = ParentRow["NodeId"].ToString().Trim(); 35 SQLiteDBHelper db = new SQLiteDBHelper(ComPath); 36 string strSQL = "select * from TTree where parentId=" + Convert.ToInt32(ParentRow["NodeId"]); 37 DataTable dtTree = db.ExecuteDataTable(strSQL,null); 38 if (dtTree.Rows.Count > 0) 39 { 40 DataView tmpDV = new DataView(dtTree); 41 foreach (DataRowView myRow in tmpDV) 42 { 43 strNode = myRow["Title"].ToString().Trim(); 44 TreeNode myNode = new TreeNode(strNode, 0, 1); 45 myNode.StateImageIndex = 1; 46 myNode.Tag = myRow["NodeId"]; 47 ParentNode.Nodes.Add(myNode); 48 ChildTreeView(myNode, myRow); 49 50 } 51 } 52 }
下面就是实现树节点拖拽的效果了,也就用到文章开头我提到的三个事件了;
在拖拽时要注意的是:1.目标节点不能为空。2.目标节点不能被拖拽节点本身。3.目标节点不能是被拖拽节点的子节点。
1 Point Position = new Point(0, 0); 2 //当用户开始拖动节点时发生 3 private void treeView1_ItemDrag(object sender, ItemDragEventArgs e) 4 { 5 DoDragDrop(e.Item, DragDropEffects.Move); 6 } 7 8 //在将对象拖入控件的边界时发生 9 private void treeView1_DragEnter(object sender, DragEventArgs e) 10 { 11 //判断拖动的是否为树节点 12 if (e.Data.GetDataPresent(typeof(TreeNode))) 13 e.Effect = DragDropEffects.Move; 14 else 15 e.Effect = DragDropEffects.None; 16 } 17 18 //在完成拖放操作时发生 19 private void treeView1_DragDrop(object sender, DragEventArgs e) 20 { 21 string Moveid = "", Dropid = ""; 22 23 24 TreeNode myNode = null; 25 if (e.Data.GetDataPresent(typeof(TreeNode))) 26 { 27 //获得移动节点 28 myNode = (TreeNode)(e.Data.GetData(typeof(TreeNode))); 29 //获得移动节点的NodeId 30 Moveid = (string)myNode.Tag.ToString(); 31 32 } 33 else 34 { 35 MessageBox.Show("error"); 36 } 37 38 //将树节点的位置计算成工作区坐标。 39 Position.X = e.X; 40 Position.Y = e.Y; 41 Position = treeView1.PointToClient(Position); 42 43 //检索目标节点 44 TreeNode DropNode = this.treeView1.GetNodeAt(Position); 45 // 1.目标节点不是空。2.目标节点不是被拖拽接点的子节点。3.目标节点不是被拖拽节点本身 46 if (DropNode != null && DropNode.Parent != myNode && DropNode != myNode) 47 { 48 //临时节点 49 TreeNode tempNode = myNode; 50 // 将被拖拽节点从原来位置删除。 51 myNode.Remove(); 52 // 在目标节点下增加被拖拽节点 53 DropNode.Nodes.Add(tempNode); 54 //目标节点的NodeId值 55 Dropid = (string)DropNode.Tag.ToString(); 56 57 //数据库中更新移动节点的parentID 58 string strSQL; 59 strSQL = " update TTree set parentId =" + Dropid + " where NodeId = " + Moveid + " "; 60 SQLiteDBHelper db = new SQLiteDBHelper(ComPath); 61 int i = db.ExecuteNonQuery(strSQL,null); 62 if (i < 0) 63 { 64 MessageBox.Show("请正确拖动文件"); 65 } 66 67 68 } 69 // 如果目标节点不存在,即拖拽的位置不存在节点,那么就将被拖拽节点放在根节点之下 70 if (DropNode == null) 71 { 72 //parentId = 0 表示根节点 73 string strSQL; 74 strSQL = " update TTree set parentId = 0 where NodeId = " + Moveid + " "; 75 SQLiteDBHelper db = new SQLiteDBHelper(ComPath); 76 int i = db.ExecuteNonQuery(strSQL, null); 77 78 TreeNode DragNode = myNode; 79 myNode.Remove(); 80 treeView1.Nodes.Add(DragNode); 81 } 82 }
结语:
到目前为止基本的拖拽效果已经实现了,但还是 没有 达到windows资源管理器拖拽时能显示节点缩略图的效果,
对此暂时没有思路了.....似乎网上有说用Win32 API实现,还没有细研究,也不知道是什么东西~
待闲下来时再去研究研究~当然这就是后话了~
1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Linq; 7 using System.Text; 8 using System.Windows.Forms; 9 using FileManager; 10 11 12 namespace DropTree 13 { 14 public partial class Form1 : Form 15 { 16 17 string ComPath = System.IO.Directory.GetCurrentDirectory() + "\\Test.db"; 18 public Form1() 19 { 20 InitializeComponent(); 21 this.Load+=new EventHandler(Form1_Load); 22 //如果控件允许拖放操作,则为 true;否则为 false。默认为 false。 23 treeView1.AllowDrop = true; 24 treeView1.ItemDrag += new ItemDragEventHandler(treeView1_ItemDrag); 25 treeView1.DragEnter += new DragEventHandler(treeView1_DragEnter); 26 treeView1.DragDrop += new DragEventHandler(treeView1_DragDrop); 27 } 28 29 private void Form1_Load(object sender, EventArgs e) 30 { 31 IniTable(); 32 IniTreeView(); 33 //展开所有节点 34 this.treeView1.ExpandAll(); 35 treeView1.SelectedNode = null; 36 } 37 // 在拖拽时要注意的是:1.目标节点不能为空。2.目标节点不能被拖拽节点本身。3.目标节点不能是被拖拽节点的子节点。 38 //1.ItemDrag事件(当用户开始拖动节点时发生。)2.DragEnter事件(在将对象拖入控件的边界时发生。)3.DragDrop事件(在完成拖放操作时发生。) 39 40 #region 初始化数据库 41 public void IniTable() 42 { 43 try 44 { 45 MessageBox.Show(ComPath); 46 //如果不存在数据库文件,则创建该数据库文件 47 if (!System.IO.File.Exists(ComPath)) 48 { 49 SQLiteDBHelper.CreateDB(ComPath); 50 SQLiteDBHelper db = new SQLiteDBHelper(ComPath); 51 string sql = "CREATE TABLE TTree(NodeId integer NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,Title varchar(50),parentId varchar(20),CreateTime datetime,UpdateTime Date ) "; 52 //sql += " CREATE TABLE TContent (NodeId integer NOT NULL,Title varchar(50),Type varchar(50),Size varchar(50),Time time,CreateTime datetime,UpdateTime datetime, Content blob)"; 53 db.ExecuteNonQuery(sql, null); 54 } 55 56 } 57 catch (Exception) 58 { 59 60 throw; 61 } 62 63 64 } 65 66 #endregion 67 68 #region 加载树 69 public void IniTreeView() 70 { 71 treeView1.Nodes.Clear(); 72 //添加根节点 73 74 string strSQL; 75 strSQL = "select * from TTree where parentId = 0"; 76 SQLiteDBHelper db = new SQLiteDBHelper(ComPath); 77 DataTable dt = db.ExecuteDataTable(strSQL,null); 78 79 if (dt.Rows.Count > 0) 80 { 81 DataView tmpDV = new DataView(dt); 82 if (tmpDV.Count > 0) 83 { 84 foreach (DataRowView myView in tmpDV) 85 { 86 string strTreeNode = myView["Title"].ToString().Trim(); 87 TreeNode tmp = new TreeNode(strTreeNode, 0, 1); 88 tmp.Tag = myView["NodeId"]; 89 treeView1.Nodes.Add(tmp); 90 treeView1.SelectedNode = tmp; 91 ChildTreeView(tmp, myView); //递归填充树子节点 92 } 93 } 94 } 95 } 96 97 private void ChildTreeView(TreeNode ParentNode, DataRowView ParentRow) 98 { 99 100 string strNode; 101 string sParentNode = ParentRow["NodeId"].ToString().Trim(); 102 SQLiteDBHelper db = new SQLiteDBHelper(ComPath); 103 string strSQL = "select * from TTree where parentId=" + Convert.ToInt32(ParentRow["NodeId"]); 104 DataTable dtTree = db.ExecuteDataTable(strSQL,null); 105 if (dtTree.Rows.Count > 0) 106 { 107 DataView tmpDV = new DataView(dtTree); 108 foreach (DataRowView myRow in tmpDV) 109 { 110 strNode = myRow["Title"].ToString().Trim(); 111 TreeNode myNode = new TreeNode(strNode, 0, 1); 112 myNode.StateImageIndex = 1; 113 myNode.Tag = myRow["NodeId"]; 114 ParentNode.Nodes.Add(myNode); 115 ChildTreeView(myNode, myRow); 116 117 } 118 } 119 } 120 #endregion 121 122 #region 树节点拖动效果 123 Point Position = new Point(0, 0); 124 //当用户开始拖动节点时发生 125 private void treeView1_ItemDrag(object sender, ItemDragEventArgs e) 126 { 127 DoDragDrop(e.Item, DragDropEffects.Move); 128 } 129 130 //在将对象拖入控件的边界时发生 131 private void treeView1_DragEnter(object sender, DragEventArgs e) 132 { 133 //判断拖动的是否为树节点 134 if (e.Data.GetDataPresent(typeof(TreeNode))) 135 e.Effect = DragDropEffects.Move; 136 else 137 e.Effect = DragDropEffects.None; 138 } 139 140 //在完成拖放操作时发生 141 private void treeView1_DragDrop(object sender, DragEventArgs e) 142 { 143 string Moveid = "", Dropid = ""; 144 145 146 TreeNode myNode = null; 147 if (e.Data.GetDataPresent(typeof(TreeNode))) 148 { 149 //获得移动节点 150 myNode = (TreeNode)(e.Data.GetData(typeof(TreeNode))); 151 //获得移动节点的NodeId 152 Moveid = (string)myNode.Tag.ToString(); 153 154 } 155 else 156 { 157 MessageBox.Show("error"); 158 } 159 160 //将树节点的位置计算成工作区坐标。 161 Position.X = e.X; 162 Position.Y = e.Y; 163 Position = treeView1.PointToClient(Position); 164 165 //检索目标节点 166 TreeNode DropNode = this.treeView1.GetNodeAt(Position); 167 // 1.目标节点不是空。2.目标节点不是被拖拽接点的子节点。3.目标节点不是被拖拽节点本身 168 if (DropNode != null && DropNode.Parent != myNode && DropNode != myNode) 169 { 170 //临时节点 171 TreeNode tempNode = myNode; 172 // 将被拖拽节点从原来位置删除。 173 myNode.Remove(); 174 // 在目标节点下增加被拖拽节点 175 DropNode.Nodes.Add(tempNode); 176 //目标节点的NodeId值 177 Dropid = (string)DropNode.Tag.ToString(); 178 179 //数据库中更新移动节点的parentID 180 string strSQL; 181 strSQL = " update TTree set parentId =" + Dropid + " where NodeId = " + Moveid + " "; 182 SQLiteDBHelper db = new SQLiteDBHelper(ComPath); 183 int i = db.ExecuteNonQuery(strSQL,null); 184 if (i < 0) 185 { 186 MessageBox.Show("请正确拖动文件"); 187 } 188 189 190 } 191 // 如果目标节点不存在,即拖拽的位置不存在节点,那么就将被拖拽节点放在根节点之下 192 if (DropNode == null) 193 { 194 //parentId = 0 表示根节点 195 string strSQL; 196 strSQL = " update TTree set parentId = 0 where NodeId = " + Moveid + " "; 197 SQLiteDBHelper db = new SQLiteDBHelper(ComPath); 198 int i = db.ExecuteNonQuery(strSQL, null); 199 200 TreeNode DragNode = myNode; 201 myNode.Remove(); 202 treeView1.Nodes.Add(DragNode); 203 } 204 } 205 #endregion 206 207 } 208 }
如有问题或建议,欢迎留言 ~ 博客地址:http://www.cnblogs.com/zqiang/ |