You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

831 lines
22 KiB

  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. get { return (SharpTreeNode)GetValue(RootProperty); }
  56. set { SetValue(RootProperty, value); }
  57. }
  58. public static readonly DependencyProperty ShowRootProperty =
  59. DependencyProperty.Register("ShowRoot", typeof(bool), typeof(SharpTreeView),
  60. new FrameworkPropertyMetadata(true));
  61. public bool ShowRoot {
  62. get { return (bool)GetValue(ShowRootProperty); }
  63. set { SetValue(ShowRootProperty, value); }
  64. }
  65. public static readonly DependencyProperty ShowRootExpanderProperty =
  66. DependencyProperty.Register("ShowRootExpander", typeof(bool), typeof(SharpTreeView),
  67. new FrameworkPropertyMetadata(false));
  68. public bool ShowRootExpander {
  69. get { return (bool)GetValue(ShowRootExpanderProperty); }
  70. set { SetValue(ShowRootExpanderProperty, value); }
  71. }
  72. public static readonly DependencyProperty AllowDropOrderProperty =
  73. DependencyProperty.Register("AllowDropOrder", typeof(bool), typeof(SharpTreeView));
  74. public bool AllowDropOrder {
  75. get { return (bool)GetValue(AllowDropOrderProperty); }
  76. set { SetValue(AllowDropOrderProperty, value); }
  77. }
  78. public static readonly DependencyProperty ShowLinesProperty =
  79. DependencyProperty.Register("ShowLines", typeof(bool), typeof(SharpTreeView),
  80. new FrameworkPropertyMetadata(true));
  81. public bool ShowLines {
  82. get { return (bool)GetValue(ShowLinesProperty); }
  83. set { SetValue(ShowLinesProperty, value); }
  84. }
  85. public static bool GetShowAlternation(DependencyObject obj)
  86. {
  87. return (bool)obj.GetValue(ShowAlternationProperty);
  88. }
  89. public static void SetShowAlternation(DependencyObject obj, bool value)
  90. {
  91. obj.SetValue(ShowAlternationProperty, value);
  92. }
  93. public static readonly DependencyProperty ShowAlternationProperty =
  94. DependencyProperty.RegisterAttached("ShowAlternation", typeof(bool), typeof(SharpTreeView),
  95. new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.Inherits));
  96. protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
  97. {
  98. base.OnPropertyChanged(e);
  99. if (e.Property == RootProperty ||
  100. e.Property == ShowRootProperty ||
  101. e.Property == ShowRootExpanderProperty)
  102. {
  103. Reload();
  104. }
  105. }
  106. TreeFlattener flattener;
  107. bool updatesLocked;
  108. public IDisposable LockUpdates()
  109. {
  110. return new UpdateLock(this);
  111. }
  112. class UpdateLock : IDisposable
  113. {
  114. SharpTreeView instance;
  115. public UpdateLock(SharpTreeView instance)
  116. {
  117. this.instance = instance;
  118. this.instance.updatesLocked = true;
  119. }
  120. public void Dispose()
  121. {
  122. this.instance.updatesLocked = false;
  123. }
  124. }
  125. void Reload()
  126. {
  127. if (flattener != null)
  128. {
  129. flattener.Stop();
  130. }
  131. if (Root != null)
  132. {
  133. if (!(ShowRoot && ShowRootExpander))
  134. {
  135. Root.IsExpanded = true;
  136. }
  137. flattener = new TreeFlattener(Root, ShowRoot);
  138. flattener.CollectionChanged += flattener_CollectionChanged;
  139. this.ItemsSource = flattener;
  140. }
  141. }
  142. void flattener_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
  143. {
  144. // Deselect nodes that are being hidden, if any remain in the tree
  145. if (e.Action == NotifyCollectionChangedAction.Remove && Items.Count > 0)
  146. {
  147. List<SharpTreeNode> selectedOldItems = null;
  148. foreach (SharpTreeNode node in e.OldItems)
  149. {
  150. if (node.IsSelected)
  151. {
  152. if (selectedOldItems == null)
  153. selectedOldItems = new List<SharpTreeNode>();
  154. selectedOldItems.Add(node);
  155. }
  156. }
  157. if (!updatesLocked && selectedOldItems != null)
  158. {
  159. var list = SelectedItems.Cast<SharpTreeNode>().Except(selectedOldItems).ToList();
  160. UpdateFocusedNode(list, Math.Max(0, e.OldStartingIndex - 1));
  161. }
  162. }
  163. }
  164. void UpdateFocusedNode(List<SharpTreeNode> newSelection, int topSelectedIndex)
  165. {
  166. if (updatesLocked)
  167. return;
  168. SetSelectedItems(newSelection ?? Enumerable.Empty<SharpTreeNode>());
  169. if (SelectedItem == null && this.IsKeyboardFocusWithin)
  170. {
  171. // if we removed all selected nodes, then move the focus to the node
  172. // preceding the first of the old selected nodes
  173. SelectedIndex = topSelectedIndex;
  174. if (SelectedItem != null)
  175. FocusNode((SharpTreeNode)SelectedItem);
  176. }
  177. }
  178. protected override DependencyObject GetContainerForItemOverride()
  179. {
  180. return new SharpTreeViewItem();
  181. }
  182. protected override bool IsItemItsOwnContainerOverride(object item)
  183. {
  184. return item is SharpTreeViewItem;
  185. }
  186. protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
  187. {
  188. base.PrepareContainerForItemOverride(element, item);
  189. SharpTreeViewItem container = element as SharpTreeViewItem;
  190. container.ParentTreeView = this;
  191. // Make sure that the line renderer takes into account the new bound data
  192. if (container.NodeView != null)
  193. {
  194. container.NodeView.LinesRenderer.InvalidateVisual();
  195. }
  196. }
  197. bool doNotScrollOnExpanding;
  198. /// <summary>
  199. /// Handles the node expanding event in the tree view.
  200. /// This method gets called only if the node is in the visible region (a SharpTreeNodeView exists).
  201. /// </summary>
  202. internal void HandleExpanding(SharpTreeNode node)
  203. {
  204. if (doNotScrollOnExpanding)
  205. return;
  206. SharpTreeNode lastVisibleChild = node;
  207. while (true)
  208. {
  209. SharpTreeNode tmp = lastVisibleChild.Children.LastOrDefault(c => c.IsVisible);
  210. if (tmp != null)
  211. {
  212. lastVisibleChild = tmp;
  213. }
  214. else
  215. {
  216. break;
  217. }
  218. }
  219. if (lastVisibleChild != node)
  220. {
  221. // Make the the expanded children are visible; but don't scroll down
  222. // to much (keep node itself visible)
  223. base.ScrollIntoView(lastVisibleChild);
  224. // For some reason, this only works properly when delaying it...
  225. Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action(
  226. delegate {
  227. base.ScrollIntoView(node);
  228. }));
  229. }
  230. }
  231. protected override void OnKeyDown(KeyEventArgs e)
  232. {
  233. SharpTreeViewItem container = e.OriginalSource as SharpTreeViewItem;
  234. switch (e.Key)
  235. {
  236. case Key.Left:
  237. if (container != null && ItemsControl.ItemsControlFromItemContainer(container) == this)
  238. {
  239. if (container.Node.IsExpanded)
  240. {
  241. container.Node.IsExpanded = false;
  242. }
  243. else if (container.Node.Parent != null)
  244. {
  245. this.FocusNode(container.Node.Parent);
  246. }
  247. e.Handled = true;
  248. }
  249. break;
  250. case Key.Right:
  251. if (container != null && ItemsControl.ItemsControlFromItemContainer(container) == this)
  252. {
  253. if (!container.Node.IsExpanded && container.Node.ShowExpander)
  254. {
  255. container.Node.IsExpanded = true;
  256. }
  257. else if (container.Node.Children.Count > 0)
  258. {
  259. // jump to first child:
  260. container.MoveFocus(new TraversalRequest(FocusNavigationDirection.Down));
  261. }
  262. e.Handled = true;
  263. }
  264. break;
  265. case Key.Return:
  266. if (container != null && Keyboard.Modifiers == ModifierKeys.None && this.SelectedItems.Count == 1 && this.SelectedItem == container.Node)
  267. {
  268. e.Handled = true;
  269. container.Node.ActivateItem(e);
  270. }
  271. break;
  272. case Key.Space:
  273. if (container != null && Keyboard.Modifiers == ModifierKeys.None && this.SelectedItems.Count == 1 && this.SelectedItem == container.Node)
  274. {
  275. e.Handled = true;
  276. if (container.Node.IsCheckable)
  277. {
  278. if (container.Node.IsChecked == null) // If partially selected, we want to select everything
  279. container.Node.IsChecked = true;
  280. else
  281. container.Node.IsChecked = !container.Node.IsChecked;
  282. }
  283. else
  284. {
  285. container.Node.ActivateItem(e);
  286. }
  287. }
  288. break;
  289. case Key.Add:
  290. if (container != null && ItemsControl.ItemsControlFromItemContainer(container) == this)
  291. {
  292. container.Node.IsExpanded = true;
  293. e.Handled = true;
  294. }
  295. break;
  296. case Key.Subtract:
  297. if (container != null && ItemsControl.ItemsControlFromItemContainer(container) == this)
  298. {
  299. container.Node.IsExpanded = false;
  300. e.Handled = true;
  301. }
  302. break;
  303. case Key.Multiply:
  304. if (container != null && ItemsControl.ItemsControlFromItemContainer(container) == this)
  305. {
  306. container.Node.IsExpanded = true;
  307. ExpandRecursively(container.Node);
  308. e.Handled = true;
  309. }
  310. break;
  311. case Key.Back:
  312. if (IsTextSearchEnabled)
  313. {
  314. var instance = SharpTreeViewTextSearch.GetInstance(this);
  315. if (instance != null)
  316. {
  317. instance.RevertLastCharacter();
  318. e.Handled = true;
  319. }
  320. }
  321. break;
  322. }
  323. if (!e.Handled)
  324. base.OnKeyDown(e);
  325. }
  326. protected override void OnTextInput(TextCompositionEventArgs e)
  327. {
  328. if (!string.IsNullOrEmpty(e.Text) && IsTextSearchEnabled && (e.OriginalSource == this || ItemsControl.ItemsControlFromItemContainer(e.OriginalSource as DependencyObject) == this))
  329. {
  330. var instance = SharpTreeViewTextSearch.GetInstance(this);
  331. if (instance != null)
  332. {
  333. instance.Search(e.Text);
  334. e.Handled = true;
  335. }
  336. }
  337. if (!e.Handled)
  338. base.OnTextInput(e);
  339. }
  340. void ExpandRecursively(SharpTreeNode node)
  341. {
  342. if (node.CanExpandRecursively)
  343. {
  344. node.IsExpanded = true;
  345. foreach (SharpTreeNode child in node.Children)
  346. {
  347. ExpandRecursively(child);
  348. }
  349. }
  350. }
  351. /// <summary>
  352. /// Scrolls the specified node in view and sets keyboard focus on it.
  353. /// </summary>
  354. public void FocusNode(SharpTreeNode node)
  355. {
  356. if (node == null)
  357. throw new ArgumentNullException("node");
  358. ScrollIntoView(node);
  359. // WPF's ScrollIntoView() uses the same if/dispatcher construct, so we call OnFocusItem() after the item was brought into view.
  360. if (this.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
  361. {
  362. OnFocusItem(node);
  363. }
  364. else
  365. {
  366. this.Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new DispatcherOperationCallback(this.OnFocusItem), node);
  367. }
  368. }
  369. public void ScrollIntoView(SharpTreeNode node)
  370. {
  371. if (node == null)
  372. throw new ArgumentNullException("node");
  373. doNotScrollOnExpanding = true;
  374. foreach (SharpTreeNode ancestor in node.Ancestors())
  375. ancestor.IsExpanded = true;
  376. doNotScrollOnExpanding = false;
  377. base.ScrollIntoView(node);
  378. }
  379. object OnFocusItem(object item)
  380. {
  381. FrameworkElement element = this.ItemContainerGenerator.ContainerFromItem(item) as FrameworkElement;
  382. if (element != null)
  383. {
  384. element.Focus();
  385. }
  386. return null;
  387. }
  388. protected override System.Windows.Automation.Peers.AutomationPeer OnCreateAutomationPeer()
  389. {
  390. return new SharpTreeViewAutomationPeer(this);
  391. }
  392. #region Track selection
  393. protected override void OnSelectionChanged(SelectionChangedEventArgs e)
  394. {
  395. foreach (SharpTreeNode node in e.RemovedItems)
  396. {
  397. node.IsSelected = false;
  398. }
  399. foreach (SharpTreeNode node in e.AddedItems)
  400. {
  401. node.IsSelected = true;
  402. }
  403. base.OnSelectionChanged(e);
  404. }
  405. #endregion
  406. #region Drag and Drop
  407. protected override void OnDragEnter(DragEventArgs e)
  408. {
  409. OnDragOver(e);
  410. }
  411. protected override void OnDragOver(DragEventArgs e)
  412. {
  413. e.Effects = DragDropEffects.None;
  414. if (Root != null && !ShowRoot)
  415. {
  416. e.Handled = true;
  417. Root.CanDrop(e, Root.Children.Count);
  418. }
  419. }
  420. protected override void OnDrop(DragEventArgs e)
  421. {
  422. e.Effects = DragDropEffects.None;
  423. if (Root != null && !ShowRoot)
  424. {
  425. e.Handled = true;
  426. Root.InternalDrop(e, Root.Children.Count);
  427. }
  428. }
  429. internal void HandleDragEnter(SharpTreeViewItem item, DragEventArgs e)
  430. {
  431. HandleDragOver(item, e);
  432. }
  433. internal void HandleDragOver(SharpTreeViewItem item, DragEventArgs e)
  434. {
  435. HidePreview();
  436. var target = GetDropTarget(item, e);
  437. if (target != null)
  438. {
  439. e.Handled = true;
  440. ShowPreview(target.Item, target.Place);
  441. }
  442. }
  443. internal void HandleDrop(SharpTreeViewItem item, DragEventArgs e)
  444. {
  445. try
  446. {
  447. HidePreview();
  448. var target = GetDropTarget(item, e);
  449. if (target != null)
  450. {
  451. e.Handled = true;
  452. target.Node.InternalDrop(e, target.Index);
  453. }
  454. }
  455. catch (Exception ex)
  456. {
  457. Debug.WriteLine(ex.ToString());
  458. throw;
  459. }
  460. }
  461. internal void HandleDragLeave(SharpTreeViewItem item, DragEventArgs e)
  462. {
  463. HidePreview();
  464. e.Handled = true;
  465. }
  466. class DropTarget
  467. {
  468. public SharpTreeViewItem Item;
  469. public DropPlace Place;
  470. public double Y;
  471. public SharpTreeNode Node;
  472. public int Index;
  473. }
  474. DropTarget GetDropTarget(SharpTreeViewItem item, DragEventArgs e)
  475. {
  476. var dropTargets = BuildDropTargets(item, e);
  477. var y = e.GetPosition(item).Y;
  478. foreach (var target in dropTargets)
  479. {
  480. if (target.Y >= y)
  481. {
  482. return target;
  483. }
  484. }
  485. return null;
  486. }
  487. List<DropTarget> BuildDropTargets(SharpTreeViewItem item, DragEventArgs e)
  488. {
  489. var result = new List<DropTarget>();
  490. var node = item.Node;
  491. if (AllowDropOrder)
  492. {
  493. TryAddDropTarget(result, item, DropPlace.Before, e);
  494. }
  495. TryAddDropTarget(result, item, DropPlace.Inside, e);
  496. if (AllowDropOrder)
  497. {
  498. if (node.IsExpanded && node.Children.Count > 0)
  499. {
  500. var firstChildItem = ItemContainerGenerator.ContainerFromItem(node.Children[0]) as SharpTreeViewItem;
  501. TryAddDropTarget(result, firstChildItem, DropPlace.Before, e);
  502. }
  503. else
  504. {
  505. TryAddDropTarget(result, item, DropPlace.After, e);
  506. }
  507. }
  508. var h = item.ActualHeight;
  509. var y1 = 0.2 * h;
  510. var y2 = h / 2;
  511. var y3 = h - y1;
  512. if (result.Count == 2)
  513. {
  514. if (result[0].Place == DropPlace.Inside &&
  515. result[1].Place != DropPlace.Inside)
  516. {
  517. result[0].Y = y3;
  518. }
  519. else if (result[0].Place != DropPlace.Inside &&
  520. result[1].Place == DropPlace.Inside)
  521. {
  522. result[0].Y = y1;
  523. }
  524. else
  525. {
  526. result[0].Y = y2;
  527. }
  528. }
  529. else if (result.Count == 3)
  530. {
  531. result[0].Y = y1;
  532. result[1].Y = y3;
  533. }
  534. if (result.Count > 0)
  535. {
  536. result[result.Count - 1].Y = h;
  537. }
  538. return result;
  539. }
  540. void TryAddDropTarget(List<DropTarget> targets, SharpTreeViewItem item, DropPlace place, DragEventArgs e)
  541. {
  542. SharpTreeNode node;
  543. int index;
  544. GetNodeAndIndex(item, place, out node, out index);
  545. if (node != null)
  546. {
  547. e.Effects = DragDropEffects.None;
  548. if (node.CanDrop(e, index))
  549. {
  550. DropTarget target = new DropTarget() {
  551. Item = item,
  552. Place = place,
  553. Node = node,
  554. Index = index
  555. };
  556. targets.Add(target);
  557. }
  558. }
  559. }
  560. void GetNodeAndIndex(SharpTreeViewItem item, DropPlace place, out SharpTreeNode node, out int index)
  561. {
  562. node = null;
  563. index = 0;
  564. if (place == DropPlace.Inside)
  565. {
  566. node = item.Node;
  567. index = node.Children.Count;
  568. }
  569. else if (place == DropPlace.Before)
  570. {
  571. if (item.Node.Parent != null)
  572. {
  573. node = item.Node.Parent;
  574. index = node.Children.IndexOf(item.Node);
  575. }
  576. }
  577. else
  578. {
  579. if (item.Node.Parent != null)
  580. {
  581. node = item.Node.Parent;
  582. index = node.Children.IndexOf(item.Node) + 1;
  583. }
  584. }
  585. }
  586. SharpTreeNodeView previewNodeView;
  587. InsertMarker insertMarker;
  588. DropPlace previewPlace;
  589. enum DropPlace
  590. {
  591. Before, Inside, After
  592. }
  593. void ShowPreview(SharpTreeViewItem item, DropPlace place)
  594. {
  595. previewNodeView = item.NodeView;
  596. previewPlace = place;
  597. if (place == DropPlace.Inside)
  598. {
  599. previewNodeView.TextBackground = SystemColors.HighlightBrush;
  600. previewNodeView.Foreground = SystemColors.HighlightTextBrush;
  601. }
  602. else
  603. {
  604. if (insertMarker == null)
  605. {
  606. var adornerLayer = AdornerLayer.GetAdornerLayer(this);
  607. var adorner = new GeneralAdorner(this);
  608. insertMarker = new InsertMarker();
  609. adorner.Child = insertMarker;
  610. adornerLayer.Add(adorner);
  611. }
  612. insertMarker.Visibility = Visibility.Visible;
  613. var p1 = previewNodeView.TransformToVisual(this).Transform(new Point());
  614. var p = new Point(p1.X + previewNodeView.CalculateIndent() + 4.5, p1.Y - 3);
  615. if (place == DropPlace.After)
  616. {
  617. p.Y += previewNodeView.ActualHeight;
  618. }
  619. insertMarker.Margin = new Thickness(p.X, p.Y, 0, 0);
  620. SharpTreeNodeView secondNodeView = null;
  621. var index = flattener.IndexOf(item.Node);
  622. if (place == DropPlace.Before)
  623. {
  624. if (index > 0)
  625. {
  626. secondNodeView = (ItemContainerGenerator.ContainerFromIndex(index - 1) as SharpTreeViewItem).NodeView;
  627. }
  628. }
  629. else if (index + 1 < flattener.Count)
  630. {
  631. secondNodeView = (ItemContainerGenerator.ContainerFromIndex(index + 1) as SharpTreeViewItem).NodeView;
  632. }
  633. var w = p1.X + previewNodeView.ActualWidth - p.X;
  634. if (secondNodeView != null)
  635. {
  636. var p2 = secondNodeView.TransformToVisual(this).Transform(new Point());
  637. w = Math.Max(w, p2.X + secondNodeView.ActualWidth - p.X);
  638. }
  639. insertMarker.Width = w + 10;
  640. }
  641. }
  642. void HidePreview()
  643. {
  644. if (previewNodeView != null)
  645. {
  646. previewNodeView.ClearValue(SharpTreeNodeView.TextBackgroundProperty);
  647. previewNodeView.ClearValue(SharpTreeNodeView.ForegroundProperty);
  648. if (insertMarker != null)
  649. {
  650. insertMarker.Visibility = Visibility.Collapsed;
  651. }
  652. previewNodeView = null;
  653. }
  654. }
  655. #endregion
  656. #region Cut / Copy / Paste / Delete Commands
  657. static void RegisterCommands()
  658. {
  659. CommandManager.RegisterClassCommandBinding(typeof(SharpTreeView),
  660. new CommandBinding(ApplicationCommands.Cut, HandleExecuted_Cut, HandleCanExecute_Cut));
  661. CommandManager.RegisterClassCommandBinding(typeof(SharpTreeView),
  662. new CommandBinding(ApplicationCommands.Copy, HandleExecuted_Copy, HandleCanExecute_Copy));
  663. CommandManager.RegisterClassCommandBinding(typeof(SharpTreeView),
  664. new CommandBinding(ApplicationCommands.Paste, HandleExecuted_Paste, HandleCanExecute_Paste));
  665. CommandManager.RegisterClassCommandBinding(typeof(SharpTreeView),
  666. new CommandBinding(ApplicationCommands.Delete, HandleExecuted_Delete, HandleCanExecute_Delete));
  667. }
  668. static void HandleExecuted_Cut(object sender, ExecutedRoutedEventArgs e)
  669. {
  670. }
  671. static void HandleCanExecute_Cut(object sender, CanExecuteRoutedEventArgs e)
  672. {
  673. e.CanExecute = false;
  674. }
  675. static void HandleExecuted_Copy(object sender, ExecutedRoutedEventArgs e)
  676. {
  677. }
  678. static void HandleCanExecute_Copy(object sender, CanExecuteRoutedEventArgs e)
  679. {
  680. e.CanExecute = false;
  681. }
  682. static void HandleExecuted_Paste(object sender, ExecutedRoutedEventArgs e)
  683. {
  684. }
  685. static void HandleCanExecute_Paste(object sender, CanExecuteRoutedEventArgs e)
  686. {
  687. e.CanExecute = false;
  688. }
  689. static void HandleExecuted_Delete(object sender, ExecutedRoutedEventArgs e)
  690. {
  691. SharpTreeView treeView = (SharpTreeView)sender;
  692. treeView.updatesLocked = true;
  693. int selectedIndex = -1;
  694. try
  695. {
  696. foreach (SharpTreeNode node in treeView.GetTopLevelSelection().ToArray())
  697. {
  698. if (selectedIndex == -1)
  699. selectedIndex = treeView.flattener.IndexOf(node);
  700. node.Delete();
  701. }
  702. }
  703. finally
  704. {
  705. treeView.updatesLocked = false;
  706. treeView.UpdateFocusedNode(null, Math.Max(0, selectedIndex - 1));
  707. }
  708. }
  709. static void HandleCanExecute_Delete(object sender, CanExecuteRoutedEventArgs e)
  710. {
  711. SharpTreeView treeView = (SharpTreeView)sender;
  712. e.CanExecute = treeView.GetTopLevelSelection().All(node => node.CanDelete());
  713. }
  714. /// <summary>
  715. /// Gets the selected items which do not have any of their ancestors selected.
  716. /// </summary>
  717. public IEnumerable<SharpTreeNode> GetTopLevelSelection()
  718. {
  719. var selection = this.SelectedItems.OfType<SharpTreeNode>();
  720. var selectionHash = new HashSet<SharpTreeNode>(selection);
  721. return selection.Where(item => item.Ancestors().All(a => !selectionHash.Contains(a)));
  722. }
  723. #endregion
  724. public void SetSelectedNodes(IEnumerable<SharpTreeNode> nodes)
  725. {
  726. this.SetSelectedItems(nodes.ToList());
  727. }
  728. }
  729. }