Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

SharpTreeView: improve left/right keyboard navigation

  • Loading branch information...
commit ba6fc13648c723cc3ad933ff62e41b627ad03c49 1 parent 937f0e6
@dgrunwald dgrunwald authored
Showing with 88 additions and 34 deletions.
  1. +81 −21 SharpTreeView/SharpTreeView.cs
  2. +7 −13 SharpTreeView/SharpTreeViewItem.cs
View
102 SharpTreeView/SharpTreeView.cs
@@ -6,12 +6,14 @@
using System.Diagnostics;
using System.Linq;
using System.Text;
-using System.Windows.Controls;
using System.Windows;
-using System.Windows.Media;
+using System.Windows.Controls;
+using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Threading;
namespace ICSharpCode.TreeView
{
@@ -20,19 +22,19 @@ public class SharpTreeView : ListView
static SharpTreeView()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(SharpTreeView),
- new FrameworkPropertyMetadata(typeof(SharpTreeView)));
+ new FrameworkPropertyMetadata(typeof(SharpTreeView)));
SelectionModeProperty.OverrideMetadata(typeof(SharpTreeView),
- new FrameworkPropertyMetadata(SelectionMode.Extended));
+ new FrameworkPropertyMetadata(SelectionMode.Extended));
AlternationCountProperty.OverrideMetadata(typeof(SharpTreeView),
- new FrameworkPropertyMetadata(2));
+ new FrameworkPropertyMetadata(2));
- DefaultItemContainerStyleKey =
+ DefaultItemContainerStyleKey =
new ComponentResourceKey(typeof(SharpTreeView), "DefaultItemContainerStyleKey");
VirtualizingStackPanel.VirtualizationModeProperty.OverrideMetadata(typeof(SharpTreeView),
- new FrameworkPropertyMetadata(VirtualizationMode.Recycling));
+ new FrameworkPropertyMetadata(VirtualizationMode.Recycling));
}
public static ResourceKey DefaultItemContainerStyleKey { get; private set; }
@@ -53,7 +55,7 @@ public SharpTreeNode Root
public static readonly DependencyProperty ShowRootProperty =
DependencyProperty.Register("ShowRoot", typeof(bool), typeof(SharpTreeView),
- new FrameworkPropertyMetadata(true));
+ new FrameworkPropertyMetadata(true));
public bool ShowRoot
{
@@ -63,7 +65,7 @@ public bool ShowRoot
public static readonly DependencyProperty ShowRootExpanderProperty =
DependencyProperty.Register("ShowRootExpander", typeof(bool), typeof(SharpTreeView),
- new FrameworkPropertyMetadata(false));
+ new FrameworkPropertyMetadata(false));
public bool ShowRootExpander
{
@@ -82,7 +84,7 @@ public bool AllowDropOrder
public static readonly DependencyProperty ShowLinesProperty =
DependencyProperty.Register("ShowLines", typeof(bool), typeof(SharpTreeView),
- new FrameworkPropertyMetadata(true));
+ new FrameworkPropertyMetadata(true));
public bool ShowLines
{
@@ -102,14 +104,14 @@ public static void SetShowAlternation(DependencyObject obj, bool value)
public static readonly DependencyProperty ShowAlternationProperty =
DependencyProperty.RegisterAttached("ShowAlternation", typeof(bool), typeof(SharpTreeView),
- new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.Inherits));
-
+ new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.Inherits));
+
protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
{
base.OnPropertyChanged(e);
- if (e.Property == RootProperty ||
- e.Property == ShowRootProperty ||
- e.Property == ShowRootExpanderProperty) {
+ if (e.Property == RootProperty ||
+ e.Property == ShowRootProperty ||
+ e.Property == ShowRootExpanderProperty) {
Reload();
}
}
@@ -157,6 +159,64 @@ internal void HandleCollapsing(SharpTreeNode Node)
SetSelectedItems(list);
}
}
+
+ protected override void OnKeyDown(KeyEventArgs e)
+ {
+ switch (e.Key) {
+ case Key.Left:
+ case Key.Right:
+ SharpTreeViewItem container = e.OriginalSource as SharpTreeViewItem;
+ if (container != null && ItemsControl.ItemsControlFromItemContainer(container) == this) {
+ bool newExpanded = (e.Key == Key.Right);
+ if (container.Node.IsExpanded != newExpanded) {
+ container.Node.IsExpanded = newExpanded;
+ } else {
+ // already in collapsed/expanded - jump to parent/first child:
+ if (e.Key == Key.Right) {
+ container.MoveFocus(new TraversalRequest(FocusNavigationDirection.Down));
+ } else if (container.Node.Parent != null) {
+ this.FocusNode(container.Node.Parent);
+ }
+ }
+ e.Handled = true;
+ }
+ break;
+ default:
+ base.OnKeyDown(e);
+ break;
+ }
+ }
+
+ public void FocusNode(SharpTreeNode node)
+ {
+ if (node == null)
+ throw new ArgumentNullException("node");
+ ScrollIntoView(node);
+ // WPF's ScrollIntoView() uses the same if/dispatcher construct, so we call OnFocusItem() after the item was brought into view.
+ if (this.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated) {
+ OnFocusItem(node);
+ } else {
+ this.Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new DispatcherOperationCallback(this.OnFocusItem), node);
+ }
+ }
+
+ public void ScrollIntoView(SharpTreeNode node)
+ {
+ if (node == null)
+ throw new ArgumentNullException("node");
+ foreach (SharpTreeNode ancestor in node.Ancestors())
+ ancestor.IsExpanded = true;
+ base.ScrollIntoView(node);
+ }
+
+ object OnFocusItem(object item)
+ {
+ FrameworkElement element = this.ItemContainerGenerator.ContainerFromItem(item) as FrameworkElement;
+ if (element != null) {
+ element.Focus();
+ }
+ return null;
+ }
#region Track selection
@@ -265,7 +325,7 @@ class DropTarget
{
public SharpTreeViewItem Item;
public DropPlace Place;
- public double Y;
+ public double Y;
public SharpTreeNode Node;
public int Index;
}
@@ -288,7 +348,7 @@ List<DropTarget> BuildDropTargets(SharpTreeViewItem item, DragEventArgs e)
var node = item.Node;
if (AllowDropOrder) {
- TryAddDropTarget(result, item, DropPlace.Before, e);
+ TryAddDropTarget(result, item, DropPlace.Before, e);
}
TryAddDropTarget(result, item, DropPlace.Inside, e);
@@ -310,11 +370,11 @@ List<DropTarget> BuildDropTargets(SharpTreeViewItem item, DragEventArgs e)
if (result.Count == 2) {
if (result[0].Place == DropPlace.Inside &&
- result[1].Place != DropPlace.Inside) {
+ result[1].Place != DropPlace.Inside) {
result[0].Y = y3;
}
else if (result[0].Place != DropPlace.Inside &&
- result[1].Place == DropPlace.Inside) {
+ result[1].Place == DropPlace.Inside) {
result[0].Y = y1;
}
else {
@@ -349,7 +409,7 @@ void TryAddDropTarget(List<DropTarget> targets, SharpTreeViewItem item, DropPlac
};
targets.Add(target);
}
- }
+ }
}
void GetNodeAndIndex(SharpTreeViewItem item, DropPlace place, out SharpTreeNode node, out int index)
@@ -387,7 +447,7 @@ enum DropPlace
void ShowPreview(SharpTreeViewItem item, DropPlace place)
{
previewNodeView = item.NodeView;
- previewPlace = place;
+ previewPlace = place;
if (place == DropPlace.Inside) {
previewNodeView.TextBackground = SystemColors.HighlightBrush;
View
20 SharpTreeView/SharpTreeViewItem.cs
@@ -18,7 +18,7 @@ public class SharpTreeViewItem : ListViewItem
static SharpTreeViewItem()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(SharpTreeViewItem),
- new FrameworkPropertyMetadata(typeof(SharpTreeViewItem)));
+ new FrameworkPropertyMetadata(typeof(SharpTreeViewItem)));
RegisterCommands();
}
@@ -29,7 +29,7 @@ public SharpTreeNode Node
}
public SharpTreeNodeView NodeView { get; internal set; }
- public SharpTreeView ParentTreeView { get; internal set; }
+ public SharpTreeView ParentTreeView { get; internal set; }
protected override void OnKeyDown(KeyEventArgs e)
{
@@ -42,12 +42,6 @@ protected override void OnKeyDown(KeyEventArgs e)
case Key.Escape:
Node.IsEditing = false;
break;
- case Key.Left:
- Node.IsExpanded = false;
- break;
- case Key.Right:
- Node.IsExpanded = true;
- break;
}
}
@@ -89,7 +83,7 @@ protected override void OnMouseMove(MouseEventArgs e)
if (IsMouseCaptured) {
var currentPoint = e.GetPosition(null);
if (Math.Abs(currentPoint.X - startPoint.X) >= SystemParameters.MinimumHorizontalDragDistance ||
- Math.Abs(currentPoint.Y - startPoint.Y) >= SystemParameters.MinimumVerticalDragDistance) {
+ Math.Abs(currentPoint.Y - startPoint.Y) >= SystemParameters.MinimumVerticalDragDistance) {
if (Node.InternalCanDrag()) {
Node.InternalDrag(this);
@@ -147,16 +141,16 @@ protected override void OnDragLeave(DragEventArgs e)
static void RegisterCommands()
{
CommandManager.RegisterClassCommandBinding(typeof(SharpTreeViewItem),
- new CommandBinding(ApplicationCommands.Cut, HandleExecuted_Cut, HandleCanExecute_Cut));
+ new CommandBinding(ApplicationCommands.Cut, HandleExecuted_Cut, HandleCanExecute_Cut));
CommandManager.RegisterClassCommandBinding(typeof(SharpTreeViewItem),
- new CommandBinding(ApplicationCommands.Copy, HandleExecuted_Copy, HandleCanExecute_Copy));
+ new CommandBinding(ApplicationCommands.Copy, HandleExecuted_Copy, HandleCanExecute_Copy));
CommandManager.RegisterClassCommandBinding(typeof(SharpTreeViewItem),
- new CommandBinding(ApplicationCommands.Paste, HandleExecuted_Paste, HandleCanExecute_Paste));
+ new CommandBinding(ApplicationCommands.Paste, HandleExecuted_Paste, HandleCanExecute_Paste));
CommandManager.RegisterClassCommandBinding(typeof(SharpTreeViewItem),
- new CommandBinding(ApplicationCommands.Delete, HandleExecuted_Delete, HandleCanExecute_Delete));
+ new CommandBinding(ApplicationCommands.Delete, HandleExecuted_Delete, HandleCanExecute_Delete));
}
static void HandleExecuted_Cut(object sender, ExecutedRoutedEventArgs e)
Please sign in to comment.
Something went wrong with that request. Please try again.