From 0faeaf47e34079b7c12184d5b6c5adbe5ad1c5a6 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Mon, 25 Jan 2016 14:41:56 +0100 Subject: [PATCH] Scroll newly selected tree view node into view. --- src/Perspex.Base/Threading/DispatcherTimer.cs | 27 +++++++++ src/Perspex.Controls/TreeView.cs | 57 ++++++++++++++++++- src/Perspex.Controls/TreeViewItem.cs | 22 +------ 3 files changed, 84 insertions(+), 22 deletions(-) diff --git a/src/Perspex.Base/Threading/DispatcherTimer.cs b/src/Perspex.Base/Threading/DispatcherTimer.cs index 376ca5c0c9..1ece331c13 100644 --- a/src/Perspex.Base/Threading/DispatcherTimer.cs +++ b/src/Perspex.Base/Threading/DispatcherTimer.cs @@ -146,6 +146,33 @@ namespace Perspex.Threading return Disposable.Create(() => timer.Stop()); } + /// + /// Runs a method once, after the specified interval. + /// + /// + /// The method to call after the interval has elapsed. + /// + /// The interval after which to call the method. + /// The priority to use. + /// An used to cancel the timer. + public static IDisposable RunOnce( + Action action, + TimeSpan interval, + DispatcherPriority priority = DispatcherPriority.Normal) + { + var timer = new DispatcherTimer(priority) { Interval = interval }; + + timer.Tick += (s, e) => + { + action(); + timer.Stop(); + }; + + timer.Start(); + + return Disposable.Create(() => timer.Stop()); + } + /// /// Starts the timer. /// diff --git a/src/Perspex.Controls/TreeView.cs b/src/Perspex.Controls/TreeView.cs index ea2b5d632c..dd36db7ec0 100644 --- a/src/Perspex.Controls/TreeView.cs +++ b/src/Perspex.Controls/TreeView.cs @@ -8,6 +8,7 @@ using Perspex.Controls.Primitives; using Perspex.Input; using Perspex.Interactivity; using Perspex.Styling; +using Perspex.Threading; using Perspex.VisualTree; namespace Perspex.Controls @@ -17,6 +18,14 @@ namespace Perspex.Controls /// public class TreeView : ItemsControl { + /// + /// Defines the property. + /// + public static readonly PerspexProperty AutoScrollToSelectedItemProperty = + PerspexProperty.Register( + nameof(AutoScrollToSelectedItem), + defaultValue: true); + /// /// Defines the property. /// @@ -41,6 +50,15 @@ namespace Perspex.Controls public new ITreeItemContainerGenerator ItemContainerGenerator => (ITreeItemContainerGenerator)base.ItemContainerGenerator; + /// + /// Gets or sets a value indicating whether to automatically scroll to newly selected items. + /// + public bool AutoScrollToSelectedItem + { + get { return GetValue(AutoScrollToSelectedItemProperty); } + set { SetValue(AutoScrollToSelectedItemProperty, value); } + } + /// /// Gets or sets the selected item. /// @@ -65,6 +83,11 @@ namespace Perspex.Controls { var container = ItemContainerGenerator.Index.ContainerFromItem(_selectedItem); MarkContainerSelected(container, true); + + if (AutoScrollToSelectedItem && container != null) + { + container.BringIntoView(); + } } } } @@ -72,12 +95,14 @@ namespace Perspex.Controls /// protected override IItemContainerGenerator CreateItemContainerGenerator() { - return new TreeItemContainerGenerator( + var result = new TreeItemContainerGenerator( this, TreeViewItem.HeaderProperty, TreeViewItem.ItemsProperty, TreeViewItem.IsExpandedProperty, new TreeContainerIndex()); + result.Index.Materialized += ContainerMaterialized; + return result; } /// @@ -190,6 +215,36 @@ namespace Perspex.Controls return null; } + /// + /// Called when a new item container is materialized, to set its selected state. + /// + /// The event sender. + /// The event args. + private void ContainerMaterialized(object sender, ItemContainerEventArgs e) + { + var selectedItem = SelectedItem; + + if (selectedItem != null) + { + foreach (var container in e.Containers) + { + if (container.Item == selectedItem) + { + ((TreeViewItem)container.ContainerControl).IsSelected = true; + + if (AutoScrollToSelectedItem) + { + DispatcherTimer.RunOnce( + container.ContainerControl.BringIntoView, + TimeSpan.Zero); + } + + break; + } + } + } + } + /// /// Sets a container's 'selected' class or . /// diff --git a/src/Perspex.Controls/TreeViewItem.cs b/src/Perspex.Controls/TreeViewItem.cs index e68a77b56e..e2d8107cfc 100644 --- a/src/Perspex.Controls/TreeViewItem.cs +++ b/src/Perspex.Controls/TreeViewItem.cs @@ -73,16 +73,12 @@ namespace Perspex.Controls /// protected override IItemContainerGenerator CreateItemContainerGenerator() { - var result = new TreeItemContainerGenerator( + return new TreeItemContainerGenerator( this, TreeViewItem.HeaderProperty, TreeViewItem.ItemsProperty, TreeViewItem.IsExpandedProperty, _treeView?.ItemContainerGenerator.Index ?? new TreeContainerIndex()); - - result.Materialized += ItemMaterialized; - - return result; } /// @@ -123,21 +119,5 @@ namespace Perspex.Controls base.OnKeyDown(e); } - - private void ItemMaterialized(object sender, ItemContainerEventArgs e) - { - var selectedItem = _treeView?.SelectedItem; - - if (selectedItem != null) - { - foreach (var container in e.Containers) - { - if (container.Item == selectedItem) - { - ((TreeViewItem)container.ContainerControl).IsSelected = true; - } - } - } - } } }