Browse Source

copy enhanced SharpTreeView from ILSpy

4.1
Siegfried Pammer 14 years ago
parent
commit
2a34197c75
  1. 2
      src/Libraries/SharpTreeView/ICSharpCode.TreeView/ExtensionMethods.cs
  2. 397
      src/Libraries/SharpTreeView/ICSharpCode.TreeView/FlatListTreeNode.cs
  3. 2
      src/Libraries/SharpTreeView/ICSharpCode.TreeView/ICSharpCode.TreeView.csproj
  4. 750
      src/Libraries/SharpTreeView/ICSharpCode.TreeView/SharpTreeNode.cs
  5. 205
      src/Libraries/SharpTreeView/ICSharpCode.TreeView/SharpTreeNodeCollection.cs
  6. 21
      src/Libraries/SharpTreeView/ICSharpCode.TreeView/SharpTreeNodeView.cs
  7. 358
      src/Libraries/SharpTreeView/ICSharpCode.TreeView/SharpTreeView.cs
  8. 112
      src/Libraries/SharpTreeView/ICSharpCode.TreeView/SharpTreeViewItem.cs
  9. 28
      src/Libraries/SharpTreeView/ICSharpCode.TreeView/Themes/Generic.xaml
  10. 221
      src/Libraries/SharpTreeView/ICSharpCode.TreeView/TreeFlattener.cs
  11. 54
      src/Libraries/SharpTreeView/ICSharpCode.TreeView/TreeTraversal.cs

2
src/Libraries/SharpTreeView/ICSharpCode.TreeView/ExtensionMethods.cs

@ -12,7 +12,7 @@ using System.Windows.Input; @@ -12,7 +12,7 @@ using System.Windows.Input;
namespace ICSharpCode.TreeView
{
public static class ExtensionMethods
static class ExtensionMethods
{
public static T FindAncestor<T>(this DependencyObject d) where T : class
{

397
src/Libraries/SharpTreeView/ICSharpCode.TreeView/FlatListTreeNode.cs

@ -0,0 +1,397 @@ @@ -0,0 +1,397 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.Diagnostics;
namespace ICSharpCode.TreeView
{
// This part of SharpTreeNode controls the 'flat list' data structure, which emulates
// a big flat list containing the whole tree; allowing access by visible index.
partial class SharpTreeNode
{
/// <summary>The parent in the flat list</summary>
internal SharpTreeNode listParent;
/// <summary>Left/right nodes in the flat list</summary>
SharpTreeNode left, right;
internal TreeFlattener treeFlattener;
/// <summary>Subtree height in the flat list tree</summary>
byte height = 1;
/// <summary>Length in the flat list, including children (children within the flat list). -1 = invalidated</summary>
int totalListLength = -1;
int Balance {
get { return Height(right) - Height(left); }
}
static int Height(SharpTreeNode node)
{
return node != null ? node.height : 0;
}
internal SharpTreeNode GetListRoot()
{
SharpTreeNode node = this;
while (node.listParent != null)
node = node.listParent;
return node;
}
#region Debugging
[Conditional("DEBUG")]
void CheckRootInvariants()
{
GetListRoot().CheckInvariants();
}
[Conditional("DATACONSISTENCYCHECK")]
void CheckInvariants()
{
Debug.Assert(left == null || left.listParent == this);
Debug.Assert(right == null || right.listParent == this);
Debug.Assert(height == 1 + Math.Max(Height(left), Height(right)));
Debug.Assert(Math.Abs(this.Balance) <= 1);
Debug.Assert(totalListLength == -1 || totalListLength == (left != null ? left.totalListLength : 0) + (isVisible ? 1 : 0) + (right != null ? right.totalListLength : 0));
if (left != null) left.CheckInvariants();
if (right != null) right.CheckInvariants();
}
[Conditional("DEBUG")]
static void DumpTree(SharpTreeNode node)
{
node.GetListRoot().DumpTree();
}
[Conditional("DEBUG")]
void DumpTree()
{
Debug.Indent();
if (left != null)
left.DumpTree();
Debug.Unindent();
Debug.WriteLine("{0}, totalListLength={1}, height={2}, Balance={3}, isVisible={4}", ToString(), totalListLength, height, Balance, isVisible);
Debug.Indent();
if (right != null)
right.DumpTree();
Debug.Unindent();
}
#endregion
#region GetNodeByVisibleIndex / GetVisibleIndexForNode
internal static SharpTreeNode GetNodeByVisibleIndex(SharpTreeNode root, int index)
{
root.GetTotalListLength(); // ensure all list lengths are calculated
Debug.Assert(index >= 0);
Debug.Assert(index < root.totalListLength);
SharpTreeNode node = root;
while (true) {
if (node.left != null && index < node.left.totalListLength) {
node = node.left;
} else {
if (node.left != null) {
index -= node.left.totalListLength;
}
if (node.isVisible) {
if (index == 0)
return node;
index--;
}
node = node.right;
}
}
}
internal static int GetVisibleIndexForNode(SharpTreeNode node)
{
int index = node.left != null ? node.left.GetTotalListLength() : 0;
while (node.listParent != null) {
if (node == node.listParent.right) {
if (node.listParent.left != null)
index += node.listParent.left.GetTotalListLength();
if (node.listParent.isVisible)
index++;
}
node = node.listParent;
}
return index;
}
#endregion
#region Balancing
/// <summary>
/// Balances the subtree rooted in <paramref name="node"/> and recomputes the 'height' field.
/// This method assumes that the children of this node are already balanced and have an up-to-date 'height' value.
/// </summary>
/// <returns>The new root node</returns>
static SharpTreeNode Rebalance(SharpTreeNode node)
{
Debug.Assert(node.left == null || Math.Abs(node.left.Balance) <= 1);
Debug.Assert(node.right == null || Math.Abs(node.right.Balance) <= 1);
// Keep looping until it's balanced. Not sure if this is stricly required; this is based on
// the Rope code where node merging made this necessary.
while (Math.Abs(node.Balance) > 1) {
// AVL balancing
// note: because we don't care about the identity of concat nodes, this works a little different than usual
// tree rotations: in our implementation, the "this" node will stay at the top, only its children are rearranged
if (node.Balance > 1) {
if (node.right.Balance < 0) {
node.right = node.right.RotateRight();
}
node = node.RotateLeft();
// If 'node' was unbalanced by more than 2, we've shifted some of the inbalance to the left node; so rebalance that.
node.left = Rebalance(node.left);
} else if (node.Balance < -1) {
if (node.left.Balance > 0) {
node.left = node.left.RotateLeft();
}
node = node.RotateRight();
// If 'node' was unbalanced by more than 2, we've shifted some of the inbalance to the right node; so rebalance that.
node.right = Rebalance(node.right);
}
}
Debug.Assert(Math.Abs(node.Balance) <= 1);
node.height = (byte)(1 + Math.Max(Height(node.left), Height(node.right)));
node.totalListLength = -1; // mark for recalculation
// since balancing checks the whole tree up to the root, the whole path will get marked as invalid
return node;
}
internal int GetTotalListLength()
{
if (totalListLength >= 0)
return totalListLength;
int length = (isVisible ? 1 : 0);
if (left != null) {
length += left.GetTotalListLength();
}
if (right != null) {
length += right.GetTotalListLength();
}
return totalListLength = length;
}
SharpTreeNode RotateLeft()
{
/* Rotate tree to the left
*
* this right
* / \ / \
* A right ===> this C
* / \ / \
* B C A B
*/
SharpTreeNode b = right.left;
SharpTreeNode newTop = right;
if (b != null) b.listParent = this;
this.right = b;
newTop.left = this;
newTop.listParent = this.listParent;
this.listParent = newTop;
// rebalance the 'this' node - this is necessary in some bulk insertion cases:
newTop.left = Rebalance(this);
return newTop;
}
SharpTreeNode RotateRight()
{
/* Rotate tree to the right
*
* this left
* / \ / \
* left C ===> A this
* / \ / \
* A B B C
*/
SharpTreeNode b = left.right;
SharpTreeNode newTop = left;
if (b != null) b.listParent = this;
this.left = b;
newTop.right = this;
newTop.listParent = this.listParent;
this.listParent = newTop;
newTop.right = Rebalance(this);
return newTop;
}
static void RebalanceUntilRoot(SharpTreeNode pos)
{
while (pos.listParent != null) {
if (pos == pos.listParent.left) {
pos = pos.listParent.left = Rebalance(pos);
} else {
Debug.Assert(pos == pos.listParent.right);
pos = pos.listParent.right = Rebalance(pos);
}
pos = pos.listParent;
}
SharpTreeNode newRoot = Rebalance(pos);
if (newRoot != pos && pos.treeFlattener != null) {
Debug.Assert(newRoot.treeFlattener == null);
newRoot.treeFlattener = pos.treeFlattener;
pos.treeFlattener = null;
newRoot.treeFlattener.root = newRoot;
}
Debug.Assert(newRoot.listParent == null);
newRoot.CheckInvariants();
}
#endregion
#region Insertion
static void InsertNodeAfter(SharpTreeNode pos, SharpTreeNode newNode)
{
// newNode might be the model root of a whole subtree, so go to the list root of that subtree:
newNode = newNode.GetListRoot();
if (pos.right == null) {
pos.right = newNode;
newNode.listParent = pos;
} else {
// insert before pos.right's leftmost:
pos = pos.right;
while (pos.left != null)
pos = pos.left;
Debug.Assert(pos.left == null);
pos.left = newNode;
newNode.listParent = pos;
}
RebalanceUntilRoot(pos);
}
#endregion
#region Removal
void RemoveNodes(SharpTreeNode start, SharpTreeNode end)
{
// Removes all nodes from start to end (inclusive)
// All removed nodes will be reorganized in a separate tree, do not delete
// regions that don't belong together in the tree model!
List<SharpTreeNode> removedSubtrees = new List<SharpTreeNode>();
SharpTreeNode oldPos;
SharpTreeNode pos = start;
do {
// recalculate the endAncestors every time, because the tree might have been rebalanced
HashSet<SharpTreeNode> endAncestors = new HashSet<SharpTreeNode>();
for (SharpTreeNode tmp = end; tmp != null; tmp = tmp.listParent)
endAncestors.Add(tmp);
removedSubtrees.Add(pos);
if (!endAncestors.Contains(pos)) {
// we can remove pos' right subtree in a single step:
if (pos.right != null) {
removedSubtrees.Add(pos.right);
pos.right.listParent = null;
pos.right = null;
}
}
SharpTreeNode succ = pos.Successor();
DeleteNode(pos); // this will also rebalance out the deletion of the right subtree
oldPos = pos;
pos = succ;
} while (oldPos != end);
// merge back together the removed subtrees:
SharpTreeNode removed = removedSubtrees[0];
for (int i = 1; i < removedSubtrees.Count; i++) {
removed = ConcatTrees(removed, removedSubtrees[i]);
}
}
static SharpTreeNode ConcatTrees(SharpTreeNode first, SharpTreeNode second)
{
SharpTreeNode tmp = first;
while (tmp.right != null)
tmp = tmp.right;
InsertNodeAfter(tmp, second);
return tmp.GetListRoot();
}
SharpTreeNode Successor()
{
if (right != null) {
SharpTreeNode node = right;
while (node.left != null)
node = node.left;
return node;
} else {
SharpTreeNode node = this;
SharpTreeNode oldNode;
do {
oldNode = node;
node = node.listParent;
// loop while we are on the way up from the right part
} while (node != null && node.right == oldNode);
return node;
}
}
static void DeleteNode(SharpTreeNode node)
{
SharpTreeNode balancingNode;
if (node.left == null) {
balancingNode = node.listParent;
node.ReplaceWith(node.right);
node.right = null;
} else if (node.right == null) {
balancingNode = node.listParent;
node.ReplaceWith(node.left);
node.left = null;
} else {
SharpTreeNode tmp = node.right;
while (tmp.left != null)
tmp = tmp.left;
// First replace tmp with tmp.right
balancingNode = tmp.listParent;
tmp.ReplaceWith(tmp.right);
tmp.right = null;
Debug.Assert(tmp.left == null);
Debug.Assert(tmp.listParent == null);
// Now move node's children to tmp:
tmp.left = node.left; node.left = null;
tmp.right = node.right; node.right = null;
if (tmp.left != null) tmp.left.listParent = tmp;
if (tmp.right != null) tmp.right.listParent = tmp;
// Then replace node with tmp
node.ReplaceWith(tmp);
if (balancingNode == node)
balancingNode = tmp;
}
Debug.Assert(node.listParent == null);
Debug.Assert(node.left == null);
Debug.Assert(node.right == null);
if (balancingNode != null)
RebalanceUntilRoot(balancingNode);
}
void ReplaceWith(SharpTreeNode node)
{
if (listParent != null) {
if (listParent.left == this) {
listParent.left = node;
} else {
Debug.Assert(listParent.right == this);
listParent.right = node;
}
if (node != null)
node.listParent = listParent;
listParent = null;
} else {
// this was a root node
Debug.Assert(node != null); // cannot delete the only node in the tree
node.listParent = null;
if (treeFlattener != null) {
Debug.Assert(node.treeFlattener == null);
node.treeFlattener = this.treeFlattener;
this.treeFlattener = null;
node.treeFlattener.root = node;
}
}
}
#endregion
}
}

2
src/Libraries/SharpTreeView/ICSharpCode.TreeView/ICSharpCode.TreeView.csproj

@ -67,6 +67,7 @@ @@ -67,6 +67,7 @@
<Compile Include="DropEffect.cs" />
<Compile Include="EditTextBox.cs" />
<Compile Include="ExtensionMethods.cs" />
<Compile Include="FlatListTreeNode.cs" />
<Compile Include="GeneralAdorner.cs" />
<Compile Include="InsertMarker.cs" />
<Compile Include="LinesRenderer.cs" />
@ -81,6 +82,7 @@ @@ -81,6 +82,7 @@
<Compile Include="SharpTreeViewItem.cs" />
<Compile Include="TreeFlattener.cs" />
<AppDesigner Include="Properties\" />
<Compile Include="TreeTraversal.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.

750
src/Libraries/SharpTreeView/ICSharpCode.TreeView/SharpTreeNode.cs

@ -3,6 +3,7 @@ @@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Windows;
@ -14,107 +15,226 @@ using System.Windows.Input; @@ -14,107 +15,226 @@ using System.Windows.Input;
namespace ICSharpCode.TreeView
{
public class SharpTreeNode : INotifyPropertyChanged
public partial class SharpTreeNode : INotifyPropertyChanged
{
#region Main
static SharpTreeNode()
{
SelectedNodes = new List<SharpTreeNode>();
ActiveNodes = new List<SharpTreeNode>();
StartCuttedDataWatcher();
SharpTreeNodeCollection modelChildren;
internal SharpTreeNode modelParent;
bool isVisible = true;
void UpdateIsVisible(bool parentIsVisible, bool updateFlattener)
{
bool newIsVisible = parentIsVisible && !isHidden;
if (isVisible != newIsVisible) {
isVisible = newIsVisible;
// invalidate the augmented data
SharpTreeNode node = this;
while (node != null && node.totalListLength >= 0) {
node.totalListLength = -1;
node = node.listParent;
}
// Remember the removed nodes:
List<SharpTreeNode> removedNodes = null;
if (updateFlattener && !newIsVisible) {
removedNodes = VisibleDescendantsAndSelf().ToList();
}
// also update the model children:
UpdateChildIsVisible(false);
// Validate our invariants:
if (updateFlattener)
CheckRootInvariants();
// Tell the flattener about the removed nodes:
if (removedNodes != null) {
var flattener = GetListRoot().treeFlattener;
if (flattener != null) {
flattener.NodesRemoved(GetVisibleIndexForNode(this), removedNodes);
foreach (var n in removedNodes)
n.OnIsVisibleChanged();
}
}
// Tell the flattener about the new nodes:
if (updateFlattener && newIsVisible) {
var flattener = GetListRoot().treeFlattener;
if (flattener != null) {
flattener.NodesInserted(GetVisibleIndexForNode(this), VisibleDescendantsAndSelf());
foreach (var n in VisibleDescendantsAndSelf())
n.OnIsVisibleChanged();
}
}
}
}
public static List<SharpTreeNode> SelectedNodes { get; private set; }
public static List<SharpTreeNode> ActiveNodes { get; private set; }
static SharpTreeNode[] ActiveNodesArray
protected virtual void OnIsVisibleChanged() {}
void UpdateChildIsVisible(bool updateFlattener)
{
get
{
return ActiveNodes.ToArray();
if (modelChildren != null && modelChildren.Count > 0) {
bool showChildren = isVisible && isExpanded;
foreach (SharpTreeNode child in modelChildren) {
child.UpdateIsVisible(showChildren, updateFlattener);
}
}
}
#region Main
public SharpTreeNode()
{
Children = new SharpTreeNodeCollection(this);
}
public SharpTreeNodeCollection Children { get; private set; }
public SharpTreeNode Parent { get; internal set; }
public SharpTreeNodeCollection Children {
get {
if (modelChildren == null)
modelChildren = new SharpTreeNodeCollection(this);
return modelChildren;
}
}
public SharpTreeNode Parent {
get { return modelParent; }
}
public virtual object Text
{
get { return null; }
}
public virtual object Icon
{
get { return null; }
}
public virtual object ToolTip
{
get { return null; }
}
public int Level
{
get { return Parent != null ? Parent.Level + 1 : 0; }
}
public bool IsRoot
{
get { return Parent == null; }
}
//bool isSelected;
//public bool IsSelected
//{
// get { return isSelected; }
// set
// {
// isSelected = value;
// RaisePropertyChanged("IsSelected");
// }
//}
public virtual ContextMenu GetContextMenu()
bool isHidden;
public bool IsHidden
{
return null;
get { return isHidden; }
set {
if (isHidden != value) {
isHidden = value;
if (modelParent != null)
UpdateIsVisible(modelParent.isVisible && modelParent.isExpanded, true);
RaisePropertyChanged("IsHidden");
if (Parent != null)
Parent.RaisePropertyChanged("ShowExpander");
}
}
}
internal protected void OnChildrenChanged(NotifyCollectionChangedEventArgs e)
{
/// <summary>
/// Return true when this node is not hidden and when all parent nodes are expanded and not hidden.
/// </summary>
public bool IsVisible {
get { return isVisible; }
}
bool isSelected;
public bool IsSelected {
get { return isSelected; }
set {
if (isSelected != value) {
isSelected = value;
RaisePropertyChanged("IsSelected");
}
}
}
#endregion
#region OnChildrenChanged
internal protected virtual void OnChildrenChanged(NotifyCollectionChangedEventArgs e)
{
if (e.OldItems != null) {
foreach (SharpTreeNode node in e.OldItems) {
Debug.Assert(node.modelParent == this);
node.modelParent = null;
Debug.WriteLine("Removing {0} from {1}", node, this);
SharpTreeNode removeEnd = node;
while (removeEnd.modelChildren != null && removeEnd.modelChildren.Count > 0)
removeEnd = removeEnd.modelChildren.Last();
List<SharpTreeNode> removedNodes = null;
int visibleIndexOfRemoval = 0;
if (node.isVisible) {
visibleIndexOfRemoval = GetVisibleIndexForNode(node);
removedNodes = node.VisibleDescendantsAndSelf().ToList();
}
RemoveNodes(node, removeEnd);
if (removedNodes != null) {
var flattener = GetListRoot().treeFlattener;
if (flattener != null) {
flattener.NodesRemoved(visibleIndexOfRemoval, removedNodes);
}
}
}
}
if (e.NewItems != null) {
SharpTreeNode insertionPos;
if (e.NewStartingIndex == 0)
insertionPos = null;
else
insertionPos = modelChildren[e.NewStartingIndex - 1];
foreach (SharpTreeNode node in e.NewItems) {
Debug.Assert(node.modelParent == null);
node.modelParent = this;
node.UpdateIsVisible(isVisible && isExpanded, false);
//Debug.WriteLine("Inserting {0} after {1}", node, insertionPos);
while (insertionPos != null && insertionPos.modelChildren != null && insertionPos.modelChildren.Count > 0) {
insertionPos = insertionPos.modelChildren.Last();
}
InsertNodeAfter(insertionPos ?? this, node);
insertionPos = node;
if (node.isVisible) {
var flattener = GetListRoot().treeFlattener;
if (flattener != null) {
flattener.NodesInserted(GetVisibleIndexForNode(node), node.VisibleDescendantsAndSelf());
}
}
}
}
RaisePropertyChanged("ShowExpander");
RaiseIsLastChangedIfNeeded(e);
}
#endregion
#region Expanding / LazyLoading
public event EventHandler Collapsing;
public virtual object ExpandedIcon
{
get { return Icon; }
}
public virtual bool ShowExpander
{
get { return Children.Count > 0 || LazyLoading; }
get { return LazyLoading || Children.Any(c => !c.isHidden); }
}
//public virtual bool ShowLoading
//{
// get { return false; }
//}
bool isExpanded;
public bool IsExpanded
{
get { return isExpanded; }
@ -124,19 +244,21 @@ namespace ICSharpCode.TreeView @@ -124,19 +244,21 @@ namespace ICSharpCode.TreeView
isExpanded = value;
if (isExpanded) {
EnsureLazyChildren();
OnExpanding();
} else {
OnCollapsing();
}
else {
if (Collapsing != null) {
Collapsing(this, EventArgs.Empty);
}
}
UpdateChildIsVisible(true);
RaisePropertyChanged("IsExpanded");
}
}
}
protected virtual void OnExpanding() {}
protected virtual void OnCollapsing() {}
bool lazyLoading;
public bool LazyLoading
{
get { return lazyLoading; }
@ -145,77 +267,71 @@ namespace ICSharpCode.TreeView @@ -145,77 +267,71 @@ namespace ICSharpCode.TreeView
lazyLoading = value;
if (lazyLoading) {
IsExpanded = false;
if (canExpandRecursively) {
canExpandRecursively = false;
RaisePropertyChanged("CanExpandRecursively");
}
}
RaisePropertyChanged("LazyLoading");
RaisePropertyChanged("ShowExpander");
}
}
bool showIcon;
bool canExpandRecursively = true;
public bool ShowIcon
/// <summary>
/// Gets whether this node can be expanded recursively.
/// If not overridden, this property returns false if the node is using lazy-loading, and true otherwise.
/// </summary>
public virtual bool CanExpandRecursively {
get { return canExpandRecursively; }
}
public virtual bool ShowIcon
{
get { return showIcon; }
set {
showIcon = value;
RaisePropertyChanged("ShowIcon");
}
get { return Icon != null; }
}
public virtual void LoadChildren()
protected virtual void LoadChildren()
{
throw new NotSupportedException(GetType().Name + " does not support lazy loading");
}
/// <summary>
/// Ensures the children were initialized (loads children if lazy loading is enabled)
/// </summary>
public void EnsureLazyChildren()
{
if (LazyLoading) {
LoadChildren();
LazyLoading = false;
LoadChildren();
}
}
#endregion
#region Ancestors / Descendants
public IEnumerable<SharpTreeNode> Descendants()
{
foreach (var child in Children) {
foreach (var child2 in child.DescendantsAndSelf()) {
yield return child2;
}
}
return TreeTraversal.PreOrder(this.Children, n => n.Children);
}
public IEnumerable<SharpTreeNode> DescendantsAndSelf()
{
yield return this;
foreach (var child in Descendants()) {
yield return child;
}
return TreeTraversal.PreOrder(this, n => n.Children);
}
public IEnumerable<SharpTreeNode> ExpandedDescendants()
internal IEnumerable<SharpTreeNode> VisibleDescendants()
{
foreach (var child in Children) {
foreach (var child2 in child.ExpandedDescendantsAndSelf()) {
yield return child2;
}
}
return TreeTraversal.PreOrder(this.Children.Where(c => c.isVisible), n => n.Children.Where(c => c.isVisible));
}
public IEnumerable<SharpTreeNode> ExpandedDescendantsAndSelf()
internal IEnumerable<SharpTreeNode> VisibleDescendantsAndSelf()
{
yield return this;
if (IsExpanded) {
foreach (var child in Children) {
foreach (var child2 in child.ExpandedDescendantsAndSelf()) {
yield return child2;
}
}
}
return TreeTraversal.PreOrder(this, n => n.Children.Where(c => c.isVisible));
}
public IEnumerable<SharpTreeNode> Ancestors()
{
var node = this;
@ -224,7 +340,7 @@ namespace ICSharpCode.TreeView @@ -224,7 +340,7 @@ namespace ICSharpCode.TreeView
node = node.Parent;
}
}
public IEnumerable<SharpTreeNode> AncestorsAndSelf()
{
yield return this;
@ -232,18 +348,18 @@ namespace ICSharpCode.TreeView @@ -232,18 +348,18 @@ namespace ICSharpCode.TreeView
yield return node;
}
}
#endregion
#region Editing
public virtual bool IsEditable
{
get { return false; }
}
bool isEditing;
public bool IsEditing
{
get { return isEditing; }
@ -255,42 +371,39 @@ namespace ICSharpCode.TreeView @@ -255,42 +371,39 @@ namespace ICSharpCode.TreeView
}
}
}
public virtual string LoadEditText()
{
return null;
}
public virtual bool SaveEditText(string value)
{
return true;
}
#endregion
#region Checkboxes
public virtual bool IsCheckable
{
public virtual bool IsCheckable {
get { return false; }
}
bool? isChecked;
public bool? IsChecked
{
public bool? IsChecked {
get { return isChecked; }
set
{
set {
SetIsChecked(value, true);
}
}
void SetIsChecked(bool? value, bool update)
{
if (isChecked != value) {
isChecked = value;
if (update) {
if (IsChecked != null) {
foreach (var child in Descendants()) {
@ -299,7 +412,7 @@ namespace ICSharpCode.TreeView @@ -299,7 +412,7 @@ namespace ICSharpCode.TreeView
}
}
}
foreach (var parent in Ancestors()) {
if (parent.IsCheckable) {
if (!parent.TryValueForIsChecked(true)) {
@ -310,11 +423,11 @@ namespace ICSharpCode.TreeView @@ -310,11 +423,11 @@ namespace ICSharpCode.TreeView
}
}
}
RaisePropertyChanged("IsChecked");
}
}
bool TryValueForIsChecked(bool? value)
{
if (Children.Where(n => n.IsCheckable).All(n => n.IsChecked == value)) {
@ -323,247 +436,188 @@ namespace ICSharpCode.TreeView @@ -323,247 +436,188 @@ namespace ICSharpCode.TreeView
}
return false;
}
#endregion
#region Cut / Copy / Paste / Delete
static List<SharpTreeNode> cuttedNodes = new List<SharpTreeNode>();
static IDataObject cuttedData;
static EventHandler requerySuggestedHandler; // for weak event
static void StartCuttedDataWatcher()
{
requerySuggestedHandler = new EventHandler(CommandManager_RequerySuggested);
CommandManager.RequerySuggested += requerySuggestedHandler;
}
static void CommandManager_RequerySuggested(object sender, EventArgs e)
{
if (cuttedData != null && !Clipboard.IsCurrent(cuttedData)) {
ClearCuttedData();
public bool IsCut { get { return false; } }
/*
static List<SharpTreeNode> cuttedNodes = new List<SharpTreeNode>();
static IDataObject cuttedData;
static EventHandler requerySuggestedHandler; // for weak event
static void StartCuttedDataWatcher()
{
requerySuggestedHandler = new EventHandler(CommandManager_RequerySuggested);
CommandManager.RequerySuggested += requerySuggestedHandler;
}
}
static void ClearCuttedData()
{
foreach (var node in cuttedNodes) {
node.IsCut = false;
static void CommandManager_RequerySuggested(object sender, EventArgs e)
{
if (cuttedData != null && !Clipboard.IsCurrent(cuttedData)) {
ClearCuttedData();
}
}
cuttedNodes.Clear();
cuttedData = null;
}
//static public IEnumerable<SharpTreeNode> PurifyNodes(IEnumerable<SharpTreeNode> nodes)
//{
// var list = nodes.ToList();
// var array = list.ToArray();
// foreach (var node1 in array) {
// foreach (var node2 in array) {
// if (node1.Descendants().Contains(node2)) {
// list.Remove(node2);
// }
// }
// }
// return list;
//}
bool isCut;
public bool IsCut
{
get { return isCut; }
private set
static void ClearCuttedData()
{
isCut = value;
RaisePropertyChanged("IsCut");
foreach (var node in cuttedNodes) {
node.IsCut = false;
}
cuttedNodes.Clear();
cuttedData = null;
}
}
internal bool InternalCanCut()
{
return InternalCanCopy() && InternalCanDelete();
}
internal void InternalCut()
{
ClearCuttedData();
cuttedData = Copy(ActiveNodesArray);
Clipboard.SetDataObject(cuttedData);
foreach (var node in ActiveNodes) {
node.IsCut = true;
cuttedNodes.Add(node);
//static public IEnumerable<SharpTreeNode> PurifyNodes(IEnumerable<SharpTreeNode> nodes)
//{
// var list = nodes.ToList();
// var array = list.ToArray();
// foreach (var node1 in array) {
// foreach (var node2 in array) {
// if (node1.Descendants().Contains(node2)) {
// list.Remove(node2);
// }
// }
// }
// return list;
//}
bool isCut;
public bool IsCut
{
get { return isCut; }
private set
{
isCut = value;
RaisePropertyChanged("IsCut");
}
}
}
internal bool InternalCanCopy()
{
return CanCopy(ActiveNodesArray);
}
internal void InternalCopy()
{
Clipboard.SetDataObject(Copy(ActiveNodesArray));
}
internal bool InternalCanPaste()
{
return CanPaste(Clipboard.GetDataObject());
}
internal void InternalPaste()
{
Paste(Clipboard.GetDataObject());
if (cuttedData != null) {
DeleteCore(cuttedNodes.ToArray());
internal bool InternalCanCut()
{
return InternalCanCopy() && InternalCanDelete();
}
internal void InternalCut()
{
ClearCuttedData();
cuttedData = Copy(ActiveNodesArray);
Clipboard.SetDataObject(cuttedData);
foreach (var node in ActiveNodes) {
node.IsCut = true;
cuttedNodes.Add(node);
}
}
}
internal bool InternalCanDelete()
{
return CanDelete(ActiveNodesArray);
}
internal void InternalDelete()
{
Delete(ActiveNodesArray);
}
public virtual bool CanDelete(SharpTreeNode[] nodes)
internal bool InternalCanCopy()
{
return CanCopy(ActiveNodesArray);
}
internal void InternalCopy()
{
Clipboard.SetDataObject(Copy(ActiveNodesArray));
}
internal bool InternalCanPaste()
{
return CanPaste(Clipboard.GetDataObject());
}
internal void InternalPaste()
{
Paste(Clipboard.GetDataObject());
if (cuttedData != null) {
DeleteCore(cuttedNodes.ToArray());
ClearCuttedData();
}
}
*/
public virtual bool CanDelete()
{
return false;
}
public virtual void Delete(SharpTreeNode[] nodes)
{
}
public virtual void DeleteCore(SharpTreeNode[] nodes)
public virtual void Delete()
{
throw new NotSupportedException(GetType().Name + " does not support deletion");
}
public virtual bool CanCopy(SharpTreeNode[] nodes)
public virtual void DeleteCore()
{
return false;
throw new NotSupportedException(GetType().Name + " does not support deletion");
}
public virtual IDataObject Copy(SharpTreeNode[] nodes)
{
return null;
}
public virtual bool CanPaste(IDataObject data)
{
return false;
}
public virtual void Paste(IDataObject data)
{
EnsureLazyChildren();
Drop(data, Children.Count, DropEffect.Copy);
throw new NotSupportedException(GetType().Name + " does not support copy/paste or drag'n'drop");
}
/*
public virtual bool CanCopy(SharpTreeNode[] nodes)
{
return false;
}
public virtual bool CanPaste(IDataObject data)
{
return false;
}
public virtual void Paste(IDataObject data)
{
EnsureLazyChildren();
Drop(data, Children.Count, DropEffect.Copy);
}
*/
#endregion
#region Drag and Drop
internal bool InternalCanDrag()
public virtual bool CanDrag(SharpTreeNode[] nodes)
{
return CanDrag(ActiveNodesArray);
return false;
}
internal void InternalDrag(DependencyObject dragSource)
{
DragDrop.DoDragDrop(dragSource, Copy(ActiveNodesArray), DragDropEffects.All);
public virtual void StartDrag(DependencyObject dragSource, SharpTreeNode[] nodes)
{
DragDropEffects effects = DragDropEffects.All;
if (!nodes.All(n => n.CanDelete()))
effects &= ~DragDropEffects.Move;
DragDropEffects result = DragDrop.DoDragDrop(dragSource, Copy(nodes), effects);
if (result == DragDropEffects.Move) {
foreach (SharpTreeNode node in nodes)
node.DeleteCore();
}
}
internal bool InternalCanDrop(DragEventArgs e, int index)
public virtual bool CanDrop(DragEventArgs e, int index)
{
var finalEffect = GetFinalEffect(e, index);
e.Effects = GetDragDropEffects(finalEffect);
return finalEffect != DropEffect.None;
return false;
}
internal void InternalDrop(DragEventArgs e, int index)
{
if (LazyLoading) {
EnsureLazyChildren();
index = Children.Count;
}
var finalEffect = GetFinalEffect(e, index);
Drop(e.Data, index, finalEffect);
if (finalEffect == DropEffect.Move) {
DeleteCore(ActiveNodesArray);
}
Drop(e, index);
}
DropEffect GetFinalEffect(DragEventArgs e, int index)
{
var requestedEffect = GetDropEffect(e);
var result = CanDrop(e.Data, requestedEffect);
if (result == DropEffect.Move) {
if (!CanDelete(ActiveNodesArray)) {
return DropEffect.None;
}
}
return result;
}
static DropEffect GetDropEffect(DragEventArgs e)
{
if (e.Data != null) {
var all = DragDropKeyStates.ControlKey | DragDropKeyStates.ShiftKey | DragDropKeyStates.AltKey;
if ((e.KeyStates & all) == DragDropKeyStates.ControlKey) {
return DropEffect.Copy;
}
if ((e.KeyStates & all) == DragDropKeyStates.AltKey) {
return DropEffect.Link;
}
if ((e.KeyStates & all) == (DragDropKeyStates.ControlKey | DragDropKeyStates.ShiftKey)) {
return DropEffect.Link;
}
return DropEffect.Move;
}
return DropEffect.None;
}
static DragDropEffects GetDragDropEffects(DropEffect effect)
{
switch (effect) {
case DropEffect.Copy:
return DragDropEffects.Copy;
case DropEffect.Link:
return DragDropEffects.Link;
case DropEffect.Move:
return DragDropEffects.Move;
}
return DragDropEffects.None;
}
public virtual bool CanDrag(SharpTreeNode[] nodes)
{
return false;
}
public virtual DropEffect CanDrop(IDataObject data, DropEffect requestedEffect)
{
return DropEffect.None;
}
public virtual void Drop(IDataObject data, int index, DropEffect finalEffect)
public virtual void Drop(DragEventArgs e, int index)
{
throw new NotSupportedException(GetType().Name + " does not support Drop()");
}
#endregion
#region IsLast (for TreeView lines)
public bool IsLast
{
get
@ -572,7 +626,7 @@ namespace ICSharpCode.TreeView @@ -572,7 +626,7 @@ namespace ICSharpCode.TreeView
Parent.Children[Parent.Children.Count - 1] == this;
}
}
void RaiseIsLastChangedIfNeeded(NotifyCollectionChangedEventArgs e)
{
switch (e.Action) {
@ -593,20 +647,34 @@ namespace ICSharpCode.TreeView @@ -593,20 +647,34 @@ namespace ICSharpCode.TreeView
break;
}
}
#endregion
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string name)
{
if (PropertyChanged != null) {
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
#endregion
/// <summary>
/// Gets called when the item is double-clicked.
/// </summary>
public virtual void ActivateItem(RoutedEventArgs e)
{
}
public override string ToString()
{
// used for keyboard navigation
object text = this.Text;
return text != null ? text.ToString() : string.Empty;
}
}
}

205
src/Libraries/SharpTreeView/ICSharpCode.TreeView/SharpTreeNodeCollection.cs

@ -3,6 +3,7 @@ @@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;
@ -10,40 +11,200 @@ using System.Collections.Specialized; @@ -10,40 +11,200 @@ using System.Collections.Specialized;
namespace ICSharpCode.TreeView
{
public class SharpTreeNodeCollection : ObservableCollection<SharpTreeNode>
/// <summary>
/// Collection that validates that inserted nodes do not have another parent.
/// </summary>
public sealed class SharpTreeNodeCollection : IList<SharpTreeNode>, INotifyCollectionChanged
{
readonly SharpTreeNode parent;
List<SharpTreeNode> list = new List<SharpTreeNode>();
bool isRaisingEvent;
public SharpTreeNodeCollection(SharpTreeNode parent)
{
Parent = parent;
this.parent = parent;
}
public SharpTreeNode Parent { get; private set; }
protected override void InsertItem(int index, SharpTreeNode node)
public event NotifyCollectionChangedEventHandler CollectionChanged;
void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
node.Parent = Parent;
base.InsertItem(index, node);
Debug.Assert(!isRaisingEvent);
isRaisingEvent = true;
try {
parent.OnChildrenChanged(e);
if (CollectionChanged != null)
CollectionChanged(this, e);
} finally {
isRaisingEvent = false;
}
}
protected override void RemoveItem(int index)
void ThrowOnReentrancy()
{
var node = this[index];
node.Parent = null;
base.RemoveItem(index);
if (isRaisingEvent)
throw new InvalidOperationException();
}
protected override void ClearItems()
void ThrowIfValueIsNullOrHasParent(SharpTreeNode node)
{
foreach (var node in this) {
node.Parent = null;
if (node == null)
throw new ArgumentNullException("node");
if (node.modelParent != null)
throw new ArgumentException("The node already has a parent", "node");
}
public SharpTreeNode this[int index] {
get {
return list[index];
}
set {
ThrowOnReentrancy();
var oldItem = list[index];
if (oldItem == value)
return;
ThrowIfValueIsNullOrHasParent(value);
list[index] = value;
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, value, oldItem, index));
}
base.ClearItems();
}
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
public int Count {
get { return list.Count; }
}
bool ICollection<SharpTreeNode>.IsReadOnly {
get { return false; }
}
public int IndexOf(SharpTreeNode node)
{
if (node == null || node.modelParent != parent)
return -1;
else
return list.IndexOf(node);
}
public void Insert(int index, SharpTreeNode node)
{
ThrowOnReentrancy();
ThrowIfValueIsNullOrHasParent(node);
list.Insert(index, node);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, node, index));
}
public void InsertRange(int index, IEnumerable<SharpTreeNode> nodes)
{
if (nodes == null)
throw new ArgumentNullException("nodes");
ThrowOnReentrancy();
List<SharpTreeNode> newNodes = nodes.ToList();
if (newNodes.Count == 0)
return;
foreach (SharpTreeNode node in newNodes) {
ThrowIfValueIsNullOrHasParent(node);
}
list.InsertRange(index, newNodes);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, newNodes, index));
}
public void RemoveAt(int index)
{
ThrowOnReentrancy();
var oldItem = list[index];
list.RemoveAt(index);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, oldItem, index));
}
public void RemoveRange(int index, int count)
{
ThrowOnReentrancy();
if (count == 0)
return;
var oldItems = list.GetRange(index, count);
list.RemoveRange(index, count);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, oldItems, index));
}
public void Add(SharpTreeNode node)
{
ThrowOnReentrancy();
ThrowIfValueIsNullOrHasParent(node);
list.Add(node);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, node, list.Count - 1));
}
public void AddRange(IEnumerable<SharpTreeNode> nodes)
{
InsertRange(this.Count, nodes);
}
public void Clear()
{
ThrowOnReentrancy();
var oldList = list;
list = new List<SharpTreeNode>();
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, oldList, 0));
}
public bool Contains(SharpTreeNode node)
{
return IndexOf(node) >= 0;
}
public void CopyTo(SharpTreeNode[] array, int arrayIndex)
{
list.CopyTo(array, arrayIndex);
}
public bool Remove(SharpTreeNode item)
{
int pos = IndexOf(item);
if (pos >= 0) {
RemoveAt(pos);
return true;
} else {
return false;
}
}
public IEnumerator<SharpTreeNode> GetEnumerator()
{
return list.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return list.GetEnumerator();
}
public void RemoveAll(Predicate<SharpTreeNode> match)
{
base.OnCollectionChanged(e);
Parent.OnChildrenChanged(e);
if (match == null)
throw new ArgumentNullException("match");
ThrowOnReentrancy();
int firstToRemove = 0;
for (int i = 0; i < list.Count; i++) {
bool removeNode;
isRaisingEvent = true;
try {
removeNode = match(list[i]);
} finally {
isRaisingEvent = false;
}
if (!removeNode) {
if (firstToRemove < i) {
RemoveRange(firstToRemove, i - firstToRemove);
i = firstToRemove - 1;
} else {
firstToRemove = i + 1;
}
Debug.Assert(firstToRemove == i + 1);
}
}
if (firstToRemove < list.Count) {
RemoveRange(firstToRemove, list.Count - firstToRemove);
}
}
}
}

21
src/Libraries/SharpTreeView/ICSharpCode.TreeView/SharpTreeNodeView.cs

@ -20,7 +20,7 @@ namespace ICSharpCode.TreeView @@ -20,7 +20,7 @@ namespace ICSharpCode.TreeView
static SharpTreeNodeView()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(SharpTreeNodeView),
new FrameworkPropertyMetadata(typeof(SharpTreeNodeView)));
new FrameworkPropertyMetadata(typeof(SharpTreeNodeView)));
}
public static readonly DependencyProperty TextBackgroundProperty =
@ -71,14 +71,12 @@ namespace ICSharpCode.TreeView @@ -71,14 +71,12 @@ namespace ICSharpCode.TreeView
void UpdateDataContext(SharpTreeNode oldNode, SharpTreeNode newNode)
{
if (newNode != null) {
newNode.Collapsing += Node_Collapsing;
newNode.PropertyChanged += Node_PropertyChanged;
if (Template != null) {
UpdateTemplate();
}
}
if (oldNode != null) {
oldNode.Collapsing -= Node_Collapsing;
oldNode.PropertyChanged -= Node_PropertyChanged;
}
}
@ -87,24 +85,21 @@ namespace ICSharpCode.TreeView @@ -87,24 +85,21 @@ namespace ICSharpCode.TreeView
{
if (e.PropertyName == "IsEditing") {
OnIsEditingChanged();
}
else if (e.PropertyName == "IsLast") {
} else if (e.PropertyName == "IsLast") {
if (ParentTreeView.ShowLines) {
foreach (var child in Node.ExpandedDescendantsAndSelf()) {
foreach (var child in Node.VisibleDescendantsAndSelf()) {
var container = ParentTreeView.ItemContainerGenerator.ContainerFromItem(child) as SharpTreeViewItem;
if (container != null) {
container.NodeView.LinesRenderer.InvalidateVisual();
}
}
}
} else if (e.PropertyName == "IsExpanded") {
if (Node.IsExpanded)
ParentTreeView.HandleExpanding(Node);
}
}
void Node_Collapsing(object sender, EventArgs e)
{
ParentTreeView.HandleCollapsing(Node);
}
void OnIsEditingChanged()
{
var textEditorContainer = Template.FindName("textEditorContainer", this) as Border;
@ -143,7 +138,9 @@ namespace ICSharpCode.TreeView @@ -143,7 +138,9 @@ namespace ICSharpCode.TreeView
else {
result -= 19;
}
if (result < 0)
throw new InvalidOperationException();
return result;
}
}
}
}

358
src/Libraries/SharpTreeView/ICSharpCode.TreeView/SharpTreeView.cs

@ -3,14 +3,18 @@ @@ -3,14 +3,18 @@
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.Windows;
using System.Windows.Media;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Threading;
namespace ICSharpCode.TreeView
{
@ -19,19 +23,21 @@ namespace ICSharpCode.TreeView @@ -19,19 +23,21 @@ namespace ICSharpCode.TreeView
static SharpTreeView()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(SharpTreeView),
new FrameworkPropertyMetadata(typeof(SharpTreeView)));
new FrameworkPropertyMetadata(typeof(SharpTreeView)));
SelectionModeProperty.OverrideMetadata(typeof(SharpTreeView),
new FrameworkPropertyMetadata(SelectionMode.Extended));
new FrameworkPropertyMetadata(SelectionMode.Extended));
AlternationCountProperty.OverrideMetadata(typeof(SharpTreeView),
new FrameworkPropertyMetadata(2));
new FrameworkPropertyMetadata(2));
DefaultItemContainerStyleKey =
DefaultItemContainerStyleKey =
new ComponentResourceKey(typeof(SharpTreeView), "DefaultItemContainerStyleKey");
VirtualizingStackPanel.VirtualizationModeProperty.OverrideMetadata(typeof(SharpTreeView),
new FrameworkPropertyMetadata(VirtualizationMode.Recycling));
new FrameworkPropertyMetadata(VirtualizationMode.Recycling));
RegisterCommands();
}
public static ResourceKey DefaultItemContainerStyleKey { get; private set; }
@ -52,7 +58,7 @@ namespace ICSharpCode.TreeView @@ -52,7 +58,7 @@ namespace ICSharpCode.TreeView
public static readonly DependencyProperty ShowRootProperty =
DependencyProperty.Register("ShowRoot", typeof(bool), typeof(SharpTreeView),
new FrameworkPropertyMetadata(true));
new FrameworkPropertyMetadata(true));
public bool ShowRoot
{
@ -62,7 +68,7 @@ namespace ICSharpCode.TreeView @@ -62,7 +68,7 @@ namespace ICSharpCode.TreeView
public static readonly DependencyProperty ShowRootExpanderProperty =
DependencyProperty.Register("ShowRootExpander", typeof(bool), typeof(SharpTreeView),
new FrameworkPropertyMetadata(false));
new FrameworkPropertyMetadata(false));
public bool ShowRootExpander
{
@ -81,7 +87,7 @@ namespace ICSharpCode.TreeView @@ -81,7 +87,7 @@ namespace ICSharpCode.TreeView
public static readonly DependencyProperty ShowLinesProperty =
DependencyProperty.Register("ShowLines", typeof(bool), typeof(SharpTreeView),
new FrameworkPropertyMetadata(true));
new FrameworkPropertyMetadata(true));
public bool ShowLines
{
@ -101,14 +107,14 @@ namespace ICSharpCode.TreeView @@ -101,14 +107,14 @@ namespace ICSharpCode.TreeView
public static readonly DependencyProperty ShowAlternationProperty =
DependencyProperty.RegisterAttached("ShowAlternation", typeof(bool), typeof(SharpTreeView),
new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.Inherits));
new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.Inherits));
protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
{
base.OnPropertyChanged(e);
if (e.Property == RootProperty ||
e.Property == ShowRootProperty ||
e.Property == ShowRootExpanderProperty) {
if (e.Property == RootProperty ||
e.Property == ShowRootProperty ||
e.Property == ShowRootExpanderProperty) {
Reload();
}
}
@ -125,11 +131,34 @@ namespace ICSharpCode.TreeView @@ -125,11 +131,34 @@ namespace ICSharpCode.TreeView
Root.IsExpanded = true;
}
flattener = new TreeFlattener(Root, ShowRoot);
ItemsSource = flattener.List;
flattener.Start();
flattener.CollectionChanged += flattener_CollectionChanged;
this.ItemsSource = flattener;
}
}
void flattener_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
// Deselect nodes that are being hidden
if (e.Action == NotifyCollectionChangedAction.Remove) {
List<SharpTreeNode> selectedOldItems = null;
foreach (SharpTreeNode node in e.OldItems) {
if (node.IsSelected) {
if (selectedOldItems == null)
selectedOldItems = new List<SharpTreeNode>();
selectedOldItems.Add(node);
}
}
if (selectedOldItems != null) {
var list = SelectedItems.Cast<SharpTreeNode>().Except(selectedOldItems).ToList();
SetSelectedItems(list);
}
// reset the focus to the previous node
SelectedIndex = Math.Max(0, e.OldStartingIndex - 1);
if (SelectedItem != null)
FocusNode((SharpTreeNode)SelectedItem);
}
}
protected override DependencyObject GetContainerForItemOverride()
{
return new SharpTreeViewItem();
@ -146,68 +175,155 @@ namespace ICSharpCode.TreeView @@ -146,68 +175,155 @@ namespace ICSharpCode.TreeView
SharpTreeViewItem container = element as SharpTreeViewItem;
container.ParentTreeView = this;
}
internal void HandleCollapsing(SharpTreeNode Node)
{
var selectedChilds = Node.Descendants().Where(n => SharpTreeNode.SelectedNodes.Contains(n));
if (selectedChilds.Any()) {
var list = SelectedItems.Cast<SharpTreeNode>().Except(selectedChilds).ToList();
list.AddOnce(Node);
SetSelectedItems(list);
bool doNotScrollOnExpanding;
/// <summary>
/// Handles the node expanding event in the tree view.
/// This method gets called only if the node is in the visible region (a SharpTreeNodeView exists).
/// </summary>
internal void HandleExpanding(SharpTreeNode node)
{
if (doNotScrollOnExpanding)
return;
SharpTreeNode lastVisibleChild = node;
while (true) {
SharpTreeNode tmp = lastVisibleChild.Children.LastOrDefault(c => c.IsVisible);
if (tmp != null) {
lastVisibleChild = tmp;
} else {
break;
}
}
if (lastVisibleChild != node) {
// Make the the expanded children are visible; but don't scroll down
// to much (keep node itself visible)
base.ScrollIntoView(lastVisibleChild);
// For some reason, this only works properly when delaying it...
Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action(
delegate {
base.ScrollIntoView(node);
}));
}
}
#region Track selection
protected override void OnSelectionChanged(SelectionChangedEventArgs e)
{
foreach (SharpTreeNode node in e.RemovedItems) {
SharpTreeNode.SelectedNodes.Remove(node);
}
foreach (SharpTreeNode node in e.AddedItems) {
SharpTreeNode.SelectedNodes.AddOnce(node);
protected override void OnKeyDown(KeyEventArgs e)
{
SharpTreeViewItem container = e.OriginalSource as SharpTreeViewItem;
switch (e.Key) {
case Key.Left:
if (container != null && ItemsControl.ItemsControlFromItemContainer(container) == this) {
if (container.Node.IsExpanded) {
container.Node.IsExpanded = false;
} else if (container.Node.Parent != null) {
this.FocusNode(container.Node.Parent);
}
e.Handled = true;
}
break;
case Key.Right:
if (container != null && ItemsControl.ItemsControlFromItemContainer(container) == this) {
if (!container.Node.IsExpanded && container.Node.ShowExpander) {
container.Node.IsExpanded = true;
} else if (container.Node.Children.Count > 0) {
// jump to first child:
container.MoveFocus(new TraversalRequest(FocusNavigationDirection.Down));
}
e.Handled = true;
}
break;
case Key.Return:
case Key.Space:
if (container != null && Keyboard.Modifiers == ModifierKeys.None && this.SelectedItems.Count == 1 && this.SelectedItem == container.Node) {
container.Node.ActivateItem(e);
}
break;
case Key.Add:
if (container != null && ItemsControl.ItemsControlFromItemContainer(container) == this) {
container.Node.IsExpanded = true;
e.Handled = true;
}
break;
case Key.Subtract:
if (container != null && ItemsControl.ItemsControlFromItemContainer(container) == this) {
container.Node.IsExpanded = false;
e.Handled = true;
}
break;
case Key.Multiply:
if (container != null && ItemsControl.ItemsControlFromItemContainer(container) == this) {
container.Node.IsExpanded = true;
ExpandRecursively(container.Node);
e.Handled = true;
}
break;
}
if (IsKeyboardFocusWithin) {
foreach (SharpTreeNode node in e.RemovedItems) {
SharpTreeNode.ActiveNodes.Remove(node);
}
foreach (SharpTreeNode node in e.AddedItems) {
SharpTreeNode.ActiveNodes.AddOnce(node);
if (!e.Handled)
base.OnKeyDown(e);
}
void ExpandRecursively(SharpTreeNode node)
{
if (node.CanExpandRecursively) {
node.IsExpanded = true;
foreach (SharpTreeNode child in node.Children) {
ExpandRecursively(child);
}
SortActiveNodes();
}
base.OnSelectionChanged(e);
}
protected override void OnPreviewGotKeyboardFocus(KeyboardFocusChangedEventArgs e)
{
foreach (SharpTreeNode node in SelectedItems) {
SharpTreeNode.ActiveNodes.AddOnce(node);
/// <summary>
/// Scrolls the specified node in view and sets keyboard focus on it.
/// </summary>
public void FocusNode(SharpTreeNode node)
{
if (node == null)
throw new ArgumentNullException("node");
ScrollIntoView(node);
// WPF's ScrollIntoView() uses the same if/dispatcher construct, so we call OnFocusItem() after the item was brought into view.
if (this.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated) {
OnFocusItem(node);
} else {
this.Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new DispatcherOperationCallback(this.OnFocusItem), node);
}
SortActiveNodes();
}
protected override void OnPreviewLostKeyboardFocus(KeyboardFocusChangedEventArgs e)
public void ScrollIntoView(SharpTreeNode node)
{
if (node == null)
throw new ArgumentNullException("node");
doNotScrollOnExpanding = true;
foreach (SharpTreeNode ancestor in node.Ancestors())
ancestor.IsExpanded = true;
doNotScrollOnExpanding = false;
base.ScrollIntoView(node);
}
object OnFocusItem(object item)
{
foreach (SharpTreeNode node in SelectedItems) {
SharpTreeNode.ActiveNodes.Remove(node);
FrameworkElement element = this.ItemContainerGenerator.ContainerFromItem(item) as FrameworkElement;
if (element != null) {
element.Focus();
}
return null;
}
void SortActiveNodes()
#region Track selection
protected override void OnSelectionChanged(SelectionChangedEventArgs e)
{
SharpTreeNode.ActiveNodes.Sort(delegate(SharpTreeNode n1, SharpTreeNode n2) {
var index1 = Items.IndexOf(n1);
var index2 = Items.IndexOf(n2);
return index1.CompareTo(index2);
});
foreach (SharpTreeNode node in e.RemovedItems) {
node.IsSelected = false;
}
foreach (SharpTreeNode node in e.AddedItems) {
node.IsSelected = true;
}
base.OnSelectionChanged(e);
}
#endregion
#region Drag and Drop
protected override void OnDragEnter(DragEventArgs e)
{
OnDragOver(e);
@ -216,20 +332,20 @@ namespace ICSharpCode.TreeView @@ -216,20 +332,20 @@ namespace ICSharpCode.TreeView
protected override void OnDragOver(DragEventArgs e)
{
e.Effects = DragDropEffects.None;
e.Handled = true;
if (Root != null && !ShowRoot && Root.Children.Count == 0) {
Root.InternalCanDrop(e, 0);
if (Root != null && !ShowRoot) {
e.Handled = true;
Root.CanDrop(e, Root.Children.Count);
}
}
protected override void OnDrop(DragEventArgs e)
{
e.Effects = DragDropEffects.None;
e.Handled = true;
if (Root != null && !ShowRoot && Root.Children.Count == 0) {
Root.InternalDrop(e, 0);
if (Root != null && !ShowRoot) {
e.Handled = true;
Root.InternalDrop(e, Root.Children.Count);
}
}
@ -241,22 +357,27 @@ namespace ICSharpCode.TreeView @@ -241,22 +357,27 @@ namespace ICSharpCode.TreeView
internal void HandleDragOver(SharpTreeViewItem item, DragEventArgs e)
{
HidePreview();
e.Handled = true;
var target = GetDropTarget(item, e);
if (target != null) {
e.Handled = true;
ShowPreview(target.Item, target.Place);
}
}
internal void HandleDrop(SharpTreeViewItem item, DragEventArgs e)
{
HidePreview();
e.Handled = true;
try {
HidePreview();
var target = GetDropTarget(item, e);
if (target != null) {
target.Node.InternalDrop(e, target.Index);
var target = GetDropTarget(item, e);
if (target != null) {
e.Handled = true;
target.Node.InternalDrop(e, target.Index);
}
} catch (Exception ex) {
Debug.WriteLine(ex.ToString());
throw;
}
}
@ -270,7 +391,7 @@ namespace ICSharpCode.TreeView @@ -270,7 +391,7 @@ namespace ICSharpCode.TreeView
{
public SharpTreeViewItem Item;
public DropPlace Place;
public double Y;
public double Y;
public SharpTreeNode Node;
public int Index;
}
@ -293,7 +414,7 @@ namespace ICSharpCode.TreeView @@ -293,7 +414,7 @@ namespace ICSharpCode.TreeView
var node = item.Node;
if (AllowDropOrder) {
TryAddDropTarget(result, item, DropPlace.Before, e);
TryAddDropTarget(result, item, DropPlace.Before, e);
}
TryAddDropTarget(result, item, DropPlace.Inside, e);
@ -315,11 +436,11 @@ namespace ICSharpCode.TreeView @@ -315,11 +436,11 @@ namespace ICSharpCode.TreeView
if (result.Count == 2) {
if (result[0].Place == DropPlace.Inside &&
result[1].Place != DropPlace.Inside) {
result[1].Place != DropPlace.Inside) {
result[0].Y = y3;
}
else if (result[0].Place != DropPlace.Inside &&
result[1].Place == DropPlace.Inside) {
result[1].Place == DropPlace.Inside) {
result[0].Y = y1;
}
else {
@ -345,7 +466,7 @@ namespace ICSharpCode.TreeView @@ -345,7 +466,7 @@ namespace ICSharpCode.TreeView
if (node != null) {
e.Effects = DragDropEffects.None;
if (node.InternalCanDrop(e, index)) {
if (node.CanDrop(e, index)) {
DropTarget target = new DropTarget() {
Item = item,
Place = place,
@ -354,7 +475,7 @@ namespace ICSharpCode.TreeView @@ -354,7 +475,7 @@ namespace ICSharpCode.TreeView
};
targets.Add(target);
}
}
}
}
void GetNodeAndIndex(SharpTreeViewItem item, DropPlace place, out SharpTreeNode node, out int index)
@ -392,7 +513,7 @@ namespace ICSharpCode.TreeView @@ -392,7 +513,7 @@ namespace ICSharpCode.TreeView
void ShowPreview(SharpTreeViewItem item, DropPlace place)
{
previewNodeView = item.NodeView;
previewPlace = place;
previewPlace = place;
if (place == DropPlace.Inside) {
previewNodeView.TextBackground = SystemColors.HighlightBrush;
@ -419,14 +540,14 @@ namespace ICSharpCode.TreeView @@ -419,14 +540,14 @@ namespace ICSharpCode.TreeView
insertMarker.Margin = new Thickness(p.X, p.Y, 0, 0);
SharpTreeNodeView secondNodeView = null;
var index = flattener.List.IndexOf(item.Node);
var index = flattener.IndexOf(item.Node);
if (place == DropPlace.Before) {
if (index > 0) {
secondNodeView = (ItemContainerGenerator.ContainerFromIndex(index - 1) as SharpTreeViewItem).NodeView;
}
}
else if (index + 1 < flattener.List.Count) {
else if (index + 1 < flattener.Count) {
secondNodeView = (ItemContainerGenerator.ContainerFromIndex(index + 1) as SharpTreeViewItem).NodeView;
}
@ -452,6 +573,77 @@ namespace ICSharpCode.TreeView @@ -452,6 +573,77 @@ namespace ICSharpCode.TreeView
previewNodeView = null;
}
}
#endregion
#region Cut / Copy / Paste / Delete Commands
static void RegisterCommands()
{
CommandManager.RegisterClassCommandBinding(typeof(SharpTreeView),
new CommandBinding(ApplicationCommands.Cut, HandleExecuted_Cut, HandleCanExecute_Cut));
CommandManager.RegisterClassCommandBinding(typeof(SharpTreeView),
new CommandBinding(ApplicationCommands.Copy, HandleExecuted_Copy, HandleCanExecute_Copy));
CommandManager.RegisterClassCommandBinding(typeof(SharpTreeView),
new CommandBinding(ApplicationCommands.Paste, HandleExecuted_Paste, HandleCanExecute_Paste));
CommandManager.RegisterClassCommandBinding(typeof(SharpTreeView),
new CommandBinding(ApplicationCommands.Delete, HandleExecuted_Delete, HandleCanExecute_Delete));
}
static void HandleExecuted_Cut(object sender, ExecutedRoutedEventArgs e)
{
}
static void HandleCanExecute_Cut(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = false;
}
static void HandleExecuted_Copy(object sender, ExecutedRoutedEventArgs e)
{
}
static void HandleCanExecute_Copy(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = false;
}
static void HandleExecuted_Paste(object sender, ExecutedRoutedEventArgs e)
{
}
static void HandleCanExecute_Paste(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = false;
}
static void HandleExecuted_Delete(object sender, ExecutedRoutedEventArgs e)
{
SharpTreeView treeView = (SharpTreeView)sender;
foreach (SharpTreeNode node in treeView.GetTopLevelSelection().ToArray())
node.Delete();
}
static void HandleCanExecute_Delete(object sender, CanExecuteRoutedEventArgs e)
{
SharpTreeView treeView = (SharpTreeView)sender;
e.CanExecute = treeView.GetTopLevelSelection().All(node => node.CanDelete());
}
/// <summary>
/// Gets the selected items which do not have any of their ancestors selected.
/// </summary>
public IEnumerable<SharpTreeNode> GetTopLevelSelection()
{
var selection = this.SelectedItems.OfType<SharpTreeNode>();
var selectionHash = new HashSet<SharpTreeNode>(selection);
return selection.Where(item => item.Ancestors().All(a => !selectionHash.Contains(a)));
}
#endregion
}

112
src/Libraries/SharpTreeView/ICSharpCode.TreeView/SharpTreeViewItem.cs

@ -18,9 +18,7 @@ namespace ICSharpCode.TreeView @@ -18,9 +18,7 @@ namespace ICSharpCode.TreeView
static SharpTreeViewItem()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(SharpTreeViewItem),
new FrameworkPropertyMetadata(typeof(SharpTreeViewItem)));
RegisterCommands();
new FrameworkPropertyMetadata(typeof(SharpTreeViewItem)));
}
public SharpTreeNode Node
@ -29,42 +27,28 @@ namespace ICSharpCode.TreeView @@ -29,42 +27,28 @@ namespace ICSharpCode.TreeView
}
public SharpTreeNodeView NodeView { get; internal set; }
public SharpTreeView ParentTreeView { get; internal set; }
public SharpTreeView ParentTreeView { get; internal set; }
protected override void OnKeyDown(KeyEventArgs e)
{
switch (e.Key) {
case Key.F2:
if (SharpTreeNode.ActiveNodes.Count == 1 && Node.IsEditable) {
Node.IsEditing = true;
}
// if (SharpTreeNode.ActiveNodes.Count == 1 && Node.IsEditable) {
// Node.IsEditing = true;
// e.Handled = true;
// }
break;
case Key.Escape:
Node.IsEditing = false;
break;
case Key.Left:
Node.IsExpanded = false;
break;
case Key.Right:
Node.IsExpanded = true;
break;
}
}
protected override void OnContextMenuOpening(ContextMenuEventArgs e)
{
ContextMenu = Node.GetContextMenu();
}
protected override void OnContextMenuClosing(ContextMenuEventArgs e)
{
ClearValue(ContextMenuProperty);
}
#region Mouse
Point startPoint;
bool wasSelected;
bool wasDoubleClick;
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
@ -78,9 +62,7 @@ namespace ICSharpCode.TreeView @@ -78,9 +62,7 @@ namespace ICSharpCode.TreeView
CaptureMouse();
if (e.ClickCount == 2) {
if (!Node.IsRoot || ParentTreeView.ShowRootExpander) {
Node.IsExpanded = !Node.IsExpanded;
}
wasDoubleClick = true;
}
}
}
@ -90,10 +72,11 @@ namespace ICSharpCode.TreeView @@ -90,10 +72,11 @@ namespace ICSharpCode.TreeView
if (IsMouseCaptured) {
var currentPoint = e.GetPosition(null);
if (Math.Abs(currentPoint.X - startPoint.X) >= SystemParameters.MinimumHorizontalDragDistance ||
Math.Abs(currentPoint.Y - startPoint.Y) >= SystemParameters.MinimumVerticalDragDistance) {
Math.Abs(currentPoint.Y - startPoint.Y) >= SystemParameters.MinimumVerticalDragDistance) {
if (Node.InternalCanDrag()) {
Node.InternalDrag(this);
var selection = ParentTreeView.GetTopLevelSelection().ToArray();
if (Node.CanDrag(selection)) {
Node.StartDrag(this, selection);
}
}
}
@ -101,6 +84,16 @@ namespace ICSharpCode.TreeView @@ -101,6 +84,16 @@ namespace ICSharpCode.TreeView
protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
{
if (wasDoubleClick) {
wasDoubleClick = false;
Node.ActivateItem(e);
if (!e.Handled) {
if (!Node.IsRoot || ParentTreeView.ShowRootExpander) {
Node.IsExpanded = !Node.IsExpanded;
}
}
}
ReleaseMouseCapture();
if (wasSelected) {
base.OnMouseLeftButtonDown(e);
@ -108,7 +101,7 @@ namespace ICSharpCode.TreeView @@ -108,7 +101,7 @@ namespace ICSharpCode.TreeView
}
#endregion
#region Drag and Drop
protected override void OnDragEnter(DragEventArgs e)
@ -132,64 +125,5 @@ namespace ICSharpCode.TreeView @@ -132,64 +125,5 @@ namespace ICSharpCode.TreeView
}
#endregion
#region Cut / Copy / Paste / Delete Commands
static void RegisterCommands()
{
CommandManager.RegisterClassCommandBinding(typeof(SharpTreeViewItem),
new CommandBinding(ApplicationCommands.Cut, HandleExecuted_Cut, HandleCanExecute_Cut));
CommandManager.RegisterClassCommandBinding(typeof(SharpTreeViewItem),
new CommandBinding(ApplicationCommands.Copy, HandleExecuted_Copy, HandleCanExecute_Copy));
CommandManager.RegisterClassCommandBinding(typeof(SharpTreeViewItem),
new CommandBinding(ApplicationCommands.Paste, HandleExecuted_Paste, HandleCanExecute_Paste));
CommandManager.RegisterClassCommandBinding(typeof(SharpTreeViewItem),
new CommandBinding(ApplicationCommands.Delete, HandleExecuted_Delete, HandleCanExecute_Delete));
}
static void HandleExecuted_Cut(object sender, ExecutedRoutedEventArgs e)
{
(sender as SharpTreeViewItem).Node.InternalCut();
}
static void HandleCanExecute_Cut(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = (sender as SharpTreeViewItem).Node.InternalCanCut();
}
static void HandleExecuted_Copy(object sender, ExecutedRoutedEventArgs e)
{
(sender as SharpTreeViewItem).Node.InternalCopy();
}
static void HandleCanExecute_Copy(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = (sender as SharpTreeViewItem).Node.InternalCanCopy();
}
static void HandleExecuted_Paste(object sender, ExecutedRoutedEventArgs e)
{
(sender as SharpTreeViewItem).Node.InternalPaste();
}
static void HandleCanExecute_Paste(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = (sender as SharpTreeViewItem).Node.InternalCanPaste();
}
static void HandleExecuted_Delete(object sender, ExecutedRoutedEventArgs e)
{
(sender as SharpTreeViewItem).Node.InternalDelete();
}
static void HandleCanExecute_Delete(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = (sender as SharpTreeViewItem).Node.InternalCanDelete();
}
#endregion
}
}

28
src/Libraries/SharpTreeView/ICSharpCode.TreeView/Themes/Generic.xaml

@ -248,19 +248,25 @@ @@ -248,19 +248,25 @@
<StackPanel Orientation="Horizontal"
Background="Transparent"
ToolTip="{Binding ToolTip}">
<ContentControl Name="icon"
Content="{Binding Icon}"
Width="16"
Height="16"
Margin="0 0 5 1"
VerticalAlignment="Center"
Focusable="False" />
<ContentPresenter Name="icon"
Content="{Binding Icon}"
Width="16"
Height="16"
Margin="0 0 5 1"
VerticalAlignment="Center"
Focusable="False">
<ContentPresenter.ContentTemplate>
<DataTemplate>
<Image Source="{Binding}"/>
</DataTemplate>
</ContentPresenter.ContentTemplate>
</ContentPresenter>
<Border Name="textContainer"
Background="{TemplateBinding TextBackground}">
<ContentControl Content="{Binding Text}"
Margin="2 0 6 0"
VerticalAlignment="Center"
Focusable="False" />
<ContentPresenter Content="{Binding Text}"
Margin="2 0 6 0"
VerticalAlignment="Center"
Focusable="False" />
</Border>
<Border Name="textEditorContainer" />
</StackPanel>

221
src/Libraries/SharpTreeView/ICSharpCode.TreeView/TreeFlattener.cs

@ -2,148 +2,153 @@ @@ -2,148 +2,153 @@
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Collections.Specialized;
namespace ICSharpCode.TreeView
{
class TreeFlattener
sealed class TreeFlattener : IList, INotifyCollectionChanged
{
public TreeFlattener(SharpTreeNode root, bool includeRoot)
/// <summary>
/// The root node of the flat list tree.
/// Tjis is not necessarily the root of the model!
/// </summary>
internal SharpTreeNode root;
readonly bool includeRoot;
readonly object syncRoot = new object();
public TreeFlattener(SharpTreeNode modelRoot, bool includeRoot)
{
this.root = root;
this.root = modelRoot;
while (root.listParent != null)
root = root.listParent;
root.treeFlattener = this;
this.includeRoot = includeRoot;
List = new ObservableCollection<SharpTreeNode>();
}
SharpTreeNode root;
bool includeRoot;
public ObservableCollection<SharpTreeNode> List { get; private set; }
public void Start()
public event NotifyCollectionChangedEventHandler CollectionChanged;
public void RaiseCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (includeRoot) {
Add(root);
}
else {
root.Children.CollectionChanged += node_ChildrenChanged;
if (CollectionChanged != null)
CollectionChanged(this, e);
}
public void NodesInserted(int index, IEnumerable<SharpTreeNode> nodes)
{
if (!includeRoot) index--;
foreach (SharpTreeNode node in nodes) {
RaiseCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, node, index++));
}
foreach (var node in root.ExpandedDescendants()) {
Add(node);
}
public void NodesRemoved(int index, IEnumerable<SharpTreeNode> nodes)
{
if (!includeRoot) index--;
foreach (SharpTreeNode node in nodes) {
RaiseCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, node, index));
}
}
public void Stop()
{
while (List.Count > 0) {
RemoveAt(0);
Debug.Assert(root.treeFlattener == this);
root.treeFlattener = null;
}
public object this[int index] {
get {
if (index < 0 || index >= this.Count)
throw new ArgumentOutOfRangeException();
return SharpTreeNode.GetNodeByVisibleIndex(root, includeRoot ? index : index + 1);
}
set {
throw new NotSupportedException();
}
}
void Add(SharpTreeNode node)
{
Insert(List.Count, node);
public int Count {
get {
return includeRoot ? root.GetTotalListLength() : root.GetTotalListLength() - 1;
}
}
void Insert(int index, SharpTreeNode node)
public int IndexOf(object item)
{
List.Insert(index, node);
node.PropertyChanged += node_PropertyChanged;
if (node.IsExpanded) {
node.Children.CollectionChanged += node_ChildrenChanged;
SharpTreeNode node = item as SharpTreeNode;
if (node != null && node.IsVisible && node.GetListRoot() == root) {
if (includeRoot)
return SharpTreeNode.GetVisibleIndexForNode(node);
else
return SharpTreeNode.GetVisibleIndexForNode(node) - 1;
} else {
return -1;
}
}
void RemoveAt(int index)
{
var node = List[index];
List.RemoveAt(index);
node.PropertyChanged -= node_PropertyChanged;
if (node.IsExpanded) {
node.Children.CollectionChanged -= node_ChildrenChanged;
bool IList.IsReadOnly {
get { return true; }
}
bool IList.IsFixedSize {
get { return false; }
}
bool ICollection.IsSynchronized {
get { return false; }
}
object ICollection.SyncRoot {
get {
return syncRoot;
}
}
void ClearDescendants(SharpTreeNode node)
void IList.Insert(int index, object item)
{
var index = List.IndexOf(node);
while (index + 1 < List.Count && List[index + 1].Level > node.Level) {
RemoveAt(index + 1);
}
throw new NotSupportedException();
}
void node_PropertyChanged(object sender, PropertyChangedEventArgs e)
void IList.RemoveAt(int index)
{
if (e.PropertyName == "IsExpanded") {
var node = sender as SharpTreeNode;
if (node.IsExpanded) {
var index = List.IndexOf(node);
foreach (var childNode in node.ExpandedDescendants()) {
Insert(++index, childNode);
}
node.Children.CollectionChanged += node_ChildrenChanged;
}
else {
ClearDescendants(node);
node.Children.CollectionChanged -= node_ChildrenChanged;
}
}
throw new NotSupportedException();
}
void Insert(SharpTreeNode parent, int index, SharpTreeNode node)
int IList.Add(object item)
{
int finalIndex = 0;
if (index > 0) {
finalIndex = List.IndexOf(parent.Children[index - 1]) + 1;
while (finalIndex < List.Count && List[finalIndex].Level > node.Level) {
finalIndex++;
}
}
else {
finalIndex = List.IndexOf(parent) + 1;
}
Insert(finalIndex, node);
throw new NotSupportedException();
}
void RemoveAt(SharpTreeNode parent, int index, SharpTreeNode node)
void IList.Clear()
{
var i = List.IndexOf(node);
foreach (var child in node.ExpandedDescendantsAndSelf()) {
RemoveAt(i);
}
throw new NotSupportedException();
}
void node_ChildrenChanged(object sender, NotifyCollectionChangedEventArgs e)
public bool Contains(object item)
{
var collection = sender as SharpTreeNodeCollection;
var parent = collection.Parent;
var index = List.IndexOf(collection.Parent) + 1;
switch (e.Action) {
case NotifyCollectionChangedAction.Add:
Insert(parent, e.NewStartingIndex, e.NewItems[0] as SharpTreeNode);
break;
case NotifyCollectionChangedAction.Remove:
RemoveAt(parent, e.OldStartingIndex, e.OldItems[0] as SharpTreeNode);
break;
case NotifyCollectionChangedAction.Move:
RemoveAt(parent, e.OldStartingIndex, e.OldItems[0] as SharpTreeNode);
Insert(parent, e.NewStartingIndex, e.NewItems[0] as SharpTreeNode);
break;
case NotifyCollectionChangedAction.Replace:
RemoveAt(parent, e.OldStartingIndex, e.OldItems[0] as SharpTreeNode);
Insert(parent, e.NewStartingIndex, e.NewItems[0] as SharpTreeNode);
break;
case NotifyCollectionChangedAction.Reset:
ClearDescendants(parent);
break;
return IndexOf(item) >= 0;
}
public void CopyTo(Array array, int arrayIndex)
{
foreach (object item in this)
array.SetValue(item, arrayIndex++);
}
void IList.Remove(object item)
{
throw new NotSupportedException();
}
public IEnumerator GetEnumerator()
{
for (int i = 0; i < this.Count; i++) {
yield return this[i];
}
}
}

54
src/Libraries/SharpTreeView/ICSharpCode.TreeView/TreeTraversal.cs

@ -0,0 +1,54 @@ @@ -0,0 +1,54 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
namespace ICSharpCode.TreeView
{
/// <summary>
/// Static helper methods for traversing trees.
/// </summary>
static class TreeTraversal
{
/// <summary>
/// Converts a tree data structure into a flat list by traversing it in pre-order.
/// </summary>
/// <param name="root">The root element of the tree.</param>
/// <param name="recursion">The function that gets the children of an element.</param>
/// <returns>Iterator that enumerates the tree structure in pre-order.</returns>
public static IEnumerable<T> PreOrder<T>(T root, Func<T, IEnumerable<T>> recursion)
{
return PreOrder(new T[] { root }, recursion);
}
/// <summary>
/// Converts a tree data structure into a flat list by traversing it in pre-order.
/// </summary>
/// <param name="input">The root elements of the forest.</param>
/// <param name="recursion">The function that gets the children of an element.</param>
/// <returns>Iterator that enumerates the tree structure in pre-order.</returns>
public static IEnumerable<T> PreOrder<T>(IEnumerable<T> input, Func<T, IEnumerable<T>> recursion)
{
Stack<IEnumerator<T>> stack = new Stack<IEnumerator<T>>();
try {
stack.Push(input.GetEnumerator());
while (stack.Count > 0) {
while (stack.Peek().MoveNext()) {
T element = stack.Peek().Current;
yield return element;
IEnumerable<T> children = recursion(element);
if (children != null) {
stack.Push(children.GetEnumerator());
}
}
stack.Pop().Dispose();
}
} finally {
while (stack.Count > 0) {
stack.Pop().Dispose();
}
}
}
}
}
Loading…
Cancel
Save