Browse Source

SharpTreeView: improve left/right keyboard navigation

pull/1/head
Daniel Grunwald 15 years ago
parent
commit
ba6fc13648
  1. 102
      SharpTreeView/SharpTreeView.cs
  2. 20
      SharpTreeView/SharpTreeViewItem.cs

102
SharpTreeView/SharpTreeView.cs

@ -6,12 +6,14 @@ using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Windows.Controls;
using System.Windows; using System.Windows;
using System.Windows.Media; using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data; using System.Windows.Data;
using System.Windows.Documents; using System.Windows.Documents;
using System.Windows.Input; using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Threading;
namespace ICSharpCode.TreeView namespace ICSharpCode.TreeView
{ {
@ -20,19 +22,19 @@ namespace ICSharpCode.TreeView
static SharpTreeView() static SharpTreeView()
{ {
DefaultStyleKeyProperty.OverrideMetadata(typeof(SharpTreeView), DefaultStyleKeyProperty.OverrideMetadata(typeof(SharpTreeView),
new FrameworkPropertyMetadata(typeof(SharpTreeView))); new FrameworkPropertyMetadata(typeof(SharpTreeView)));
SelectionModeProperty.OverrideMetadata(typeof(SharpTreeView), SelectionModeProperty.OverrideMetadata(typeof(SharpTreeView),
new FrameworkPropertyMetadata(SelectionMode.Extended)); new FrameworkPropertyMetadata(SelectionMode.Extended));
AlternationCountProperty.OverrideMetadata(typeof(SharpTreeView), AlternationCountProperty.OverrideMetadata(typeof(SharpTreeView),
new FrameworkPropertyMetadata(2)); new FrameworkPropertyMetadata(2));
DefaultItemContainerStyleKey = DefaultItemContainerStyleKey =
new ComponentResourceKey(typeof(SharpTreeView), "DefaultItemContainerStyleKey"); new ComponentResourceKey(typeof(SharpTreeView), "DefaultItemContainerStyleKey");
VirtualizingStackPanel.VirtualizationModeProperty.OverrideMetadata(typeof(SharpTreeView), VirtualizingStackPanel.VirtualizationModeProperty.OverrideMetadata(typeof(SharpTreeView),
new FrameworkPropertyMetadata(VirtualizationMode.Recycling)); new FrameworkPropertyMetadata(VirtualizationMode.Recycling));
} }
public static ResourceKey DefaultItemContainerStyleKey { get; private set; } public static ResourceKey DefaultItemContainerStyleKey { get; private set; }
@ -53,7 +55,7 @@ namespace ICSharpCode.TreeView
public static readonly DependencyProperty ShowRootProperty = public static readonly DependencyProperty ShowRootProperty =
DependencyProperty.Register("ShowRoot", typeof(bool), typeof(SharpTreeView), DependencyProperty.Register("ShowRoot", typeof(bool), typeof(SharpTreeView),
new FrameworkPropertyMetadata(true)); new FrameworkPropertyMetadata(true));
public bool ShowRoot public bool ShowRoot
{ {
@ -63,7 +65,7 @@ namespace ICSharpCode.TreeView
public static readonly DependencyProperty ShowRootExpanderProperty = public static readonly DependencyProperty ShowRootExpanderProperty =
DependencyProperty.Register("ShowRootExpander", typeof(bool), typeof(SharpTreeView), DependencyProperty.Register("ShowRootExpander", typeof(bool), typeof(SharpTreeView),
new FrameworkPropertyMetadata(false)); new FrameworkPropertyMetadata(false));
public bool ShowRootExpander public bool ShowRootExpander
{ {
@ -82,7 +84,7 @@ namespace ICSharpCode.TreeView
public static readonly DependencyProperty ShowLinesProperty = public static readonly DependencyProperty ShowLinesProperty =
DependencyProperty.Register("ShowLines", typeof(bool), typeof(SharpTreeView), DependencyProperty.Register("ShowLines", typeof(bool), typeof(SharpTreeView),
new FrameworkPropertyMetadata(true)); new FrameworkPropertyMetadata(true));
public bool ShowLines public bool ShowLines
{ {
@ -102,14 +104,14 @@ namespace ICSharpCode.TreeView
public static readonly DependencyProperty ShowAlternationProperty = public static readonly DependencyProperty ShowAlternationProperty =
DependencyProperty.RegisterAttached("ShowAlternation", typeof(bool), typeof(SharpTreeView), DependencyProperty.RegisterAttached("ShowAlternation", typeof(bool), typeof(SharpTreeView),
new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.Inherits)); new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.Inherits));
protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e) protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
{ {
base.OnPropertyChanged(e); base.OnPropertyChanged(e);
if (e.Property == RootProperty || if (e.Property == RootProperty ||
e.Property == ShowRootProperty || e.Property == ShowRootProperty ||
e.Property == ShowRootExpanderProperty) { e.Property == ShowRootExpanderProperty) {
Reload(); Reload();
} }
} }
@ -157,6 +159,64 @@ namespace ICSharpCode.TreeView
SetSelectedItems(list); 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 #region Track selection
@ -265,7 +325,7 @@ namespace ICSharpCode.TreeView
{ {
public SharpTreeViewItem Item; public SharpTreeViewItem Item;
public DropPlace Place; public DropPlace Place;
public double Y; public double Y;
public SharpTreeNode Node; public SharpTreeNode Node;
public int Index; public int Index;
} }
@ -288,7 +348,7 @@ namespace ICSharpCode.TreeView
var node = item.Node; var node = item.Node;
if (AllowDropOrder) { if (AllowDropOrder) {
TryAddDropTarget(result, item, DropPlace.Before, e); TryAddDropTarget(result, item, DropPlace.Before, e);
} }
TryAddDropTarget(result, item, DropPlace.Inside, e); TryAddDropTarget(result, item, DropPlace.Inside, e);
@ -310,11 +370,11 @@ namespace ICSharpCode.TreeView
if (result.Count == 2) { if (result.Count == 2) {
if (result[0].Place == DropPlace.Inside && if (result[0].Place == DropPlace.Inside &&
result[1].Place != DropPlace.Inside) { result[1].Place != DropPlace.Inside) {
result[0].Y = y3; result[0].Y = y3;
} }
else if (result[0].Place != DropPlace.Inside && else if (result[0].Place != DropPlace.Inside &&
result[1].Place == DropPlace.Inside) { result[1].Place == DropPlace.Inside) {
result[0].Y = y1; result[0].Y = y1;
} }
else { else {
@ -349,7 +409,7 @@ namespace ICSharpCode.TreeView
}; };
targets.Add(target); targets.Add(target);
} }
} }
} }
void GetNodeAndIndex(SharpTreeViewItem item, DropPlace place, out SharpTreeNode node, out int index) void GetNodeAndIndex(SharpTreeViewItem item, DropPlace place, out SharpTreeNode node, out int index)
@ -387,7 +447,7 @@ namespace ICSharpCode.TreeView
void ShowPreview(SharpTreeViewItem item, DropPlace place) void ShowPreview(SharpTreeViewItem item, DropPlace place)
{ {
previewNodeView = item.NodeView; previewNodeView = item.NodeView;
previewPlace = place; previewPlace = place;
if (place == DropPlace.Inside) { if (place == DropPlace.Inside) {
previewNodeView.TextBackground = SystemColors.HighlightBrush; previewNodeView.TextBackground = SystemColors.HighlightBrush;

20
SharpTreeView/SharpTreeViewItem.cs

@ -18,7 +18,7 @@ namespace ICSharpCode.TreeView
static SharpTreeViewItem() static SharpTreeViewItem()
{ {
DefaultStyleKeyProperty.OverrideMetadata(typeof(SharpTreeViewItem), DefaultStyleKeyProperty.OverrideMetadata(typeof(SharpTreeViewItem),
new FrameworkPropertyMetadata(typeof(SharpTreeViewItem))); new FrameworkPropertyMetadata(typeof(SharpTreeViewItem)));
RegisterCommands(); RegisterCommands();
} }
@ -29,7 +29,7 @@ namespace ICSharpCode.TreeView
} }
public SharpTreeNodeView NodeView { get; internal set; } public SharpTreeNodeView NodeView { get; internal set; }
public SharpTreeView ParentTreeView { get; internal set; } public SharpTreeView ParentTreeView { get; internal set; }
protected override void OnKeyDown(KeyEventArgs e) protected override void OnKeyDown(KeyEventArgs e)
{ {
@ -42,12 +42,6 @@ namespace ICSharpCode.TreeView
case Key.Escape: case Key.Escape:
Node.IsEditing = false; Node.IsEditing = false;
break; break;
case Key.Left:
Node.IsExpanded = false;
break;
case Key.Right:
Node.IsExpanded = true;
break;
} }
} }
@ -89,7 +83,7 @@ namespace ICSharpCode.TreeView
if (IsMouseCaptured) { if (IsMouseCaptured) {
var currentPoint = e.GetPosition(null); var currentPoint = e.GetPosition(null);
if (Math.Abs(currentPoint.X - startPoint.X) >= SystemParameters.MinimumHorizontalDragDistance || 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()) { if (Node.InternalCanDrag()) {
Node.InternalDrag(this); Node.InternalDrag(this);
@ -147,16 +141,16 @@ namespace ICSharpCode.TreeView
static void RegisterCommands() static void RegisterCommands()
{ {
CommandManager.RegisterClassCommandBinding(typeof(SharpTreeViewItem), CommandManager.RegisterClassCommandBinding(typeof(SharpTreeViewItem),
new CommandBinding(ApplicationCommands.Cut, HandleExecuted_Cut, HandleCanExecute_Cut)); new CommandBinding(ApplicationCommands.Cut, HandleExecuted_Cut, HandleCanExecute_Cut));
CommandManager.RegisterClassCommandBinding(typeof(SharpTreeViewItem), CommandManager.RegisterClassCommandBinding(typeof(SharpTreeViewItem),
new CommandBinding(ApplicationCommands.Copy, HandleExecuted_Copy, HandleCanExecute_Copy)); new CommandBinding(ApplicationCommands.Copy, HandleExecuted_Copy, HandleCanExecute_Copy));
CommandManager.RegisterClassCommandBinding(typeof(SharpTreeViewItem), CommandManager.RegisterClassCommandBinding(typeof(SharpTreeViewItem),
new CommandBinding(ApplicationCommands.Paste, HandleExecuted_Paste, HandleCanExecute_Paste)); new CommandBinding(ApplicationCommands.Paste, HandleExecuted_Paste, HandleCanExecute_Paste));
CommandManager.RegisterClassCommandBinding(typeof(SharpTreeViewItem), 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) static void HandleExecuted_Cut(object sender, ExecutedRoutedEventArgs e)

Loading…
Cancel
Save