Browse Source

Tidied up tree item container generators.

In doing so fixed a bug in DevTools where tree items would become
unselectable.
pull/386/head
Steven Kirk 11 years ago
parent
commit
78abef1551
  1. 19
      src/Perspex.Controls/Generators/ITreeItemContainerGenerator.cs
  2. 87
      src/Perspex.Controls/Generators/TreeContainerIndex.cs
  3. 106
      src/Perspex.Controls/Generators/TreeItemContainerGenerator.cs
  4. 1
      src/Perspex.Controls/Perspex.Controls.csproj
  5. 8
      src/Perspex.Controls/TreeView.cs
  6. 2
      src/Perspex.Controls/TreeViewItem.cs
  7. 5
      tests/Perspex.Controls.UnitTests/TreeViewTests.cs

19
src/Perspex.Controls/Generators/ITreeItemContainerGenerator.cs

@ -11,23 +11,8 @@ namespace Perspex.Controls.Generators
public interface ITreeItemContainerGenerator : IItemContainerGenerator
{
/// <summary>
/// Gets the item container for the root of the tree, or null if this generator is itself
/// the root of the tree.
/// Gets the container index for the tree.
/// </summary>
ITreeItemContainerGenerator RootGenerator { get; }
/// <summary>
/// Gets the item container for the specified item, anywhere in the tree.
/// </summary>
/// <param name="item">The item.</param>
/// <returns>The container, or null if not found.</returns>
IControl TreeContainerFromItem(object item);
/// <summary>
/// Gets the item for the specified item container, anywhere in the tree.
/// </summary>
/// <param name="container">The container.</param>
/// <returns>The item, or null if not found.</returns>
object TreeItemFromContainer(IControl container);
TreeContainerIndex Index { get; }
}
}

87
src/Perspex.Controls/Generators/TreeContainerIndex.cs

@ -0,0 +1,87 @@
// Copyright (c) The Perspex Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System.Collections.Generic;
namespace Perspex.Controls.Generators
{
/// <summary>
/// Maintains an index of all item containers currently materialized by a <see cref="TreeView"/>.
/// </summary>
/// <remarks>
/// Each <see cref="TreeViewItem"/> has its own <see cref="TreeItemContainerGenerator{T}"/>
/// that maintains the list of its direct children, but they also share an instance of this
/// class in their <see cref="TreeItemContainerGenerator{T}.Index"/> property which tracks
/// the containers materialized for the entire tree.
/// </remarks>
public class TreeContainerIndex
{
private readonly Dictionary<object, IControl> _itemToContainer = new Dictionary<object, IControl>();
private readonly Dictionary<IControl, object> _containerToItem = new Dictionary<IControl, object>();
/// <summary>
/// Gets the currently materialized containers.
/// </summary>
public IEnumerable<IControl> Items => _containerToItem.Keys;
/// <summary>
/// Adds an entry to the index.
/// </summary>
/// <param name="item">The item.</param>
/// <param name="container">The item container.</param>
public void Add(object item, IControl container)
{
_itemToContainer.Add(item, container);
_containerToItem.Add(container, item);
}
/// <summary>
/// Removes a container from the index.
/// </summary>
/// <param name="container">The item container.</param>
public void Remove(IControl container)
{
var item = _containerToItem[container];
_containerToItem.Remove(container);
_itemToContainer.Remove(item);
}
/// <summary>
/// Removes a set of containers from the index.
/// </summary>
/// <param name="containers">The item containers.</param>
public void Remove(IEnumerable<ItemContainer> containers)
{
foreach (var container in containers)
{
var item = _containerToItem[container.ContainerControl];
_containerToItem.Remove(container.ContainerControl);
_itemToContainer.Remove(item);
}
}
/// <summary>
/// Gets the container for an item.
/// </summary>
/// <param name="item">The item.</param>
/// <returns>The container, or null of not found.</returns>
public IControl ContainerFromItem(object item)
{
IControl result;
_itemToContainer.TryGetValue(item, out result);
return result;
}
/// <summary>
/// Gets the item for a container.
/// </summary>
/// <param name="container">The container.</param>
/// <returns>The item, or null of not found.</returns>
public object ItemFromContainer(IControl container)
{
object result;
_containerToItem.TryGetValue(container, out result);
return result;
}
}
}

106
src/Perspex.Controls/Generators/TreeItemContainerGenerator.cs

@ -1,6 +1,7 @@
// Copyright (c) The Perspex Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.Collections;
using System.Collections.Generic;
using Perspex.Controls.Templates;
@ -14,9 +15,6 @@ namespace Perspex.Controls.Generators
public class TreeItemContainerGenerator<T> : ItemContainerGenerator<T>, ITreeItemContainerGenerator
where T : class, IControl, new()
{
private readonly Dictionary<object, T> _itemToContainer;
private readonly Dictionary<IControl, object> _containerToItem;
/// <summary>
/// Initializes a new instance of the <see cref="TreeItemContainerGenerator{T}"/> class.
/// </summary>
@ -24,34 +22,30 @@ namespace Perspex.Controls.Generators
/// <param name="contentProperty">The container's Content property.</param>
/// <param name="itemsProperty">The container's Items property.</param>
/// <param name="isExpandedProperty">The container's IsExpanded property.</param>
/// <param name="rootGenerator">
/// The item container for the root of the tree, or null if this generator is itself the
/// root of the tree.
/// </param>
/// <param name="index">The container index for the tree</param>
public TreeItemContainerGenerator(
IControl owner,
PerspexProperty contentProperty,
PerspexProperty itemsProperty,
PerspexProperty isExpandedProperty,
ITreeItemContainerGenerator rootGenerator)
TreeContainerIndex index)
: base(owner, contentProperty)
{
Contract.Requires<ArgumentNullException>(owner != null);
Contract.Requires<ArgumentNullException>(contentProperty != null);
Contract.Requires<ArgumentNullException>(itemsProperty != null);
Contract.Requires<ArgumentNullException>(isExpandedProperty != null);
Contract.Requires<ArgumentNullException>(index != null);
ItemsProperty = itemsProperty;
IsExpandedProperty = isExpandedProperty;
RootGenerator = rootGenerator;
if (rootGenerator == null)
{
_itemToContainer = new Dictionary<object, T>();
_containerToItem = new Dictionary<IControl, object>();
}
Index = index;
}
/// <summary>
/// Gets the item container for the root of the tree, or null if this generator is itself
/// the root of the tree.
/// Gets the container index for the tree.
/// </summary>
public ITreeItemContainerGenerator RootGenerator { get; }
public TreeContainerIndex Index { get; }
/// <summary>
/// Gets the item container's Items property.
@ -63,30 +57,6 @@ namespace Perspex.Controls.Generators
/// </summary>
protected PerspexProperty IsExpandedProperty { get; }
/// <summary>
/// Gets the item container for the specified item, anywhere in the tree.
/// </summary>
/// <param name="item">The item.</param>
/// <returns>The container, or null if not found.</returns>
public IControl TreeContainerFromItem(object item)
{
T result;
_itemToContainer.TryGetValue(item, out result);
return result;
}
/// <summary>
/// Gets the item for the specified item container, anywhere in the tree.
/// </summary>
/// <param name="container">The container.</param>
/// <returns>The item, or null if not found.</returns>
public object TreeItemFromContainer(IControl container)
{
object result;
_containerToItem.TryGetValue(container, out result);
return result;
}
/// <inheritdoc/>
protected override IControl CreateContainer(object item)
{
@ -114,7 +84,7 @@ namespace Perspex.Controls.Generators
result.DataContext = item;
}
AddToIndex(item, result);
Index.Add(item, result);
return result;
}
@ -122,59 +92,17 @@ namespace Perspex.Controls.Generators
public override IEnumerable<ItemContainer> Clear()
{
ClearIndex();
return base.Clear();
var items = base.Clear();
Index.Remove(items);
return items;
}
public override IEnumerable<ItemContainer> Dematerialize(int startingIndex, int count)
{
RemoveFromIndex(GetContainerRange(startingIndex, count));
Index.Remove(GetContainerRange(startingIndex, count));
return base.Dematerialize(startingIndex, count);
}
private void AddToIndex(object item, T container)
{
if (RootGenerator != null)
{
((TreeItemContainerGenerator<T>)RootGenerator).AddToIndex(item, container);
}
else
{
_itemToContainer.Add(item, container);
_containerToItem.Add(container, item);
}
}
private void RemoveFromIndex(IEnumerable<ItemContainer> containers)
{
if (RootGenerator != null)
{
((TreeItemContainerGenerator<T>)RootGenerator).RemoveFromIndex(containers);
}
else
{
foreach (var container in containers)
{
var item = _containerToItem[container.ContainerControl];
_containerToItem.Remove(container.ContainerControl);
_itemToContainer.Remove(item);
}
}
}
private void ClearIndex()
{
if (RootGenerator != null)
{
((TreeItemContainerGenerator<T>)RootGenerator).ClearIndex();
}
else
{
_containerToItem.Clear();
_itemToContainer.Clear();
}
}
/// <summary>
/// Gets the data template for the specified item.
/// </summary>

1
src/Perspex.Controls/Perspex.Controls.csproj

@ -46,6 +46,7 @@
<Compile Include="DockPanel.cs" />
<Compile Include="Expander.cs" />
<Compile Include="Generators\ItemContainer.cs" />
<Compile Include="Generators\TreeContainerIndex.cs" />
<Compile Include="HotkeyManager.cs" />
<Compile Include="INameScope.cs" />
<Compile Include="IPseudoClasses.cs" />

8
src/Perspex.Controls/TreeView.cs

@ -58,7 +58,7 @@ namespace Perspex.Controls
TreeViewItem.HeaderProperty,
TreeViewItem.ItemsProperty,
TreeViewItem.IsExpandedProperty,
null);
new TreeContainerIndex());
}
/// <inheritdoc/>
@ -101,13 +101,13 @@ namespace Perspex.Controls
bool rangeModifier = false,
bool toggleModifier = false)
{
var item = ItemContainerGenerator.TreeItemFromContainer(container);
var item = ItemContainerGenerator.Index.ItemFromContainer(container);
if (item != null)
{
if (SelectedItem != null)
{
var old = ItemContainerGenerator.TreeContainerFromItem(SelectedItem);
var old = ItemContainerGenerator.Index.ContainerFromItem(SelectedItem);
MarkContainerSelected(old, false);
}
@ -162,7 +162,7 @@ namespace Perspex.Controls
if (item != null)
{
if (item.ItemContainerGenerator.RootGenerator == this.ItemContainerGenerator)
if (item.ItemContainerGenerator.Index == this.ItemContainerGenerator.Index)
{
return item;
}

2
src/Perspex.Controls/TreeViewItem.cs

@ -79,7 +79,7 @@ namespace Perspex.Controls
TreeViewItem.HeaderProperty,
TreeViewItem.ItemsProperty,
TreeViewItem.IsExpandedProperty,
_treeView?.ItemContainerGenerator);
_treeView?.ItemContainerGenerator.Index ?? new TreeContainerIndex());
}
/// <inheritdoc/>

5
tests/Perspex.Controls.UnitTests/TreeViewTests.cs

@ -3,6 +3,7 @@
using System.Collections.Generic;
using System.Linq;
using Perspex.Collections;
using Perspex.Controls.Presenters;
using Perspex.Controls.Templates;
using Perspex.Input;
@ -65,7 +66,7 @@ namespace Perspex.Controls.UnitTests
ApplyTemplates(target);
var container = target.ItemContainerGenerator.TreeContainerFromItem(
var container = target.ItemContainerGenerator.Index.ContainerFromItem(
tree[0].Children[1].Children[0]);
Assert.NotNull(container);
@ -92,7 +93,7 @@ namespace Perspex.Controls.UnitTests
ApplyTemplates(target);
var item = tree[0].Children[1].Children[0];
var container = (TreeViewItem)target.ItemContainerGenerator.TreeContainerFromItem(item);
var container = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(item);
Assert.NotNull(container);

Loading…
Cancel
Save