/SharpTreeView/SharpTreeView.cs

http://github.com/icsharpcode/ILSpy · C# · 751 lines · 613 code · 102 blank · 36 comment · 169 complexity · a65557facf706882dffcf669cc07c61e MD5 · raw file

  1. // Copyright (c) 2020 AlphaSierraPapa for the SharpDevelop Team
  2. //
  3. // Permission is hereby granted, free of charge, to any person obtaining a copy of this
  4. // software and associated documentation files (the "Software"), to deal in the Software
  5. // without restriction, including without limitation the rights to use, copy, modify, merge,
  6. // publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
  7. // to whom the Software is furnished to do so, subject to the following conditions:
  8. //
  9. // The above copyright notice and this permission notice shall be included in all copies or
  10. // substantial portions of the Software.
  11. //
  12. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
  13. // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
  14. // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
  15. // FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
  16. // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  17. // DEALINGS IN THE SOFTWARE.
  18. using System;
  19. using System.Collections.Generic;
  20. using System.Collections.Specialized;
  21. using System.Diagnostics;
  22. using System.Linq;
  23. using System.Windows;
  24. using System.Windows.Controls;
  25. using System.Windows.Controls.Primitives;
  26. using System.Windows.Documents;
  27. using System.Windows.Input;
  28. using System.Windows.Threading;
  29. namespace ICSharpCode.TreeView
  30. {
  31. public class SharpTreeView : ListView
  32. {
  33. static SharpTreeView()
  34. {
  35. DefaultStyleKeyProperty.OverrideMetadata(typeof(SharpTreeView),
  36. new FrameworkPropertyMetadata(typeof(SharpTreeView)));
  37. SelectionModeProperty.OverrideMetadata(typeof(SharpTreeView),
  38. new FrameworkPropertyMetadata(SelectionMode.Extended));
  39. AlternationCountProperty.OverrideMetadata(typeof(SharpTreeView),
  40. new FrameworkPropertyMetadata(2));
  41. DefaultItemContainerStyleKey =
  42. new ComponentResourceKey(typeof(SharpTreeView), "DefaultItemContainerStyleKey");
  43. VirtualizingStackPanel.VirtualizationModeProperty.OverrideMetadata(typeof(SharpTreeView),
  44. new FrameworkPropertyMetadata(VirtualizationMode.Recycling));
  45. RegisterCommands();
  46. }
  47. public static ResourceKey DefaultItemContainerStyleKey { get; private set; }
  48. public SharpTreeView()
  49. {
  50. SetResourceReference(ItemContainerStyleProperty, DefaultItemContainerStyleKey);
  51. }
  52. public static readonly DependencyProperty RootProperty =
  53. DependencyProperty.Register("Root", typeof(SharpTreeNode), typeof(SharpTreeView));
  54. public SharpTreeNode Root
  55. {
  56. get { return (SharpTreeNode)GetValue(RootProperty); }
  57. set { SetValue(RootProperty, value); }
  58. }
  59. public static readonly DependencyProperty ShowRootProperty =
  60. DependencyProperty.Register("ShowRoot", typeof(bool), typeof(SharpTreeView),
  61. new FrameworkPropertyMetadata(true));
  62. public bool ShowRoot
  63. {
  64. get { return (bool)GetValue(ShowRootProperty); }
  65. set { SetValue(ShowRootProperty, value); }
  66. }
  67. public static readonly DependencyProperty ShowRootExpanderProperty =
  68. DependencyProperty.Register("ShowRootExpander", typeof(bool), typeof(SharpTreeView),
  69. new FrameworkPropertyMetadata(false));
  70. public bool ShowRootExpander
  71. {
  72. get { return (bool)GetValue(ShowRootExpanderProperty); }
  73. set { SetValue(ShowRootExpanderProperty, value); }
  74. }
  75. public static readonly DependencyProperty AllowDropOrderProperty =
  76. DependencyProperty.Register("AllowDropOrder", typeof(bool), typeof(SharpTreeView));
  77. public bool AllowDropOrder
  78. {
  79. get { return (bool)GetValue(AllowDropOrderProperty); }
  80. set { SetValue(AllowDropOrderProperty, value); }
  81. }
  82. public static readonly DependencyProperty ShowLinesProperty =
  83. DependencyProperty.Register("ShowLines", typeof(bool), typeof(SharpTreeView),
  84. new FrameworkPropertyMetadata(true));
  85. public bool ShowLines
  86. {
  87. get { return (bool)GetValue(ShowLinesProperty); }
  88. set { SetValue(ShowLinesProperty, value); }
  89. }
  90. public static bool GetShowAlternation(DependencyObject obj)
  91. {
  92. return (bool)obj.GetValue(ShowAlternationProperty);
  93. }
  94. public static void SetShowAlternation(DependencyObject obj, bool value)
  95. {
  96. obj.SetValue(ShowAlternationProperty, value);
  97. }
  98. public static readonly DependencyProperty ShowAlternationProperty =
  99. DependencyProperty.RegisterAttached("ShowAlternation", typeof(bool), typeof(SharpTreeView),
  100. new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.Inherits));
  101. protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
  102. {
  103. base.OnPropertyChanged(e);
  104. if (e.Property == RootProperty ||
  105. e.Property == ShowRootProperty ||
  106. e.Property == ShowRootExpanderProperty) {
  107. Reload();
  108. }
  109. }
  110. TreeFlattener flattener;
  111. bool updatesLocked;
  112. public IDisposable LockUpdates()
  113. {
  114. return new UpdateLock(this);
  115. }
  116. class UpdateLock : IDisposable
  117. {
  118. SharpTreeView instance;
  119. public UpdateLock(SharpTreeView instance)
  120. {
  121. this.instance = instance;
  122. this.instance.updatesLocked = true;
  123. }
  124. public void Dispose()
  125. {
  126. this.instance.updatesLocked = false;
  127. }
  128. }
  129. void Reload()
  130. {
  131. if (flattener != null) {
  132. flattener.Stop();
  133. }
  134. if (Root != null) {
  135. if (!(ShowRoot && ShowRootExpander)) {
  136. Root.IsExpanded = true;
  137. }
  138. flattener = new TreeFlattener(Root, ShowRoot);
  139. flattener.CollectionChanged += flattener_CollectionChanged;
  140. this.ItemsSource = flattener;
  141. }
  142. }
  143. void flattener_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
  144. {
  145. // Deselect nodes that are being hidden, if any remain in the tree
  146. if (e.Action == NotifyCollectionChangedAction.Remove && Items.Count > 0) {
  147. List<SharpTreeNode> selectedOldItems = null;
  148. foreach (SharpTreeNode node in e.OldItems) {
  149. if (node.IsSelected) {
  150. if (selectedOldItems == null)
  151. selectedOldItems = new List<SharpTreeNode>();
  152. selectedOldItems.Add(node);
  153. }
  154. }
  155. if (!updatesLocked && selectedOldItems != null) {
  156. var list = SelectedItems.Cast<SharpTreeNode>().Except(selectedOldItems).ToList();
  157. UpdateFocusedNode(list, Math.Max(0, e.OldStartingIndex - 1));
  158. }
  159. }
  160. }
  161. void UpdateFocusedNode(List<SharpTreeNode> newSelection, int topSelectedIndex)
  162. {
  163. if (updatesLocked) return;
  164. SetSelectedItems(newSelection ?? Enumerable.Empty<SharpTreeNode>());
  165. if (SelectedItem == null && this.IsKeyboardFocusWithin) {
  166. // if we removed all selected nodes, then move the focus to the node
  167. // preceding the first of the old selected nodes
  168. SelectedIndex = topSelectedIndex;
  169. if (SelectedItem != null)
  170. FocusNode((SharpTreeNode)SelectedItem);
  171. }
  172. }
  173. protected override DependencyObject GetContainerForItemOverride()
  174. {
  175. return new SharpTreeViewItem();
  176. }
  177. protected override bool IsItemItsOwnContainerOverride(object item)
  178. {
  179. return item is SharpTreeViewItem;
  180. }
  181. protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
  182. {
  183. base.PrepareContainerForItemOverride(element, item);
  184. SharpTreeViewItem container = element as SharpTreeViewItem;
  185. container.ParentTreeView = this;
  186. // Make sure that the line renderer takes into account the new bound data
  187. if (container.NodeView != null) {
  188. container.NodeView.LinesRenderer.InvalidateVisual();
  189. }
  190. }
  191. bool doNotScrollOnExpanding;
  192. /// <summary>
  193. /// Handles the node expanding event in the tree view.
  194. /// This method gets called only if the node is in the visible region (a SharpTreeNodeView exists).
  195. /// </summary>
  196. internal void HandleExpanding(SharpTreeNode node)
  197. {
  198. if (doNotScrollOnExpanding)
  199. return;
  200. SharpTreeNode lastVisibleChild = node;
  201. while (true) {
  202. SharpTreeNode tmp = lastVisibleChild.Children.LastOrDefault(c => c.IsVisible);
  203. if (tmp != null) {
  204. lastVisibleChild = tmp;
  205. } else {
  206. break;
  207. }
  208. }
  209. if (lastVisibleChild != node) {
  210. // Make the the expanded children are visible; but don't scroll down
  211. // to much (keep node itself visible)
  212. base.ScrollIntoView(lastVisibleChild);
  213. // For some reason, this only works properly when delaying it...
  214. Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action(
  215. delegate {
  216. base.ScrollIntoView(node);
  217. }));
  218. }
  219. }
  220. protected override void OnKeyDown(KeyEventArgs e)
  221. {
  222. SharpTreeViewItem container = e.OriginalSource as SharpTreeViewItem;
  223. switch (e.Key) {
  224. case Key.Left:
  225. if (container != null && ItemsControl.ItemsControlFromItemContainer(container) == this) {
  226. if (container.Node.IsExpanded) {
  227. container.Node.IsExpanded = false;
  228. } else if (container.Node.Parent != null) {
  229. this.FocusNode(container.Node.Parent);
  230. }
  231. e.Handled = true;
  232. }
  233. break;
  234. case Key.Right:
  235. if (container != null && ItemsControl.ItemsControlFromItemContainer(container) == this) {
  236. if (!container.Node.IsExpanded && container.Node.ShowExpander) {
  237. container.Node.IsExpanded = true;
  238. } else if (container.Node.Children.Count > 0) {
  239. // jump to first child:
  240. container.MoveFocus(new TraversalRequest(FocusNavigationDirection.Down));
  241. }
  242. e.Handled = true;
  243. }
  244. break;
  245. case Key.Return:
  246. if (container != null && Keyboard.Modifiers == ModifierKeys.None && this.SelectedItems.Count == 1 && this.SelectedItem == container.Node) {
  247. e.Handled = true;
  248. container.Node.ActivateItem(e);
  249. }
  250. break;
  251. case Key.Space:
  252. if (container != null && Keyboard.Modifiers == ModifierKeys.None && this.SelectedItems.Count == 1 && this.SelectedItem == container.Node) {
  253. e.Handled = true;
  254. if (container.Node.IsCheckable) {
  255. if (container.Node.IsChecked == null) // If partially selected, we want to select everything
  256. container.Node.IsChecked = true;
  257. else
  258. container.Node.IsChecked = !container.Node.IsChecked;
  259. } else {
  260. container.Node.ActivateItem(e);
  261. }
  262. }
  263. break;
  264. case Key.Add:
  265. if (container != null && ItemsControl.ItemsControlFromItemContainer(container) == this) {
  266. container.Node.IsExpanded = true;
  267. e.Handled = true;
  268. }
  269. break;
  270. case Key.Subtract:
  271. if (container != null && ItemsControl.ItemsControlFromItemContainer(container) == this) {
  272. container.Node.IsExpanded = false;
  273. e.Handled = true;
  274. }
  275. break;
  276. case Key.Multiply:
  277. if (container != null && ItemsControl.ItemsControlFromItemContainer(container) == this) {
  278. container.Node.IsExpanded = true;
  279. ExpandRecursively(container.Node);
  280. e.Handled = true;
  281. }
  282. break;
  283. case Key.Back:
  284. if (IsTextSearchEnabled) {
  285. var instance = SharpTreeViewTextSearch.GetInstance(this);
  286. if (instance != null) {
  287. instance.RevertLastCharacter();
  288. e.Handled = true;
  289. }
  290. }
  291. break;
  292. }
  293. if (!e.Handled)
  294. base.OnKeyDown(e);
  295. }
  296. protected override void OnTextInput(TextCompositionEventArgs e)
  297. {
  298. if (!string.IsNullOrEmpty(e.Text) && IsTextSearchEnabled && (e.OriginalSource == this || ItemsControl.ItemsControlFromItemContainer(e.OriginalSource as DependencyObject) == this)) {
  299. var instance = SharpTreeViewTextSearch.GetInstance(this);
  300. if (instance != null) {
  301. instance.Search(e.Text);
  302. e.Handled = true;
  303. }
  304. }
  305. if (!e.Handled)
  306. base.OnTextInput(e);
  307. }
  308. void ExpandRecursively(SharpTreeNode node)
  309. {
  310. if (node.CanExpandRecursively) {
  311. node.IsExpanded = true;
  312. foreach (SharpTreeNode child in node.Children) {
  313. ExpandRecursively(child);
  314. }
  315. }
  316. }
  317. /// <summary>
  318. /// Scrolls the specified node in view and sets keyboard focus on it.
  319. /// </summary>
  320. public void FocusNode(SharpTreeNode node)
  321. {
  322. if (node == null)
  323. throw new ArgumentNullException("node");
  324. ScrollIntoView(node);
  325. // WPF's ScrollIntoView() uses the same if/dispatcher construct, so we call OnFocusItem() after the item was brought into view.
  326. if (this.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated) {
  327. OnFocusItem(node);
  328. } else {
  329. this.Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new DispatcherOperationCallback(this.OnFocusItem), node);
  330. }
  331. }
  332. public void ScrollIntoView(SharpTreeNode node)
  333. {
  334. if (node == null)
  335. throw new ArgumentNullException("node");
  336. doNotScrollOnExpanding = true;
  337. foreach (SharpTreeNode ancestor in node.Ancestors())
  338. ancestor.IsExpanded = true;
  339. doNotScrollOnExpanding = false;
  340. base.ScrollIntoView(node);
  341. }
  342. object OnFocusItem(object item)
  343. {
  344. FrameworkElement element = this.ItemContainerGenerator.ContainerFromItem(item) as FrameworkElement;
  345. if (element != null) {
  346. element.Focus();
  347. }
  348. return null;
  349. }
  350. protected override System.Windows.Automation.Peers.AutomationPeer OnCreateAutomationPeer()
  351. {
  352. return new SharpTreeViewAutomationPeer(this);
  353. }
  354. #region Track selection
  355. protected override void OnSelectionChanged(SelectionChangedEventArgs e)
  356. {
  357. foreach (SharpTreeNode node in e.RemovedItems) {
  358. node.IsSelected = false;
  359. }
  360. foreach (SharpTreeNode node in e.AddedItems) {
  361. node.IsSelected = true;
  362. }
  363. base.OnSelectionChanged(e);
  364. }
  365. #endregion
  366. #region Drag and Drop
  367. protected override void OnDragEnter(DragEventArgs e)
  368. {
  369. OnDragOver(e);
  370. }
  371. protected override void OnDragOver(DragEventArgs e)
  372. {
  373. e.Effects = DragDropEffects.None;
  374. if (Root != null && !ShowRoot) {
  375. e.Handled = true;
  376. Root.CanDrop(e, Root.Children.Count);
  377. }
  378. }
  379. protected override void OnDrop(DragEventArgs e)
  380. {
  381. e.Effects = DragDropEffects.None;
  382. if (Root != null && !ShowRoot) {
  383. e.Handled = true;
  384. Root.InternalDrop(e, Root.Children.Count);
  385. }
  386. }
  387. internal void HandleDragEnter(SharpTreeViewItem item, DragEventArgs e)
  388. {
  389. HandleDragOver(item, e);
  390. }
  391. internal void HandleDragOver(SharpTreeViewItem item, DragEventArgs e)
  392. {
  393. HidePreview();
  394. var target = GetDropTarget(item, e);
  395. if (target != null) {
  396. e.Handled = true;
  397. ShowPreview(target.Item, target.Place);
  398. }
  399. }
  400. internal void HandleDrop(SharpTreeViewItem item, DragEventArgs e)
  401. {
  402. try {
  403. HidePreview();
  404. var target = GetDropTarget(item, e);
  405. if (target != null) {
  406. e.Handled = true;
  407. target.Node.InternalDrop(e, target.Index);
  408. }
  409. } catch (Exception ex) {
  410. Debug.WriteLine(ex.ToString());
  411. throw;
  412. }
  413. }
  414. internal void HandleDragLeave(SharpTreeViewItem item, DragEventArgs e)
  415. {
  416. HidePreview();
  417. e.Handled = true;
  418. }
  419. class DropTarget
  420. {
  421. public SharpTreeViewItem Item;
  422. public DropPlace Place;
  423. public double Y;
  424. public SharpTreeNode Node;
  425. public int Index;
  426. }
  427. DropTarget GetDropTarget(SharpTreeViewItem item, DragEventArgs e)
  428. {
  429. var dropTargets = BuildDropTargets(item, e);
  430. var y = e.GetPosition(item).Y;
  431. foreach (var target in dropTargets) {
  432. if (target.Y >= y) {
  433. return target;
  434. }
  435. }
  436. return null;
  437. }
  438. List<DropTarget> BuildDropTargets(SharpTreeViewItem item, DragEventArgs e)
  439. {
  440. var result = new List<DropTarget>();
  441. var node = item.Node;
  442. if (AllowDropOrder) {
  443. TryAddDropTarget(result, item, DropPlace.Before, e);
  444. }
  445. TryAddDropTarget(result, item, DropPlace.Inside, e);
  446. if (AllowDropOrder) {
  447. if (node.IsExpanded && node.Children.Count > 0) {
  448. var firstChildItem = ItemContainerGenerator.ContainerFromItem(node.Children[0]) as SharpTreeViewItem;
  449. TryAddDropTarget(result, firstChildItem, DropPlace.Before, e);
  450. }
  451. else {
  452. TryAddDropTarget(result, item, DropPlace.After, e);
  453. }
  454. }
  455. var h = item.ActualHeight;
  456. var y1 = 0.2 * h;
  457. var y2 = h / 2;
  458. var y3 = h - y1;
  459. if (result.Count == 2) {
  460. if (result[0].Place == DropPlace.Inside &&
  461. result[1].Place != DropPlace.Inside) {
  462. result[0].Y = y3;
  463. }
  464. else if (result[0].Place != DropPlace.Inside &&
  465. result[1].Place == DropPlace.Inside) {
  466. result[0].Y = y1;
  467. }
  468. else {
  469. result[0].Y = y2;
  470. }
  471. }
  472. else if (result.Count == 3) {
  473. result[0].Y = y1;
  474. result[1].Y = y3;
  475. }
  476. if (result.Count > 0) {
  477. result[result.Count - 1].Y = h;
  478. }
  479. return result;
  480. }
  481. void TryAddDropTarget(List<DropTarget> targets, SharpTreeViewItem item, DropPlace place, DragEventArgs e)
  482. {
  483. SharpTreeNode node;
  484. int index;
  485. GetNodeAndIndex(item, place, out node, out index);
  486. if (node != null) {
  487. e.Effects = DragDropEffects.None;
  488. if (node.CanDrop(e, index)) {
  489. DropTarget target = new DropTarget() {
  490. Item = item,
  491. Place = place,
  492. Node = node,
  493. Index = index
  494. };
  495. targets.Add(target);
  496. }
  497. }
  498. }
  499. void GetNodeAndIndex(SharpTreeViewItem item, DropPlace place, out SharpTreeNode node, out int index)
  500. {
  501. node = null;
  502. index = 0;
  503. if (place == DropPlace.Inside) {
  504. node = item.Node;
  505. index = node.Children.Count;
  506. }
  507. else if (place == DropPlace.Before) {
  508. if (item.Node.Parent != null) {
  509. node = item.Node.Parent;
  510. index = node.Children.IndexOf(item.Node);
  511. }
  512. }
  513. else {
  514. if (item.Node.Parent != null) {
  515. node = item.Node.Parent;
  516. index = node.Children.IndexOf(item.Node) + 1;
  517. }
  518. }
  519. }
  520. SharpTreeNodeView previewNodeView;
  521. InsertMarker insertMarker;
  522. DropPlace previewPlace;
  523. enum DropPlace
  524. {
  525. Before, Inside, After
  526. }
  527. void ShowPreview(SharpTreeViewItem item, DropPlace place)
  528. {
  529. previewNodeView = item.NodeView;
  530. previewPlace = place;
  531. if (place == DropPlace.Inside) {
  532. previewNodeView.TextBackground = SystemColors.HighlightBrush;
  533. previewNodeView.Foreground = SystemColors.HighlightTextBrush;
  534. }
  535. else {
  536. if (insertMarker == null) {
  537. var adornerLayer = AdornerLayer.GetAdornerLayer(this);
  538. var adorner = new GeneralAdorner(this);
  539. insertMarker = new InsertMarker();
  540. adorner.Child = insertMarker;
  541. adornerLayer.Add(adorner);
  542. }
  543. insertMarker.Visibility = Visibility.Visible;
  544. var p1 = previewNodeView.TransformToVisual(this).Transform(new Point());
  545. var p = new Point(p1.X + previewNodeView.CalculateIndent() + 4.5, p1.Y - 3);
  546. if (place == DropPlace.After) {
  547. p.Y += previewNodeView.ActualHeight;
  548. }
  549. insertMarker.Margin = new Thickness(p.X, p.Y, 0, 0);
  550. SharpTreeNodeView secondNodeView = null;
  551. var index = flattener.IndexOf(item.Node);
  552. if (place == DropPlace.Before) {
  553. if (index > 0) {
  554. secondNodeView = (ItemContainerGenerator.ContainerFromIndex(index - 1) as SharpTreeViewItem).NodeView;
  555. }
  556. }
  557. else if (index + 1 < flattener.Count) {
  558. secondNodeView = (ItemContainerGenerator.ContainerFromIndex(index + 1) as SharpTreeViewItem).NodeView;
  559. }
  560. var w = p1.X + previewNodeView.ActualWidth - p.X;
  561. if (secondNodeView != null) {
  562. var p2 = secondNodeView.TransformToVisual(this).Transform(new Point());
  563. w = Math.Max(w, p2.X + secondNodeView.ActualWidth - p.X);
  564. }
  565. insertMarker.Width = w + 10;
  566. }
  567. }
  568. void HidePreview()
  569. {
  570. if (previewNodeView != null) {
  571. previewNodeView.ClearValue(SharpTreeNodeView.TextBackgroundProperty);
  572. previewNodeView.ClearValue(SharpTreeNodeView.ForegroundProperty);
  573. if (insertMarker != null) {
  574. insertMarker.Visibility = Visibility.Collapsed;
  575. }
  576. previewNodeView = null;
  577. }
  578. }
  579. #endregion
  580. #region Cut / Copy / Paste / Delete Commands
  581. static void RegisterCommands()
  582. {
  583. CommandManager.RegisterClassCommandBinding(typeof(SharpTreeView),
  584. new CommandBinding(ApplicationCommands.Cut, HandleExecuted_Cut, HandleCanExecute_Cut));
  585. CommandManager.RegisterClassCommandBinding(typeof(SharpTreeView),
  586. new CommandBinding(ApplicationCommands.Copy, HandleExecuted_Copy, HandleCanExecute_Copy));
  587. CommandManager.RegisterClassCommandBinding(typeof(SharpTreeView),
  588. new CommandBinding(ApplicationCommands.Paste, HandleExecuted_Paste, HandleCanExecute_Paste));
  589. CommandManager.RegisterClassCommandBinding(typeof(SharpTreeView),
  590. new CommandBinding(ApplicationCommands.Delete, HandleExecuted_Delete, HandleCanExecute_Delete));
  591. }
  592. static void HandleExecuted_Cut(object sender, ExecutedRoutedEventArgs e)
  593. {
  594. }
  595. static void HandleCanExecute_Cut(object sender, CanExecuteRoutedEventArgs e)
  596. {
  597. e.CanExecute = false;
  598. }
  599. static void HandleExecuted_Copy(object sender, ExecutedRoutedEventArgs e)
  600. {
  601. }
  602. static void HandleCanExecute_Copy(object sender, CanExecuteRoutedEventArgs e)
  603. {
  604. e.CanExecute = false;
  605. }
  606. static void HandleExecuted_Paste(object sender, ExecutedRoutedEventArgs e)
  607. {
  608. }
  609. static void HandleCanExecute_Paste(object sender, CanExecuteRoutedEventArgs e)
  610. {
  611. e.CanExecute = false;
  612. }
  613. static void HandleExecuted_Delete(object sender, ExecutedRoutedEventArgs e)
  614. {
  615. SharpTreeView treeView = (SharpTreeView)sender;
  616. treeView.updatesLocked = true;
  617. int selectedIndex = -1;
  618. try {
  619. foreach (SharpTreeNode node in treeView.GetTopLevelSelection().ToArray()) {
  620. if (selectedIndex == -1)
  621. selectedIndex = treeView.flattener.IndexOf(node);
  622. node.Delete();
  623. }
  624. } finally {
  625. treeView.updatesLocked = false;
  626. treeView.UpdateFocusedNode(null, Math.Max(0, selectedIndex - 1));
  627. }
  628. }
  629. static void HandleCanExecute_Delete(object sender, CanExecuteRoutedEventArgs e)
  630. {
  631. SharpTreeView treeView = (SharpTreeView)sender;
  632. e.CanExecute = treeView.GetTopLevelSelection().All(node => node.CanDelete());
  633. }
  634. /// <summary>
  635. /// Gets the selected items which do not have any of their ancestors selected.
  636. /// </summary>
  637. public IEnumerable<SharpTreeNode> GetTopLevelSelection()
  638. {
  639. var selection = this.SelectedItems.OfType<SharpTreeNode>();
  640. var selectionHash = new HashSet<SharpTreeNode>(selection);
  641. return selection.Where(item => item.Ancestors().All(a => !selectionHash.Contains(a)));
  642. }
  643. #endregion
  644. public void SetSelectedNodes(IEnumerable<SharpTreeNode> nodes)
  645. {
  646. this.SetSelectedItems(nodes.ToList());
  647. }
  648. }
  649. }