Browse Source

Scroll newly selected tree view node into view.

pull/403/head
Steven Kirk 11 years ago
parent
commit
0faeaf47e3
  1. 27
      src/Perspex.Base/Threading/DispatcherTimer.cs
  2. 57
      src/Perspex.Controls/TreeView.cs
  3. 22
      src/Perspex.Controls/TreeViewItem.cs

27
src/Perspex.Base/Threading/DispatcherTimer.cs

@ -146,6 +146,33 @@ namespace Perspex.Threading
return Disposable.Create(() => timer.Stop()); return Disposable.Create(() => timer.Stop());
} }
/// <summary>
/// Runs a method once, after the specified interval.
/// </summary>
/// <param name="action">
/// The method to call after the interval has elapsed.
/// </param>
/// <param name="interval">The interval after which to call the method.</param>
/// <param name="priority">The priority to use.</param>
/// <returns>An <see cref="IDisposable"/> used to cancel the timer.</returns>
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());
}
/// <summary> /// <summary>
/// Starts the timer. /// Starts the timer.
/// </summary> /// </summary>

57
src/Perspex.Controls/TreeView.cs

@ -8,6 +8,7 @@ using Perspex.Controls.Primitives;
using Perspex.Input; using Perspex.Input;
using Perspex.Interactivity; using Perspex.Interactivity;
using Perspex.Styling; using Perspex.Styling;
using Perspex.Threading;
using Perspex.VisualTree; using Perspex.VisualTree;
namespace Perspex.Controls namespace Perspex.Controls
@ -17,6 +18,14 @@ namespace Perspex.Controls
/// </summary> /// </summary>
public class TreeView : ItemsControl public class TreeView : ItemsControl
{ {
/// <summary>
/// Defines the <see cref="AutoScrollToSelectedItem"/> property.
/// </summary>
public static readonly PerspexProperty<bool> AutoScrollToSelectedItemProperty =
PerspexProperty.Register<TreeView, bool>(
nameof(AutoScrollToSelectedItem),
defaultValue: true);
/// <summary> /// <summary>
/// Defines the <see cref="SelectedItem"/> property. /// Defines the <see cref="SelectedItem"/> property.
/// </summary> /// </summary>
@ -41,6 +50,15 @@ namespace Perspex.Controls
public new ITreeItemContainerGenerator ItemContainerGenerator => public new ITreeItemContainerGenerator ItemContainerGenerator =>
(ITreeItemContainerGenerator)base.ItemContainerGenerator; (ITreeItemContainerGenerator)base.ItemContainerGenerator;
/// <summary>
/// Gets or sets a value indicating whether to automatically scroll to newly selected items.
/// </summary>
public bool AutoScrollToSelectedItem
{
get { return GetValue(AutoScrollToSelectedItemProperty); }
set { SetValue(AutoScrollToSelectedItemProperty, value); }
}
/// <summary> /// <summary>
/// Gets or sets the selected item. /// Gets or sets the selected item.
/// </summary> /// </summary>
@ -65,6 +83,11 @@ namespace Perspex.Controls
{ {
var container = ItemContainerGenerator.Index.ContainerFromItem(_selectedItem); var container = ItemContainerGenerator.Index.ContainerFromItem(_selectedItem);
MarkContainerSelected(container, true); MarkContainerSelected(container, true);
if (AutoScrollToSelectedItem && container != null)
{
container.BringIntoView();
}
} }
} }
} }
@ -72,12 +95,14 @@ namespace Perspex.Controls
/// <inheritdoc/> /// <inheritdoc/>
protected override IItemContainerGenerator CreateItemContainerGenerator() protected override IItemContainerGenerator CreateItemContainerGenerator()
{ {
return new TreeItemContainerGenerator<TreeViewItem>( var result = new TreeItemContainerGenerator<TreeViewItem>(
this, this,
TreeViewItem.HeaderProperty, TreeViewItem.HeaderProperty,
TreeViewItem.ItemsProperty, TreeViewItem.ItemsProperty,
TreeViewItem.IsExpandedProperty, TreeViewItem.IsExpandedProperty,
new TreeContainerIndex()); new TreeContainerIndex());
result.Index.Materialized += ContainerMaterialized;
return result;
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -190,6 +215,36 @@ namespace Perspex.Controls
return null; return null;
} }
/// <summary>
/// Called when a new item container is materialized, to set its selected state.
/// </summary>
/// <param name="sender">The event sender.</param>
/// <param name="e">The event args.</param>
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;
}
}
}
}
/// <summary> /// <summary>
/// Sets a container's 'selected' class or <see cref="ISelectable.IsSelected"/>. /// Sets a container's 'selected' class or <see cref="ISelectable.IsSelected"/>.
/// </summary> /// </summary>

22
src/Perspex.Controls/TreeViewItem.cs

@ -73,16 +73,12 @@ namespace Perspex.Controls
/// <inheritdoc/> /// <inheritdoc/>
protected override IItemContainerGenerator CreateItemContainerGenerator() protected override IItemContainerGenerator CreateItemContainerGenerator()
{ {
var result = new TreeItemContainerGenerator<TreeViewItem>( return new TreeItemContainerGenerator<TreeViewItem>(
this, this,
TreeViewItem.HeaderProperty, TreeViewItem.HeaderProperty,
TreeViewItem.ItemsProperty, TreeViewItem.ItemsProperty,
TreeViewItem.IsExpandedProperty, TreeViewItem.IsExpandedProperty,
_treeView?.ItemContainerGenerator.Index ?? new TreeContainerIndex()); _treeView?.ItemContainerGenerator.Index ?? new TreeContainerIndex());
result.Materialized += ItemMaterialized;
return result;
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -123,21 +119,5 @@ namespace Perspex.Controls
base.OnKeyDown(e); 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;
}
}
}
}
} }
} }

Loading…
Cancel
Save