
前回の続きです。
複数のTreeViewの内容を同期する
TreeViewをファイルに保存する方法
今回はドラッグ&ドロップに対応できるようにします。そのためには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というクラスをつくります。
|
public class SyncTreeViewEx : SyncTreeView { public SyncTreeViewEx() { this.AllowDrop = true; } } |
|
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; } } |
さてドラッグ&ドロップに対応させるにはどうすればいいのでしょうか?
俺的ライブラリをつくる 同期化されたTreeView編
ここではドラッグ時には以下の処理がおこなわれます。
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には以下のように書いてみます。
|
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が呼ばれます。そこでこれをオーバーライドすればよいということになります。
|
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); } } |
鳩でも分かるC#管理人からのお願い
できる仕事であれば請け負います。鳩でもわかるC#管理人はクラウドワークスに在宅ワーカーとして登録しています。お仕事の依頼もお待ちしております。
⇒ 仕事を依頼する
コメントについて
コメントで英語などの外国語でコメントをされる方がいますが、管理人は日本語以外はわからないので基本的に内容が理解できず、承認することもありません。それからへんな薬を売っているサイトやリンク先のサイトが存在しないというスパムコメントも多々あります。
Some people make comments in foreign languages such as English, but since the manager does not understand anything other than Japanese, he basically cannot understand the content and does not approve it. Please use Japanese when making comments.
そんななか日本語のコメントもいただけるようになりました。「○○という変数はどこで宣言されているのか?」「××というメソッドはどこにあるのか」「例外が発生する」「いっそのことソース丸ごとくれ」という質問ですが、管理人としては嬉しく思います。「自分が書いた記事は読まれているんだな」と。疑問点には可能な限り答えます。記事に問題があれば修正いたします。
そのうえでお願いがあります。「匿名」という味も素っ気もない名前ではなく、捨てハンでいいのでなにかハンドルネームをつくってほしいと思います。
管理人のモチベーションアップのために
よろしければご支援お願いします。
⇒ 管理人の物乞いリスト