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;
- }
- }
- }
- }
}
}