From f27a7426b229f2343dc6af3d4b90fc1be529dad7 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Thu, 9 Jan 2014 21:32:03 +0100 Subject: [PATCH] ICSharpCode.TreeView: improve API for drag/drop/copy/paste of nodes. --- .../XamlBinding/XamlOutlineNode.cs | 35 +++--- .../Debugger/Debugger.AddIn/Pads/WatchPad.cs | 17 +-- .../TreeModel/SharpTreeNodeAdapter.cs | 10 +- .../Highlighting/RichText.cs | 21 ++-- .../Highlighting/RichTextModel.cs | 19 ++++ .../Utils/RichTextWriter.cs | 3 + .../ICSharpCode.TreeView.Demo/FileNode.cs | 15 ++- .../FileSystemNode.cs | 44 ++++---- .../ICSharpCode.TreeView.Demo/FolderNode.cs | 36 ++++-- .../ICSharpCode.TreeView/SharpTreeNode.cs | 106 ++++++++++++------ .../ICSharpCode.TreeView/SharpTreeNodeView.cs | 7 +- .../ICSharpCode.TreeView/SharpTreeView.cs | 78 +++++++++---- .../ICSharpCode.TreeView/SharpTreeViewItem.cs | 4 +- 13 files changed, 251 insertions(+), 144 deletions(-) diff --git a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlOutlineNode.cs b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlOutlineNode.cs index 2d27ff7169..14df5d9b0e 100644 --- a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlOutlineNode.cs +++ b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlOutlineNode.cs @@ -36,22 +36,12 @@ namespace ICSharpCode.XamlBinding public ITextAnchor EndMarker { get; set; } public ITextEditor Editor { get; set; } - public override bool CanDrag(SharpTreeNode[] nodes) - { - return false; //nodes.All(node => node.Parent != null); - } - - public override bool CanDrop(DragEventArgs e, int index) - { - return false; - } - public string GetMarkupText() { return Editor.Document.GetText(Marker.Offset, EndMarker.Offset - Marker.Offset); } - public override IDataObject Copy(SharpTreeNode[] nodes) + protected override IDataObject GetDataObject(SharpTreeNode[] nodes) { string[] data = nodes .OfType() @@ -63,11 +53,6 @@ namespace ICSharpCode.XamlBinding return dataObject; } - public override bool CanDelete() - { - return Parent != null; - } - // public override void Drop(IDataObject data, int index, DropEffect finalEffect) // { // try { @@ -96,12 +81,24 @@ namespace ICSharpCode.XamlBinding // } // } - public override void Delete() + public override bool CanDelete(SharpTreeNode[] nodes) + { + return nodes.OfType().All(n => n.Parent != null); + } + + public override void Delete(SharpTreeNode[] nodes) { - DeleteCore(); + DeleteWithoutConfirmation(nodes); + } + + public override void DeleteWithoutConfirmation(SharpTreeNode[] nodes) + { + foreach (XamlOutlineNode xamlNode in nodes.OfType()) { + xamlNode.DeleteCore(); + } } - public override void DeleteCore() + void DeleteCore() { Editor.Document.Remove(Marker.Offset, EndMarker.Offset - Marker.Offset); } diff --git a/src/AddIns/Debugger/Debugger.AddIn/Pads/WatchPad.cs b/src/AddIns/Debugger/Debugger.AddIn/Pads/WatchPad.cs index a37b779a72..a7d1485071 100644 --- a/src/AddIns/Debugger/Debugger.AddIn/Pads/WatchPad.cs +++ b/src/AddIns/Debugger/Debugger.AddIn/Pads/WatchPad.cs @@ -132,29 +132,20 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads class WatchRootNode : SharpTreeNode { - public override bool CanDrop(DragEventArgs e, int index) + public override bool CanPaste(IDataObject data) { - e.Effects = DragDropEffects.None; - if (e.Data.GetDataPresent(DataFormats.StringFormat)) { - e.Effects = DragDropEffects.Copy; - return true; - } - return false; + return data.GetDataPresent(DataFormats.StringFormat); } - public override void Drop(DragEventArgs e, int index) + public override void Paste(IDataObject data) { - if (e.Data == null) return; - if (!e.Data.GetDataPresent(DataFormats.StringFormat)) return; - - var watchValue = e.Data.GetData(DataFormats.StringFormat).ToString(); + var watchValue = data.GetData(DataFormats.StringFormat) as string; if (string.IsNullOrEmpty(watchValue)) return; var pad = SD.Workbench.GetPad(typeof(WatchPad)).PadContent as WatchPad; if (pad == null) return; pad.AddWatch(watchValue); - WindowsDebugger.RefreshPads(); } } diff --git a/src/AddIns/Debugger/Debugger.AddIn/TreeModel/SharpTreeNodeAdapter.cs b/src/AddIns/Debugger/Debugger.AddIn/TreeModel/SharpTreeNodeAdapter.cs index b1e7431cf5..6c2bcbd0a6 100644 --- a/src/AddIns/Debugger/Debugger.AddIn/TreeModel/SharpTreeNodeAdapter.cs +++ b/src/AddIns/Debugger/Debugger.AddIn/TreeModel/SharpTreeNodeAdapter.cs @@ -35,14 +35,16 @@ namespace Debugger.AddIn.Pads.Controls get { return this.Node.GetChildren != null; } } - public override bool CanDelete() + public override bool CanDelete(SharpTreeNode[] nodes) { - return this.Node.CanDelete; + return nodes.All(n => n is SharpTreeNodeAdapter) + && nodes.Cast().All(n => n.Node.CanDelete); } - public override void Delete() + public override void Delete(SharpTreeNode[] nodes) { - Parent.Children.Remove(this); + foreach (var node in nodes) + node.Parent.Children.Remove(this); } protected override void LoadChildren() diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/RichText.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/RichText.cs index 597bfe5015..6573d71a08 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/RichText.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/RichText.cs @@ -158,18 +158,23 @@ namespace ICSharpCode.AvalonEdit.Highlighting int endOffset = i + 1 < stateChangeOffsets.Length ? stateChangeOffsets[i + 1] : text.Length; Run r = new Run(text.Substring(startOffset, endOffset - startOffset)); HighlightingColor state = stateChanges[i]; - if (state.Foreground != null) - r.Foreground = state.Foreground.GetBrush(null); - if (state.Background != null) - r.Background = state.Background.GetBrush(null); - if (state.FontWeight != null) - r.FontWeight = state.FontWeight.Value; - if (state.FontStyle != null) - r.FontStyle = state.FontStyle.Value; + ApplyColorToTextElement(r, state); runs[i] = r; } return runs; } + + internal static void ApplyColorToTextElement(TextElement r, HighlightingColor state) + { + if (state.Foreground != null) + r.Foreground = state.Foreground.GetBrush(null); + if (state.Background != null) + r.Background = state.Background.GetBrush(null); + if (state.FontWeight != null) + r.FontWeight = state.FontWeight.Value; + if (state.FontStyle != null) + r.FontStyle = state.FontStyle.Value; + } /// /// Produces HTML code for the line, with <span style="..."> tags. diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/RichTextModel.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/RichTextModel.cs index 7a5f604a27..37c9e52402 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/RichTextModel.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/RichTextModel.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Windows; +using System.Windows.Documents; using System.Windows.Media; using ICSharpCode.NRefactory.Editor; using ICSharpCode.NRefactory.TypeSystem.Implementation; @@ -264,5 +265,23 @@ namespace ICSharpCode.AvalonEdit.Highlighting index++; } } + + /// + /// Creates WPF Run instances that can be used for TextBlock.Inlines. + /// + /// The text source that holds the text for this RichTextModel. + public Run[] CreateRuns(ITextSource textSource) + { + Run[] runs = new Run[stateChanges.Count]; + for (int i = 0; i < runs.Length; i++) { + int startOffset = stateChangeOffsets[i]; + int endOffset = i + 1 < stateChangeOffsets.Count ? stateChangeOffsets[i + 1] : textSource.TextLength; + Run r = new Run(textSource.GetText(startOffset, endOffset - startOffset)); + HighlightingColor state = stateChanges[i]; + RichText.ApplyColorToTextElement(r, state); + runs[i] = r; + } + return runs; + } } } diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Utils/RichTextWriter.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Utils/RichTextWriter.cs index 49b884d1c0..af274f584b 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Utils/RichTextWriter.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Utils/RichTextWriter.cs @@ -9,6 +9,9 @@ using ICSharpCode.AvalonEdit.Highlighting; namespace ICSharpCode.AvalonEdit.Utils { + // TODO: This class (and derived classes) is currently unused; decide whether to keep it. + // (until this is decided, keep the class internal) + /// /// A text writer that supports creating spans of highlighted text. /// diff --git a/src/Libraries/SharpTreeView/ICSharpCode.TreeView.Demo/FileNode.cs b/src/Libraries/SharpTreeView/ICSharpCode.TreeView.Demo/FileNode.cs index 105b58af78..ad8253b3f1 100644 --- a/src/Libraries/SharpTreeView/ICSharpCode.TreeView.Demo/FileNode.cs +++ b/src/Libraries/SharpTreeView/ICSharpCode.TreeView.Demo/FileNode.cs @@ -84,10 +84,15 @@ namespace ICSharpCode.TreeView.Demo { get { return info.FullName; } } -// -// public override void Paste(IDataObject data) -// { -// Parent.Paste(data); -// } + + public override bool CanPaste(IDataObject data) + { + return Parent.CanPaste(data); + } + + public override void Paste(IDataObject data) + { + Parent.Paste(data); + } } } diff --git a/src/Libraries/SharpTreeView/ICSharpCode.TreeView.Demo/FileSystemNode.cs b/src/Libraries/SharpTreeView/ICSharpCode.TreeView.Demo/FileSystemNode.cs index 72026b3daf..d5a8bf140c 100644 --- a/src/Libraries/SharpTreeView/ICSharpCode.TreeView.Demo/FileSystemNode.cs +++ b/src/Libraries/SharpTreeView/ICSharpCode.TreeView.Demo/FileSystemNode.cs @@ -29,47 +29,41 @@ namespace ICSharpCode.TreeView.Demo { return FullPath; } -// -// public override bool CanCopy(SharpTreeNode[] nodes) -// { -// return true; -// } - public override IDataObject Copy(SharpTreeNode[] nodes) + public override bool CanCopy(SharpTreeNode[] nodes) + { + return nodes.All(n => n is FileSystemNode); + } + + protected override IDataObject GetDataObject(SharpTreeNode[] nodes) { var data = new DataObject(); var paths = nodes.OfType().Select(n => n.FullPath).ToArray(); - data.SetData(typeof(string[]), paths); + data.SetData(DataFormats.FileDrop, paths); return data; } -// -// public override bool CanPaste(IDataObject data) -// { -// return true; -// } -// - public override bool CanDelete() + + public override bool CanDelete(SharpTreeNode[] nodes) { - return true; + return nodes.All(n => n is FileSystemNode); } - public override void Delete() + public override void Delete(SharpTreeNode[] nodes) { - if (MessageBox.Show("Sure?", "Delete", MessageBoxButton.OKCancel) == MessageBoxResult.OK) { - DeleteCore(); + if (MessageBox.Show("Are you sure you want to delete " + nodes.Length + " items?", "Delete", MessageBoxButton.OKCancel) == MessageBoxResult.OK) { + DeleteWithoutConfirmation(nodes); } } - public override void DeleteCore() - { - this.Parent.Children.Remove(this); - } - - public override bool CanDrag(SharpTreeNode[] nodes) + public override void DeleteWithoutConfirmation(SharpTreeNode[] nodes) { - return true; + foreach (var node in nodes) { + if (node.Parent != null) + node.Parent.Children.Remove(node); + } } + // ContextMenu menu; // // public override ContextMenu GetContextMenu() diff --git a/src/Libraries/SharpTreeView/ICSharpCode.TreeView.Demo/FolderNode.cs b/src/Libraries/SharpTreeView/ICSharpCode.TreeView.Demo/FolderNode.cs index 8d6ee7d43f..7c4c070ad1 100644 --- a/src/Libraries/SharpTreeView/ICSharpCode.TreeView.Demo/FolderNode.cs +++ b/src/Libraries/SharpTreeView/ICSharpCode.TreeView.Demo/FolderNode.cs @@ -63,34 +63,46 @@ namespace ICSharpCode.TreeView.Demo { try { foreach (var p in Directory.GetDirectories(path) - .OrderBy(d => Path.GetDirectoryName(d))) { + .OrderBy(d => Path.GetDirectoryName(d))) { Children.Add(new FolderNode(p)); } foreach (var p in Directory.GetFiles(path) - .OrderBy(f => Path.GetFileName(f))) { + .OrderBy(f => Path.GetFileName(f))) { Children.Add(new FileNode(p)); } } catch { } } - - public override bool CanDrop(DragEventArgs e, int index) + + public override bool CanPaste(IDataObject data) { - return e.Data.GetDataPresent(typeof(string[])); + return data.GetDataPresent(DataFormats.FileDrop); } - public override void Drop(DragEventArgs e, int index) + public override void Paste(IDataObject data) { - var paths = e.Data.GetData(typeof(string[])) as string[]; + var paths = data.GetData(DataFormats.FileDrop) as string[]; if (paths != null) { - for (int i = 0; i < paths.Length; i++) { - var p = paths[i]; + foreach (var p in paths) { if (File.Exists(p)) { - Children.Insert(index + i, new FileNode(p)); + Children.Add(new FileNode(p)); + } else { + Children.Add(new FolderNode(p)); } - else { - Children.Insert(index + i, new FolderNode(p)); + } + } + } + + public override void Drop(DragEventArgs e, int index) + { + var paths = e.Data.GetData(DataFormats.FileDrop) as string[]; + if (paths != null) { + foreach (var p in paths) { + if (File.Exists(p)) { + Children.Insert(index++, new FileNode(p)); + } else { + Children.Insert(index++, new FolderNode(p)); } } } diff --git a/src/Libraries/SharpTreeView/ICSharpCode.TreeView/SharpTreeNode.cs b/src/Libraries/SharpTreeView/ICSharpCode.TreeView/SharpTreeNode.cs index fc06fb338c..5c7e185c40 100644 --- a/src/Libraries/SharpTreeView/ICSharpCode.TreeView/SharpTreeNode.cs +++ b/src/Libraries/SharpTreeView/ICSharpCode.TreeView/SharpTreeNode.cs @@ -441,7 +441,10 @@ namespace ICSharpCode.TreeView #region Cut / Copy / Paste / Delete - public bool IsCut { get { return false; } } + /// + /// Gets whether the node should render transparently because it is 'cut' (but not actually removed yet). + /// + public virtual bool IsCut { get { return false; } } /* static List cuttedNodes = new List(); static IDataObject cuttedData; @@ -538,66 +541,99 @@ namespace ICSharpCode.TreeView } */ - public virtual bool CanDelete() + public virtual bool CanDelete(SharpTreeNode[] nodes) { return false; } - public virtual void Delete() + public virtual void Delete(SharpTreeNode[] nodes) { throw new NotSupportedException(GetType().Name + " does not support deletion"); } - public virtual void DeleteCore() + public virtual void DeleteWithoutConfirmation(SharpTreeNode[] nodes) { throw new NotSupportedException(GetType().Name + " does not support deletion"); } - public virtual IDataObject Copy(SharpTreeNode[] nodes) + public virtual bool CanCut(SharpTreeNode[] nodes) { - throw new NotSupportedException(GetType().Name + " does not support copy/paste or drag'n'drop"); + return CanCopy(nodes) && CanDelete(nodes); } - /* - public virtual bool CanCopy(SharpTreeNode[] nodes) - { - return false; - } - - public virtual bool CanPaste(IDataObject data) - { - return false; - } - - public virtual void Paste(IDataObject data) - { - EnsureLazyChildren(); - Drop(data, Children.Count, DropEffect.Copy); + public virtual void Cut(SharpTreeNode[] nodes) + { + var data = GetDataObject(nodes); + if (data != null) { + // TODO: default cut implementation should not immediately perform deletion, but use 'IsCut' + Clipboard.SetDataObject(data, copy: true); + DeleteWithoutConfirmation(nodes); } - */ - #endregion + } - #region Drag and Drop - public virtual bool CanDrag(SharpTreeNode[] nodes) + public virtual bool CanCopy(SharpTreeNode[] nodes) + { + return false; + } + + public virtual void Copy(SharpTreeNode[] nodes) + { + var data = GetDataObject(nodes); + if (data != null) + Clipboard.SetDataObject(data, copy: true); + } + + protected virtual IDataObject GetDataObject(SharpTreeNode[] nodes) + { + return null; + } + + public virtual bool CanPaste(IDataObject data) { return false; } + public virtual void Paste(IDataObject data) + { + throw new NotSupportedException(GetType().Name + " does not support copy/paste"); + } + #endregion + + #region Drag and Drop public virtual void StartDrag(DependencyObject dragSource, SharpTreeNode[] nodes) { - DragDropEffects effects = DragDropEffects.All; - if (!nodes.All(n => n.CanDelete())) - effects &= ~DragDropEffects.Move; - DragDropEffects result = DragDrop.DoDragDrop(dragSource, Copy(nodes), effects); + // The default drag implementation works by reusing the copy infrastructure. + // Derived classes should override this method + var data = GetDataObject(nodes); + if (data == null) + return; + DragDropEffects effects = DragDropEffects.Copy; + if (CanDelete(nodes)) + effects |= DragDropEffects.Move; + DragDropEffects result = DragDrop.DoDragDrop(dragSource, data, effects); if (result == DragDropEffects.Move) { - foreach (SharpTreeNode node in nodes) - node.DeleteCore(); + DeleteWithoutConfirmation(nodes); } } - public virtual bool CanDrop(DragEventArgs e, int index) + /// + /// Gets the possible drop effects. + /// If the method returns more than one of (Copy|Move|Link), the tree view will choose one effect based + /// on the allowed effects and keyboard status. + /// + public virtual DragDropEffects GetDropEffect(DragEventArgs e, int index) { - return false; + // Since the default drag implementation uses Copy(), + // we'll use Paste() in our default drop implementation. + if (CanPaste(e.Data)) { + // If Ctrl is pressed -> copy + // If moving is not allowed -> copy + // Otherwise: move + if ((e.KeyStates & DragDropKeyStates.ControlKey) != 0 || (e.AllowedEffects & DragDropEffects.Move) == 0) + return DragDropEffects.Copy; + return DragDropEffects.Move; + } + return DragDropEffects.None; } internal void InternalDrop(DragEventArgs e, int index) @@ -612,7 +648,9 @@ namespace ICSharpCode.TreeView public virtual void Drop(DragEventArgs e, int index) { - throw new NotSupportedException(GetType().Name + " does not support Drop()"); + // Since the default drag implementation uses Copy(), + // we'll use Paste() in our default drop implementation. + Paste(e.Data); } #endregion diff --git a/src/Libraries/SharpTreeView/ICSharpCode.TreeView/SharpTreeNodeView.cs b/src/Libraries/SharpTreeView/ICSharpCode.TreeView/SharpTreeNodeView.cs index 78481cf81f..4360a04503 100644 --- a/src/Libraries/SharpTreeView/ICSharpCode.TreeView/SharpTreeNodeView.cs +++ b/src/Libraries/SharpTreeView/ICSharpCode.TreeView/SharpTreeNodeView.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Text; using System.Windows.Controls; @@ -150,8 +151,10 @@ namespace ICSharpCode.TreeView else { result -= 19; } - if (result < 0) - throw new InvalidOperationException(); + if (result < 0) { + Debug.WriteLine("Negative indent level detected for node " + Node); + result = 0; + } return result; } } diff --git a/src/Libraries/SharpTreeView/ICSharpCode.TreeView/SharpTreeView.cs b/src/Libraries/SharpTreeView/ICSharpCode.TreeView/SharpTreeView.cs index 66245b6bb2..214ab62b44 100644 --- a/src/Libraries/SharpTreeView/ICSharpCode.TreeView/SharpTreeView.cs +++ b/src/Libraries/SharpTreeView/ICSharpCode.TreeView/SharpTreeView.cs @@ -355,7 +355,7 @@ namespace ICSharpCode.TreeView if (Root != null && !ShowRoot) { e.Handled = true; - Root.CanDrop(e, Root.Children.Count); + e.Effects = Root.GetDropEffect(e, Root.Children.Count); } } @@ -365,10 +365,12 @@ namespace ICSharpCode.TreeView if (Root != null && !ShowRoot) { e.Handled = true; - Root.InternalDrop(e, Root.Children.Count); + e.Effects = Root.GetDropEffect(e, Root.Children.Count); + if (e.Effects != DragDropEffects.None) + Root.InternalDrop(e, Root.Children.Count); } } - + internal void HandleDragEnter(SharpTreeViewItem item, DragEventArgs e) { HandleDragOver(item, e); @@ -377,14 +379,16 @@ namespace ICSharpCode.TreeView internal void HandleDragOver(SharpTreeViewItem item, DragEventArgs e) { HidePreview(); - + e.Effects = DragDropEffects.None; + var target = GetDropTarget(item, e); if (target != null) { e.Handled = true; + e.Effects = target.Effect; ShowPreview(target.Item, target.Place); } } - + internal void HandleDrop(SharpTreeViewItem item, DragEventArgs e) { try { @@ -393,6 +397,7 @@ namespace ICSharpCode.TreeView var target = GetDropTarget(item, e); if (target != null) { e.Handled = true; + e.Effects = target.Effect; target.Node.InternalDrop(e, target.Index); } } catch (Exception ex) { @@ -400,7 +405,7 @@ namespace ICSharpCode.TreeView throw; } } - + internal void HandleDragLeave(SharpTreeViewItem item, DragEventArgs e) { HidePreview(); @@ -414,6 +419,7 @@ namespace ICSharpCode.TreeView public double Y; public SharpTreeNode Node; public int Index; + public DragDropEffects Effect; } DropTarget GetDropTarget(SharpTreeViewItem item, DragEventArgs e) @@ -485,13 +491,14 @@ namespace ICSharpCode.TreeView GetNodeAndIndex(item, place, out node, out index); if (node != null) { - e.Effects = DragDropEffects.None; - if (node.CanDrop(e, index)) { + var effect = node.GetDropEffect(e, index); + if (effect != DragDropEffects.None) { DropTarget target = new DropTarget() { Item = item, Place = place, Node = node, - Index = index + Index = index, + Effect = effect }; targets.Add(target); } @@ -614,45 +621,78 @@ namespace ICSharpCode.TreeView static void HandleExecuted_Cut(object sender, ExecutedRoutedEventArgs e) { - + e.Handled = true; + SharpTreeView treeView = (SharpTreeView)sender; + var nodes = treeView.GetTopLevelSelection().ToArray(); + if (nodes.Length > 0) + nodes[0].Cut(nodes); } static void HandleCanExecute_Cut(object sender, CanExecuteRoutedEventArgs e) { - e.CanExecute = false; + SharpTreeView treeView = (SharpTreeView)sender; + var nodes = treeView.GetTopLevelSelection().ToArray(); + e.CanExecute = nodes.Length > 0 && nodes[0].CanCut(nodes); + e.Handled = true; } static void HandleExecuted_Copy(object sender, ExecutedRoutedEventArgs e) { - + e.Handled = true; + SharpTreeView treeView = (SharpTreeView)sender; + var nodes = treeView.GetTopLevelSelection().ToArray(); + if (nodes.Length > 0) + nodes[0].Copy(nodes); } static void HandleCanExecute_Copy(object sender, CanExecuteRoutedEventArgs e) { - e.CanExecute = false; + SharpTreeView treeView = (SharpTreeView)sender; + var nodes = treeView.GetTopLevelSelection().ToArray(); + e.CanExecute = nodes.Length > 0 && nodes[0].CanCopy(nodes); + e.Handled = true; } static void HandleExecuted_Paste(object sender, ExecutedRoutedEventArgs e) { - + SharpTreeView treeView = (SharpTreeView)sender; + var data = Clipboard.GetDataObject(); + if (data != null) { + var selectedNode = (treeView.SelectedItem as SharpTreeNode) ?? treeView.Root; + if (selectedNode != null) + selectedNode.Paste(data); + } + e.Handled = true; } static void HandleCanExecute_Paste(object sender, CanExecuteRoutedEventArgs e) { - e.CanExecute = false; + SharpTreeView treeView = (SharpTreeView)sender; + var data = Clipboard.GetDataObject(); + if (data == null) { + e.CanExecute = false; + } else { + var selectedNode = (treeView.SelectedItem as SharpTreeNode) ?? treeView.Root; + e.CanExecute = selectedNode != null && selectedNode.CanPaste(data); + } + e.Handled = true; } - + static void HandleExecuted_Delete(object sender, ExecutedRoutedEventArgs e) { + e.Handled = true; SharpTreeView treeView = (SharpTreeView)sender; - foreach (SharpTreeNode node in treeView.GetTopLevelSelection().ToArray()) - node.Delete(); + var nodes = treeView.GetTopLevelSelection().ToArray(); + if (nodes.Length > 0) + nodes[0].Delete(nodes); } static void HandleCanExecute_Delete(object sender, CanExecuteRoutedEventArgs e) { SharpTreeView treeView = (SharpTreeView)sender; - e.CanExecute = treeView.GetTopLevelSelection().All(node => node.CanDelete()); + var nodes = treeView.GetTopLevelSelection().ToArray(); + e.CanExecute = nodes.Length > 0 && nodes[0].CanDelete(nodes); + e.Handled = true; } /// diff --git a/src/Libraries/SharpTreeView/ICSharpCode.TreeView/SharpTreeViewItem.cs b/src/Libraries/SharpTreeView/ICSharpCode.TreeView/SharpTreeViewItem.cs index 2dc2eed43e..6b6a8dfad3 100644 --- a/src/Libraries/SharpTreeView/ICSharpCode.TreeView/SharpTreeViewItem.cs +++ b/src/Libraries/SharpTreeView/ICSharpCode.TreeView/SharpTreeViewItem.cs @@ -78,9 +78,7 @@ namespace ICSharpCode.TreeView Math.Abs(currentPoint.Y - startPoint.Y) >= SystemParameters.MinimumVerticalDragDistance) { var selection = ParentTreeView.GetTopLevelSelection().ToArray(); - if (Node.CanDrag(selection)) { - Node.StartDrag(this, selection); - } + Node.StartDrag(this, selection); } } }