Browse Source

Make TreeView work again, to an extent.

Still a bit of a hack: needs doing properly at some point.
pull/69/head
Steven Kirk 11 years ago
parent
commit
12c8d3712d
  1. 4
      Perspex.Controls/Generators/IItemContainerGenerator.cs
  2. 36
      Perspex.Controls/Generators/ITreeItemContainerGenerator.cs
  3. 8
      Perspex.Controls/Generators/ItemContainerGenerator.cs
  4. 287
      Perspex.Controls/Generators/TreeItemContainerGenerator.cs
  5. 5
      Perspex.Controls/Perspex.Controls.csproj
  6. 2
      Perspex.Controls/Presenters/DeckPresenter.cs
  7. 2
      Perspex.Controls/Presenters/ItemsPresenter.cs
  8. 30
      Perspex.Controls/Templates/ITreeDataTemplate.cs
  9. 148
      Perspex.Controls/Templates/TreeDataTemplate.cs
  10. 137
      Perspex.Controls/Templates/TreeDataTemplate`1.cs
  11. 138
      Perspex.Controls/TreeDataTemplate.cs
  12. 196
      Perspex.Controls/TreeView.cs

4
Perspex.Controls/Generators/IItemContainerGenerator.cs

@ -41,9 +41,9 @@ namespace Perspex.Controls.Generators
/// <param name="startingIndex">
/// The index of the first item of the data in the containing collection.
/// </param>
/// <param name="count">The number of items to remove.</param>
/// <param name="items">The items.</param>
/// <returns>The removed controls.</returns>
IList<IControl> RemoveContainers(int startingIndex, int count);
IList<IControl> RemoveContainers(int startingIndex, IEnumerable items);
/// <summary>
/// Clears the created containers from the index and returns the removed controls.

36
Perspex.Controls/Generators/ITreeItemContainerGenerator.cs

@ -0,0 +1,36 @@
// -----------------------------------------------------------------------
// <copyright file="ITreeItemContainerGenerator.cs" company="Steven Kirk">
// Copyright 2015 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Controls.Generators
{
using System.Collections.Generic;
/// <summary>
/// Creates containers for tree items and maintains a list of created containers.
/// </summary>
public interface ITreeItemContainerGenerator : IItemContainerGenerator
{
/// <summary>
/// Gets all of the generated container controls.
/// </summary>
/// <returns>The containers.</returns>
IEnumerable<IControl> GetAllContainers();
/// <summary>
/// Gets the item that is contained by the specified container.
/// </summary>
/// <param name="container">The container.</param>
/// <returns>The item.</returns>
object ItemFromContainer(IControl container);
/// <summary>
/// Gets the container for the specified item
/// </summary>
/// <param name="item">The item.</param>
/// <returns>The container.</returns>
IControl ContainerFromItem(object item);
}
}

8
Perspex.Controls/Generators/ItemContainerGenerator.cs

@ -20,7 +20,7 @@ namespace Perspex.Controls.Generators
{
private Dictionary<int, IControl> containers = new Dictionary<int, IControl>();
private Subject<ItemContainers> containersInitialized;
private Subject<ItemContainers> containersInitialized = new Subject<ItemContainers>();
/// <summary>
/// Initializes a new instance of the <see cref="ItemContainerGenerator"/> class.
@ -29,7 +29,6 @@ namespace Perspex.Controls.Generators
public ItemContainerGenerator(IControl owner)
{
this.Owner = owner;
this.containersInitialized = new Subject<ItemContainers>();
}
/// <summary>
@ -79,11 +78,12 @@ namespace Perspex.Controls.Generators
/// <param name="startingIndex">
/// The index of the first item of the data in the containing collection.
/// </param>
/// <param name="count">The number of items to remove.</param>
/// <param name="items">The items.</param>
/// <returns>The removed controls.</returns>
public IList<IControl> RemoveContainers(int startingIndex, int count)
public IList<IControl> RemoveContainers(int startingIndex, IEnumerable items)
{
var result = new List<IControl>();
var count = items.Cast<object>().Count();
for (int i = startingIndex; i < startingIndex + count; ++i)
{

287
Perspex.Controls/Generators/TreeItemContainerGenerator.cs

@ -1,6 +1,6 @@
// -----------------------------------------------------------------------
// <copyright file="TreeItemContainerGenerator.cs" company="Steven Kirk">
// Copyright 2014 MIT Licence. See licence.md for more information.
// Copyright 2015 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
@ -10,84 +10,223 @@ namespace Perspex.Controls.Generators
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Subjects;
using Perspex.Controls.Templates;
public class TreeItemContainerGenerator<T> : ItemContainerGenerator, IItemContainerGenerator where T : TreeViewItem, new()
/// <summary>
/// Creates containers for tree items and maintains a list of created containers.
/// </summary>
/// <typeparam name="T">The type of the container.</typeparam>
public class TreeItemContainerGenerator<T> : ITreeItemContainerGenerator where T : TreeViewItem, new()
{
public TreeItemContainerGenerator(ItemsControl owner)
: base(owner)
private Dictionary<object, T> containers = new Dictionary<object, T>();
private Subject<ItemContainers> containersInitialized = new Subject<ItemContainers>();
/// <summary>
/// Initializes a new instance of the <see cref="TreeItemContainerGenerator{T}"/> class.
/// </summary>
/// <param name="owner">The owner control.</param>
public TreeItemContainerGenerator(IControl owner)
{
this.Owner = owner;
}
/// <summary>
/// Signalled whenever new containers are initialized.
/// </summary>
public IObservable<ItemContainers> ContainersInitialized => this.containersInitialized;
/// <summary>
/// Gets the owner control.
/// </summary>
public IControl Owner { get; }
/// <summary>
/// Creates container controls for a collection of items.
/// </summary>
/// <param name="startingIndex">
/// The index of the first item of the data in the containing collection.
/// </param>
/// <param name="items">The items.</param>
/// <param name="itemTemplate">An optional item template.</param>
/// <returns>The created container controls.</returns>
public IList<IControl> CreateContainers(int startingIndex, IEnumerable items, IDataTemplate itemTemplate)
{
Contract.Requires<ArgumentNullException>(items != null);
int index = startingIndex;
var result = new List<IControl>();
foreach (var item in items)
{
var container = this.CreateContainer(item, itemTemplate);
this.containers.Add(item, container);
result.Add(container);
}
this.containersInitialized.OnNext(new ItemContainers(startingIndex, result));
return result.Where(x => x != null).ToList();
}
////IEnumerable<Control> IItemContainerGenerator.Remove(IEnumerable items)
////{
//// throw new NotImplementedException();
//// var result = new List<Control>();
//// foreach (var item in items)
//// {
//// var container = (T)this.GetContainerForItem(item);
//// this.Remove(container, result);
//// }
//// return result;
////}
////protected override Control CreateContainerOverride(object item)
////{
//// T result = item as T;
//// if (result == null)
//// {
//// TreeDataTemplate template = this.GetTreeDataTemplate(item);
//// result = new T
//// {
//// Header = template.Build(item),
//// Items = template.ItemsSelector(item),
//// IsExpanded = template.IsExpanded(item),
//// };
//// }
//// return result;
////}
////private TreeDataTemplate GetTreeDataTemplate(object item)
////{
//// IDataTemplate template = this.Owner.FindDataTemplate(item);
//// if (template == null)
//// {
//// template = DataTemplate.Default;
//// }
//// TreeDataTemplate treeTemplate = template as TreeDataTemplate;
//// if (treeTemplate == null)
//// {
//// treeTemplate = new TreeDataTemplate(template.Build, x => null);
//// }
//// return treeTemplate;
////}
////private void Remove(T container, List<Control> removed)
////{
//// if (container.Items != null)
//// {
//// foreach (var childItem in container.Items)
//// {
//// var childContainer = (T)this.GetContainerForItem(childItem);
//// if (childContainer != null)
//// {
//// this.Remove(childContainer, removed);
//// }
//// }
//// }
//// this.RemoveByContainerInternal(container);
//// removed.Add(container);
////}
/// <summary>
/// Removes a set of created containers from the index and returns the removed controls.
/// </summary>
/// <param name="startingIndex">
/// The index of the first item of the data in the containing collection.
/// </param>
/// <param name="items">The items.</param>
/// <returns>The removed controls.</returns>
public IList<IControl> RemoveContainers(int startingIndex, IEnumerable items)
{
var result = new List<IControl>();
foreach (var item in items)
{
T container;
if (this.containers.TryGetValue(item, out container))
{
this.Remove(container, result);
}
}
return result;
}
/// <summary>
/// Clears the created containers from the index and returns the removed controls.
/// </summary>
/// <returns>The removed controls.</returns>
public IList<IControl> ClearContainers()
{
throw new NotImplementedException();
}
/// <summary>
/// Gets the container control representing the item with the specified index.
/// </summary>
/// <param name="index">The index.</param>
/// <returns>The container or null if no container created.</returns>
public IControl ContainerFromIndex(int index)
{
throw new NotImplementedException();
}
/// <summary>
/// Gets the index of the specified container control.
/// </summary>
/// <param name="container">The container.</param>
/// <returns>The index of the container or -1 if not found.</returns>
public int IndexFromContainer(IControl container)
{
throw new NotImplementedException();
}
/// <summary>
/// Gets all of the generated container controls.
/// </summary>
/// <returns>The containers.</returns>
public IEnumerable<IControl> GetAllContainers()
{
return this.containers.Values;
}
/// <summary>
/// Gets the item that is contained by the specified container.
/// </summary>
/// <param name="container">The container.</param>
/// <returns>The item.</returns>
public object ItemFromContainer(IControl container)
{
return container.DataContext;
}
/// <summary>
/// Gets the container for the specified item
/// </summary>
/// <param name="item">The item.</param>
/// <returns>The container.</returns>
public IControl ContainerFromItem(object item)
{
T result;
this.containers.TryGetValue(item, out result);
return result;
}
/// <summary>
/// Creates the container for an item.
/// </summary>
/// <param name="item">The item.</param>
/// <param name="itemTemplate">An optional item template.</param>
/// <returns>The created container control.</returns>
protected virtual T CreateContainer(object item, IDataTemplate itemTemplate)
{
T result = item as T;
if (result == null)
{
var template = this.GetTreeDataTemplate(item);
result = new T
{
Header = template.Build(item),
Items = template.ItemsSelector(item),
IsExpanded = template.IsExpanded(item),
DataContext = item,
};
}
return result;
}
/// <summary>
/// Gets the data template for the specified item.
/// </summary>
/// <param name="item">The item.</param>
/// <returns>The template.</returns>
private ITreeDataTemplate GetTreeDataTemplate(object item)
{
IDataTemplate template = this.Owner.FindDataTemplate(item);
if (template == null)
{
template = DataTemplate.Default;
}
var treeTemplate = template as ITreeDataTemplate;
if (treeTemplate == null)
{
treeTemplate = new TreeDataTemplate(typeof(object), template.Build, x => null);
}
return treeTemplate;
}
private void Remove(T container, IList<IControl> removed)
{
if (container.Items != null)
{
foreach (var childItem in container.Items)
{
T childContainer;
if (this.containers.TryGetValue(childItem, out childContainer))
{
this.Remove(childContainer, removed);
}
}
}
// TODO: Dual index.
var i = this.containers.FirstOrDefault(x => x.Value == container);
if (i.Key != null)
{
this.containers.Remove(i.Key);
}
}
}
}

5
Perspex.Controls/Perspex.Controls.csproj

@ -36,6 +36,7 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Compile Include="Generators\ITreeItemContainerGenerator.cs" />
<Compile Include="Generators\ItemContainers.cs" />
<Compile Include="IControl.cs" />
<Compile Include="ISetLogicalParent.cs" />
@ -115,6 +116,8 @@
<Compile Include="Shapes\Ellipse.cs" />
<Compile Include="Templates\ITemplate`2.cs" />
<Compile Include="Templates\ITemplate`1.cs" />
<Compile Include="Templates\ITreeDataTemplate.cs" />
<Compile Include="Templates\TreeDataTemplate.cs" />
<Compile Include="ToolTip.cs" />
<Compile Include="UserControl.cs" />
<Compile Include="Templates\TemplateExtensions.cs" />
@ -141,7 +144,7 @@
<Compile Include="TextBox.cs" />
<Compile Include="Presenters\TextPresenter.cs" />
<Compile Include="Primitives\ToggleButton.cs" />
<Compile Include="TreeDataTemplate.cs" />
<Compile Include="Templates\TreeDataTemplate`1.cs" />
<Compile Include="TreeView.cs" />
<Compile Include="TreeViewItem.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />

2
Perspex.Controls/Presenters/DeckPresenter.cs

@ -194,7 +194,7 @@ namespace Perspex.Controls.Presenters
if (from != null)
{
this.Panel.Children.Remove(from);
generator.RemoveContainers(fromIndex, 1);
generator.RemoveContainers(fromIndex, new[] { from });
}
}

2
Perspex.Controls/Presenters/ItemsPresenter.cs

@ -210,7 +210,7 @@ namespace Perspex.Controls.Presenters
case NotifyCollectionChangedAction.Remove:
this.Panel.Children.RemoveAll(
generator.RemoveContainers(e.OldStartingIndex, e.OldItems.Count));
generator.RemoveContainers(e.OldStartingIndex, e.OldItems));
break;
}

30
Perspex.Controls/Templates/ITreeDataTemplate.cs

@ -0,0 +1,30 @@
// -----------------------------------------------------------------------
// <copyright file="ITreeDataTemplate.cs" company="Steven Kirk">
// Copyright 2014 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Controls.Templates
{
using System.Collections;
/// <summary>
/// Interface representing a template used to build hierachical data.
/// </summary>
public interface ITreeDataTemplate : IDataTemplate
{
/// <summary>
/// Checks to see if the item should be initially expanded.
/// </summary>
/// <param name="item">The item.</param>
/// <returns>True if the item should be initially expanded, otherwise false.</returns>
bool IsExpanded(object item);
/// <summary>
/// Selects the child items of an item.
/// </summary>
/// <param name="item">The item.</param>
/// <returns>The child items, or null if no child items.</returns>
IEnumerable ItemsSelector(object item);
}
}

148
Perspex.Controls/Templates/TreeDataTemplate.cs

@ -0,0 +1,148 @@
// -----------------------------------------------------------------------
// <copyright file="TreeDataTemplate.cs" company="Steven Kirk">
// Copyright 2014 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Controls.Templates
{
using System;
using System.Collections;
using System.Reflection;
/// <summary>
/// A template used to build hierachical data.
/// </summary>
public class TreeDataTemplate : DataTemplate, ITreeDataTemplate
{
private Func<object, IEnumerable> itemsSelector;
private Func<object, bool> isExpanded;
/// <summary>
/// Initializes a new instance of the <see cref="TreeDataTemplate"/> class.
/// </summary>
/// <param name="type">The type of data which the data template matches.</param>
/// <param name="build">
/// A function which when passed an object of <paramref name="type"/> returns a control.
/// </param>
/// <param name="itemsSelector">
/// A function which when passed an object of <paramref name="type"/> returns the child
/// items.
/// </param>
public TreeDataTemplate(
Type type,
Func<object, IControl> build,
Func<object, IEnumerable> itemsSelector)
: this(o => IsInstance(o, type), build, itemsSelector)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="TreeDataTemplate"/> class.
/// </summary>
/// <param name="type">The type of data which the data template matches.</param>
/// <param name="build">
/// A function which when passed an object of <paramref name="type"/> returns a control.
/// </param>
/// <param name="itemsSelector">
/// A function which when passed an object of <paramref name="type"/> returns the child
/// items.
/// </param>
/// <param name="isExpanded">
/// A function which when passed an object of <paramref name="type"/> returns the the
/// initial expanded state of the node.
/// </param>
public TreeDataTemplate(
Type type,
Func<object, IControl> build,
Func<object, IEnumerable> itemsSelector,
Func<object, bool> isExpanded)
: this(o => IsInstance(o, type), build, itemsSelector, isExpanded)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="TreeDataTemplate"/> class.
/// </summary>
/// <param name="match">
/// A function which determines whether the data template matches the specified data.
/// </param>
/// <param name="build">
/// A function which when passed a matching object returns a control.
/// </param>
/// <param name="itemsSelector">
/// A function which when passed a matching object returns the child items.
/// </param>
public TreeDataTemplate(
Func<object, bool> match,
Func<object, IControl> build,
Func<object, IEnumerable> itemsSelector)
: this(match, build, itemsSelector, _ => false)
{
this.itemsSelector = itemsSelector;
}
/// <summary>
/// Initializes a new instance of the <see cref="TreeDataTemplate"/> class.
/// </summary>
/// <param name="match">
/// A function which determines whether the data template matches the specified data.
/// </param>
/// <param name="build">
/// A function which when passed a matching object returns a control.
/// </param>
/// <param name="itemsSelector">
/// A function which when passed a matching object returns the child items.
/// </param>
/// <param name="isExpanded">
/// A function which when passed a matching object returns the the initial expanded state
/// of the node.
/// </param>
public TreeDataTemplate(
Func<object, bool> match,
Func<object, IControl> build,
Func<object, IEnumerable> itemsSelector,
Func<object, bool> isExpanded)
: base(match, build)
{
this.itemsSelector = itemsSelector;
this.isExpanded = isExpanded;
}
/// <summary>
/// Checks to see if the item should be initially expanded.
/// </summary>
/// <param name="item">The item.</param>
/// <returns>True if the item should be initially expanded, otherwise false.</returns>
public bool IsExpanded(object item)
{
return this?.isExpanded(item) ?? false;
}
/// <summary>
/// Selects the child items of an item.
/// </summary>
/// <param name="item">The item.</param>
/// <returns>The child items, or null if no child items.</returns>
public IEnumerable ItemsSelector(object item)
{
return this?.itemsSelector(item);
}
/// <summary>
/// Determines of an object is of the specified type.
/// </summary>
/// <param name="o">The object.</param>
/// <param name="t">The type.</param>
/// <returns>
/// True if <paramref name="o"/> is of type <paramref name="t"/>, otherwise false.
/// </returns>
private static bool IsInstance(object o, Type t)
{
return (o != null) ?
t.GetTypeInfo().IsAssignableFrom(o.GetType().GetTypeInfo()) :
false;
}
}
}

137
Perspex.Controls/Templates/TreeDataTemplate`1.cs

@ -0,0 +1,137 @@
// -----------------------------------------------------------------------
// <copyright file="TreeDataTemplate`1.cs" company="Steven Kirk">
// Copyright 2014 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Controls.Templates
{
using System;
using System.Collections;
/// <summary>
/// A template used to build hierachical data.
/// </summary>
/// <typeparam name="T">The type of the template's data.</typeparam>
public class TreeDataTemplate<T> : TreeDataTemplate
{
/// <summary>
/// Initializes a new instance of the <see cref="TreeDataTemplate{T}"/> class.
/// </summary>
/// <param name="build">
/// A function which when passed an object of <typeparamref name="T"/> returns a control.
/// </param>
/// <param name="itemsSelector">
/// A function which when passed an object of <typeparamref name="T"/> returns the child
/// items.
/// </param>
public TreeDataTemplate(
Func<T, Control> build,
Func<T, IEnumerable> itemsSelector)
: base(
typeof(T),
TreeDataTemplate<T>.Cast(build),
TreeDataTemplate<T>.Cast(itemsSelector))
{
}
/// <summary>
/// Initializes a new instance of the <see cref="TreeDataTemplate{T}"/> class.
/// </summary>
/// <param name="build">
/// A function which when passed an object of <typeparamref name="T"/> returns a control.
/// </param>
/// <param name="itemsSelector">
/// A function which when passed an object of <typeparamref name="T"/> returns the child
/// items.
/// </param>
/// <param name="isExpanded">
/// A function which when passed an object of <typeparamref name="T"/> returns the the
/// initial expanded state of the node.
/// </param>
public TreeDataTemplate(
Func<T, Control> build,
Func<T, IEnumerable> itemsSelector,
Func<T, bool> isExpanded)
: base(
typeof(T),
TreeDataTemplate<T>.Cast(build),
TreeDataTemplate<T>.Cast(itemsSelector),
TreeDataTemplate<T>.Cast(isExpanded))
{
}
/// <summary>
/// Initializes a new instance of the <see cref="TreeDataTemplate{T}"/> class.
/// </summary>
/// <param name="match">
/// A function which determines whether the data template matches the specified data.
/// </param>
/// <param name="build">
/// A function which when passed a matching object returns a control.
/// </param>
/// <param name="itemsSelector">
/// A function which when passed a matching object returns the child items.
/// </param>
public TreeDataTemplate(
Func<T, bool> match,
Func<T, Control> build,
Func<T, IEnumerable> itemsSelector)
: base(
TreeDataTemplate<T>.CastMatch(match),
TreeDataTemplate<T>.Cast(build),
TreeDataTemplate<T>.Cast(itemsSelector))
{
}
/// <summary>
/// Initializes a new instance of the <see cref="TreeDataTemplate{T}"/> class.
/// </summary>
/// <param name="match">
/// A function which determines whether the data template matches the specified data.
/// </param>
/// <param name="build">
/// A function which when passed a matching object returns a control.
/// </param>
/// <param name="itemsSelector">
/// A function which when passed a matching object returns the child items.
/// </param>
/// <param name="isExpanded">
/// A function which when passed a matching object returns the the initial expanded state
/// of the node.
/// </param>
public TreeDataTemplate(
Func<T, bool> match,
Func<T, Control> build,
Func<T, IEnumerable> itemsSelector,
Func<T, bool> isExpanded)
: base(
TreeDataTemplate<T>.CastMatch(match),
TreeDataTemplate<T>.Cast(build),
TreeDataTemplate<T>.Cast(itemsSelector),
TreeDataTemplate<T>.Cast(isExpanded))
{
}
/// <summary>
/// Casts a typed match function to an untyped match function.
/// </summary>
/// <param name="f">The typed function.</param>
/// <returns>The untyped function.</returns>
private static Func<object, bool> CastMatch(Func<T, bool> f)
{
return o => (o is T) ? f((T)o) : false;
}
/// <summary>
/// Casts a function with a typed parameter to an untyped function.
/// </summary>
/// <typeparam name="TResult">The result.</typeparam>
/// <param name="f">The typed function.</param>
/// <returns>The untyped function.</returns>
private static Func<object, TResult> Cast<TResult>(Func<T, TResult> f)
{
return o => f((T)o);
}
}
}

138
Perspex.Controls/TreeDataTemplate.cs

@ -1,138 +0,0 @@
// -----------------------------------------------------------------------
// <copyright file="TreeDataTemplate.cs" company="Steven Kirk">
// Copyright 2014 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Controls
{
using System;
using System.Collections;
using System.Reflection;
using Perspex.Controls.Templates;
public class TreeDataTemplate : DataTemplate
{
public TreeDataTemplate(
Func<object, IControl> build,
Func<object, IEnumerable> itemsSelector)
: this(o => true, build, itemsSelector)
{
}
public TreeDataTemplate(
Type type,
Func<object, IControl> build,
Func<object, IEnumerable> itemsSelector)
: this(o => IsInstance(o, type), build, itemsSelector)
{
}
public TreeDataTemplate(
Type type,
Func<object, IControl> build,
Func<object, IEnumerable> itemsSelector,
Func<object, bool> isExpanded)
: this(o => IsInstance(o, type), build, itemsSelector, isExpanded)
{
}
public TreeDataTemplate(
Func<object, bool> match,
Func<object, IControl> build,
Func<object, IEnumerable> itemsSelector)
: this(match, build, itemsSelector, _ => false)
{
this.ItemsSelector = itemsSelector;
}
public TreeDataTemplate(
Func<object, bool> match,
Func<object, IControl> build,
Func<object, IEnumerable> itemsSelector,
Func<object, bool> isExpanded)
: base(match, build)
{
this.ItemsSelector = itemsSelector;
this.IsExpanded = isExpanded;
}
public Func<object, IEnumerable> ItemsSelector { get; private set; }
public Func<object, bool> IsExpanded { get; private set; }
/// <summary>
/// Determines of an object is of the specified type.
/// </summary>
/// <param name="o">The object.</param>
/// <param name="t">The type.</param>
/// <returns>
/// True if <paramref name="o"/> is of type <paramref name="t"/>, otherwise false.
/// </returns>
private static bool IsInstance(object o, Type t)
{
return (o != null) ?
t.GetTypeInfo().IsAssignableFrom(o.GetType().GetTypeInfo()) :
false;
}
}
public class TreeDataTemplate<T> : TreeDataTemplate
{
public TreeDataTemplate(
Func<T, Control> build,
Func<T, IEnumerable> itemsSelector)
: base(
typeof(T),
TreeDataTemplate<T>.Cast(build),
TreeDataTemplate<T>.Cast(itemsSelector))
{
}
public TreeDataTemplate(
Func<T, Control> build,
Func<T, IEnumerable> itemsSelector,
Func<T, bool> isExpanded)
: base(
typeof(T),
TreeDataTemplate<T>.Cast(build),
TreeDataTemplate<T>.Cast(itemsSelector),
TreeDataTemplate<T>.Cast(isExpanded))
{
}
public TreeDataTemplate(
Func<T, bool> match,
Func<T, Control> build,
Func<T, IEnumerable> itemsSelector)
: base(
TreeDataTemplate<T>.CastMatch(match),
TreeDataTemplate<T>.Cast(build),
TreeDataTemplate<T>.Cast(itemsSelector))
{
}
public TreeDataTemplate(
Func<T, bool> match,
Func<T, Control> build,
Func<T, IEnumerable> itemsSelector,
Func<T, bool> isExpanded)
: base(
TreeDataTemplate<T>.CastMatch(match),
TreeDataTemplate<T>.Cast(build),
TreeDataTemplate<T>.Cast(itemsSelector),
TreeDataTemplate<T>.Cast(isExpanded))
{
}
private static Func<object, bool> CastMatch(Func<T, bool> f)
{
return o => (o is T) ? f((T)o) : false;
}
private static Func<object, TResult> Cast<TResult>(Func<T, TResult> f)
{
return o => f((T)o);
}
}
}

196
Perspex.Controls/TreeView.cs

@ -17,7 +17,7 @@ namespace Perspex.Controls
public class TreeView : ItemsControl
{
public static readonly PerspexProperty<object> SelectedItemProperty =
PerspexProperty.Register<TreeView, object>("SelectedItem", validate: ValidateSelectedItem);
PerspexProperty.Register<TreeView, object>("SelectedItem");
static TreeView()
{
@ -32,6 +32,11 @@ namespace Perspex.Controls
});
}
public new ITreeItemContainerGenerator ItemContainerGenerator
{
get { return (ITreeItemContainerGenerator)base.ItemContainerGenerator; }
}
public object SelectedItem
{
get { return this.GetValue(SelectedItemProperty); }
@ -43,178 +48,45 @@ namespace Perspex.Controls
return new TreeItemContainerGenerator<TreeViewItem>(this);
}
protected virtual void MoveSelection(FocusNavigationDirection direction)
{
// TODO: Up and down movement is a *HACK* and probably pretty slow. Probably needs
// rewriting at some point.
////if (this.SelectedItem != null)
////{
//// switch (direction)
//// {
//// case FocusNavigationDirection.Up:
//// {
//// var list = this.Flatten();
//// var index = list.IndexOf(this.SelectedItem);
//// if (index > 0)
//// {
//// this.SelectedItem = list[index - 1];
//// }
//// break;
//// }
//// case FocusNavigationDirection.Down:
//// {
//// var list = this.Flatten();
//// var index = list.IndexOf(this.SelectedItem);
//// if (index + 1 < list.Count)
//// {
//// this.SelectedItem = list[index + 1];
//// }
//// break;
//// }
//// case FocusNavigationDirection.Left:
//// {
//// var node = (TreeViewItem)this.ItemContainerGenerator.GetContainerForItem(this.SelectedItem);
//// node.IsExpanded = false;
//// break;
//// }
//// case FocusNavigationDirection.Right:
//// {
//// var node = (TreeViewItem)this.ItemContainerGenerator.GetContainerForItem(this.SelectedItem);
//// node.IsExpanded = true;
//// break;
//// }
//// }
////}
}
protected override void OnKeyDown(KeyEventArgs e)
{
////base.OnKeyDown(e);
////if (!e.Handled)
////{
//// switch (e.Key)
//// {
//// case Key.Up:
//// this.MoveSelection(FocusNavigationDirection.Up);
//// break;
//// case Key.Down:
//// this.MoveSelection(FocusNavigationDirection.Down);
//// break;
//// case Key.Left:
//// this.MoveSelection(FocusNavigationDirection.Left);
//// break;
//// case Key.Right:
//// this.MoveSelection(FocusNavigationDirection.Right);
//// break;
//// default:
//// return;
//// }
//// var selected = this.SelectedItem;
//// if (selected != null)
//// {
//// var container = this.ItemContainerGenerator.GetContainerForItem(selected);
//// if (container != null)
//// {
//// container.BringIntoView();
//// FocusManager.Instance.Focus(container, true);
//// }
//// }
//// e.Handled = true;
////}
}
protected override void OnPointerPressed(PointerPressEventArgs e)
{
////IVisual source = (IVisual)e.Source;
////var selectable = source.GetVisualAncestors()
//// .OfType<ISelectable>()
//// .OfType<Control>()
//// .FirstOrDefault();
////if (selectable != null)
////{
//// var item = this.ItemContainerGenerator.GetItemForContainer(selectable);
//// if (item != null)
//// {
//// this.SelectedItem = item;
//// selectable.BringIntoView();
//// FocusManager.Instance.Focus(selectable);
//// }
////}
////e.Handled = true;
}
private static object ValidateSelectedItem(PerspexObject o, object value)
{
//var control = o as SelectingItemsControl;
IVisual source = (IVisual)e.Source;
var selectable = source.GetVisualAncestors()
.OfType<ISelectable>()
.OfType<Control>()
.FirstOrDefault();
//if (control != null)
//{
// if (value != null && (control.Items == null || control.Items.IndexOf(value) == -1))
// {
// return null;
// }
//}
return value;
}
private List<object> Flatten()
{
var result = new List<object>();
this.Flatten(this.Items, result);
return result;
}
if (selectable != null)
{
var item = this.ItemContainerGenerator.ItemFromContainer(selectable);
private void Flatten(IEnumerable items, List<object> result)
{
////if (items != null)
////{
//// foreach (object item in items)
//// {
//// var container = (TreeViewItem)this.ItemContainerGenerator.GetContainerForItem(item);
//// result.Add(item);
if (item != null)
{
this.SelectedItem = item;
selectable.BringIntoView();
FocusManager.Instance.Focus(selectable);
}
}
//// if (container.IsExpanded)
//// {
//// this.Flatten(container.Items, result);
//// }
//// }
////}
e.Handled = true;
}
private void SelectedItemChanged(object selected)
{
////var containers = this.ItemContainerGenerator.GetAll()
//// .Select(x => x.Item2)
//// .OfType<ISelectable>();
////var selectedContainer = (selected != null) ?
//// this.ItemContainerGenerator.GetContainerForItem(selected) :
//// null;
var containers = this.ItemContainerGenerator.GetAllContainers().OfType<ISelectable>();
var selectedContainer = (selected != null) ?
this.ItemContainerGenerator.ContainerFromItem(selected) :
null;
////if (this.Presenter != null && this.Presenter.Panel != null)
////{
//// KeyboardNavigation.SetTabOnceActiveElement(this.Presenter.Panel, selectedContainer);
////}
if (this.Presenter != null && this.Presenter.Panel != null)
{
KeyboardNavigation.SetTabOnceActiveElement(this.Presenter.Panel, selectedContainer);
}
////foreach (var item in containers)
////{
//// item.IsSelected = item == selectedContainer;
////}
foreach (var item in containers)
{
item.IsSelected = item == selectedContainer;
}
}
}
}

Loading…
Cancel
Save