PageRenderTime 65ms CodeModel.GetById 32ms app.highlight 27ms RepoModel.GetById 1ms app.codeStats 0ms

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