前回の続きです。
今回はドラッグ&ドロップに対応できるようにします。そのためにはTreeViewを継承します。しなくてもできるといえばできるけど・・・・
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
public partial class Form1 : Form { public Form1() { InitializeComponent(); syncTreeView1.DragDrop += syncTreeView1_DragDrop; syncTreeView2.DragDrop += syncTreeView1_DragDrop; } private void SyncTreeView1_DragDrop(object sender, DragEventArgs e) { // ここにやりたいことを書く } private void SyncTreeView2_DragDrop(object sender, DragEventArgs e) { // 上と同じことを書く? } } |
しかしそれぞれのTreeViewでこれをやるのは面倒くさい。そこで継承してSyncTreeViewExというクラスをつくります。
1 2 3 4 5 6 7 |
public class SyncTreeViewEx : SyncTreeView { public SyncTreeViewEx() { this.AllowDrop = true; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public partial class Form1 : Form { public Form1() { InitializeComponent(); var group1 = new SyncGroup(null); group1.SetTreeView(syncTreeViewEx1); group1.SetTreeView(syncTreeViewEx2); syncTreeViewEx1.LabelEdit = true; syncTreeViewEx1.HideSelection = false; syncTreeViewEx1.AllowDrop = true; } } |
さてドラッグ&ドロップに対応させるにはどうすればいいのでしょうか?
ここではドラッグ時には以下の処理がおこなわれます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
public class SyncTreeView { private void TreeViewEx1_ItemDrag(object sender, ItemDragEventArgs e) { TreeNodeEx nodeEx = GetTreeNodeEx((TreeNode)e.Item); BeginItemDragArgs args1 = new BeginItemDragArgs(nodeEx); OnBeginItemDrag(args1); if (args1.IsCancel) return; List<TreeNode> oldNodes = new List<TreeNode>(); foreach (var view in treeViewEx1.group.TreeViews) oldNodes.Add(view.SelectedNode); DoDragDrop(nodeEx, DragDropEffects.All); foreach (var node in oldNodes) { if (node != null) node.TreeView.SelectedNode = node; } OnEndItemDrag(new EventArgs()); } } |
ItemDragイベントが発生するとDoDragDropメソッドが実行されます。これによってドラッグ&ドロップの処理がおこなわれます。OnBeginItemDragはドラッグが開始されるときにおこないたい処理、OnEndItemDragはドロップされたあとの処理を書きます。
それからSyncTreeViewのなかにTreeNodeがドラッグされたとき専用のイベントとしてDragOverExを作成しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
public class SyncTreeView { public delegate void DragOverExHandler(SyncTreeView sender, DragEventExArgs e); public event DragOverExHandler DragOverEx; protected virtual void OnDragOverEx(DragEventExArgs e) { DragOverEx?.Invoke(this, e); } private void TreeViewEx1_DragDrop(object sender, System.Windows.Forms.DragEventArgs e) { if (e.Data.GetDataPresent(typeof(TreeNodeEx))) { DragEventExArgs e1 = new DragEventExArgs(e, this); OnDragDropEx(e1); } else { DragEventArgs e1 = new DragEventArgs(e, this); OnDragDrop(e1); } } } |
DragEventExArgsを調べるとドラッグ&ドロップに関するたいていのことはわかるようになっています。
移動する場合、移動先が移動元の子孫であってはいけません。そのためCanMoveメソッドでチェックをしています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
public class DragEventExArgs { System.Windows.Forms.DragEventArgs _args = null; SyncTreeView _view = null; public DragEventExArgs(System.Windows.Forms.DragEventArgs args, SyncTreeView view) { _args = args; _view = view; } public DragDropEffects Effect { get { return _args.Effect; } set { _args.Effect = value; } } public Point ScreenPoint { get { return new Point(_args.X, _args.Y); } } public TreeNodeEx DragNodeEx { get { if (!_args.Data.GetDataPresent(typeof(TreeNodeEx))) return null; return (TreeNodeEx)_args.Data.GetData(typeof(TreeNodeEx)); } } public TreeNodeEx DropNodeEx { get { Point pt = _view.treeViewEx1.PointToClient(ScreenPoint); var ht = _view.treeViewEx1.HitTest(pt); return _view.GetTreeNodeEx(ht.Node); } } public TreeViewHitTestLocations HitTestLocation { get { Point pt = _view.treeViewEx1.PointToClient(ScreenPoint); var ht = _view.treeViewEx1.HitTest(pt); return ht.Location; } } public bool CanMove { get { return _view.CanMove(DragNodeEx, DropNodeEx); } } } |
そこでSyncTreeViewを継承したSyncTreeViewExクラスのOnDragOverExには以下のように書いてみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public class SyncTreeViewEx : SyncTreeView { public SyncTreeViewEx() { this.AllowDrop = true; } protected override void OnDragOverEx(DragEventExArgs e) { if (Control.ModifierKeys.HasFlag(Keys.Control)) e.Effect = DragDropEffects.Copy; else e.Effect = DragDropEffects.Move; } } |
通常のドラッグなら移動、Ctrlキーが押されているときはTreeNodeをコピーします。
それからドロップ時の処理ですが、SyncTreeViewクラスではこのようになっています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
public class SyncTreeView { private void TreeViewEx1_DragDrop(object sender, System.Windows.Forms.DragEventArgs e) { if (e.Data.GetDataPresent(typeof(TreeNodeEx))) { DragEventExArgs e1 = new DragEventExArgs(e, this); OnDragDropEx(e1); } else { DragEventArgs e1 = new DragEventArgs(e, this); OnDragDrop(e1); } } public delegate void DragDropExHandler(SyncTreeView sender, DragEventExArgs e); public event DragDropExHandler DragDropEx; protected virtual void OnDragDropEx(DragEventExArgs e) { DragDropEx?.Invoke(this, e); } } |
TreeNodeがドロップされたときにはOnDragDropExが呼ばれます。そこでこれをオーバーライドすればよいということになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public class SyncTreeViewEx : SyncTreeView { protected override void OnDragDropEx(DragEventExArgs e) { if (e.CanMove && e.Effect == DragDropEffects.Move) { MoveToLastChild(e.DragNodeEx, e.DropNodeEx); } if (e.Effect == DragDropEffects.Copy) { CopyToLastChild(e.DragNodeEx, e.DropNodeEx, null); } } } |
ノードの移動またはコピーをおこないます。このプログラムではドロップされたノードの一番下の子の位置に移動します。ほかにもMoveToFirstChild、ドロップされたノードの次の位置であるMoveToNext、前の位置であるMoveToPrevが選択できます。また移動の場合、移動不可能の位置の場合はなにもおきません。
SyncTreeView.MoveToLastChildとSyncTreeView.CopyToLastChildは以下のようになっています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
public class SyncTreeView { public void MoveToLastChild(TreeNodeEx sourceNode, TreeNodeEx targetNode) { if (!CanMoveToLastChild(sourceNode, targetNode)) return; foreach (TreeViewEx view in treeViewEx1.group.TreeViews) { TreeNode[] sourceNodes = view.Nodes.Find(sourceNode.Key, true); TreeNode[] targetNodes = view.Nodes.Find(targetNode.Key, true); if (sourceNodes.Length == 1 && targetNodes.Length == 1) { view.MoveToLastChild(sourceNodes[0], targetNodes[0]); } } } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
public class SyncTreeView { public void CopyToLastChild(TreeNodeEx sourceNode, TreeNodeEx targetNode, Type[] types) { List<TreeNode> newNodes = GetTreeNodesForCopy(sourceNode, types); if (newNodes == null) return; foreach (TreeViewEx view in treeViewEx1.group.TreeViews) { TreeNode[] targetNodes = view.Nodes.Find(targetNode.Key, true); if (targetNodes.Length == 1) { foreach (TreeNode node in newNodes) targetNodes[0].Nodes.Add((TreeNode)node.Clone()); } } } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
public class SyncTreeView { List<TreeNode> GetTreeNodesForCopy(TreeNodeEx sourceNode, Type[] types) { List<TreeNodeEx> treeNodeExs = GetTreeNodeExs(sourceNode); XmlSerializer xml = null; if (types == null || types.Length == 0) xml = new XmlSerializer(typeof(List<TreeNodeEx>)); else xml = new XmlSerializer(typeof(List<TreeNodeEx>), types); MemoryStream ms = new MemoryStream(); xml.Serialize(ms, treeNodeExs); ms.Position = 0; List<TreeNodeEx> newTreeNodeExs = (List<TreeNodeEx>)xml.Deserialize(ms); return treeViewEx1.RestoreNodes(newTreeNodeExs); } } |