Browse Source

Started implementing a better TreeFlattener for SharpTreeView.

pull/10/head
Daniel Grunwald 14 years ago
parent
commit
ad14b53945
  1. 3
      ILSpy/TreeNodes/AssemblyListTreeNode.cs
  2. 4
      ILSpy/TreeNodes/AssemblyTreeNode.cs
  3. 83
      SharpTreeView/FlatListTreeNode.cs
  4. 6
      SharpTreeView/ICSharpCode.TreeView.csproj
  5. 633
      SharpTreeView/SharpTreeNode.cs
  6. 150
      SharpTreeView/SharpTreeNodeCollection.cs
  7. 17
      SharpTreeView/SharpTreeNodeView.cs
  8. 37
      SharpTreeView/SharpTreeView.cs
  9. 38
      SharpTreeView/SharpTreeViewItem.cs
  10. 190
      SharpTreeView/TreeFlattener.cs
  11. 54
      SharpTreeView/TreeTraversal.cs

3
ILSpy/TreeNodes/AssemblyListTreeNode.cs

@ -45,6 +45,7 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -45,6 +45,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
this.assemblyList = assemblyList;
}
/*
public override DropEffect CanDrop(IDataObject data, DropEffect requestedEffect)
{
if (data.GetDataPresent(AssemblyTreeNode.DataFormat))
@ -79,7 +80,7 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -79,7 +80,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
}
}
}
}
}*/
public Action<SharpTreeNode> Select = delegate {};

4
ILSpy/TreeNodes/AssemblyTreeNode.cs

@ -206,10 +206,10 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -206,10 +206,10 @@ namespace ICSharpCode.ILSpy.TreeNodes
}
}
public override bool CanDrag(SharpTreeNode[] nodes)
/*public override bool CanDrag(SharpTreeNode[] nodes)
{
return nodes.All(n => n is AssemblyTreeNode);
}
}*/
public override bool CanDelete()
{

83
SharpTreeView/FlatListTreeNode.cs

@ -0,0 +1,83 @@ @@ -0,0 +1,83 @@
// 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.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>
SharpTreeNode listParent;
/// <summary>Left/right nodes in the flat list</summary>
SharpTreeNode left, right;
/// <summary>Subtree height in the flat list tree</summary>
byte height;
/// <summary>Length in the flat list, including children (children within the flat list).</summary>
internal int totalListLength = 1;
int Balance {
get { return Height(right) - Height(left); }
}
static int Height(SharpTreeNode node)
{
return node != null ? node.height : 0;
}
[Conditional("DEBUG")]
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);
if (left != null) left.CheckInvariants();
if (right != null) right.CheckInvariants();
}
#region GetNodeByVisibleIndex / GetVisibleIndexForNode
internal static SharpTreeNode GetNodeByVisibleIndex(SharpTreeNode root, int index)
{
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 = 0;
while (node.listParent != null) {
if (node == node.listParent.right) {
if (node.listParent.left != null)
index += node.listParent.left.totalListLength;
if (node.isVisible)
index++;
}
node = node.listParent;
}
return index;
}
#endregion
}
}

6
SharpTreeView/ICSharpCode.TreeView.csproj

@ -80,6 +80,7 @@ @@ -80,6 +80,7 @@
<SubType>Code</SubType>
</Compile>
<Compile Include="Properties\GlobalAssemblyInfo.cs" />
<Compile Include="FlatListTreeNode.cs" />
<Compile Include="SharpGridView.cs" />
<Compile Include="SharpTreeNode.cs" />
<Compile Include="SharpTreeNodeCollection.cs" />
@ -88,6 +89,11 @@ @@ -88,6 +89,11 @@
<Compile Include="SharpTreeViewItem.cs" />
<Compile Include="TreeFlattener.cs" />
<AppDesigner Include="Properties\" />
<Compile Include="TreeTraversal.cs" />
</ItemGroup>
<ItemGroup>
<None Include="copyright.txt" />
<None Include="license.txt" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.

633
SharpTreeView/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,123 @@ using System.Windows.Input; @@ -14,107 +15,123 @@ using System.Windows.Input;
namespace ICSharpCode.TreeView
{
public class SharpTreeNode : INotifyPropertyChanged
public partial class SharpTreeNode : INotifyPropertyChanged
{
SharpTreeNodeCollection modelChildren;
internal SharpTreeNode modelParent;
bool isVisible = true;
#region Main
static SharpTreeNode()
public SharpTreeNode()
{
SelectedNodes = new HashSet<SharpTreeNode>();
ActiveNodes = new HashSet<SharpTreeNode>();
//StartCuttedDataWatcher();
}
public static HashSet<SharpTreeNode> SelectedNodes { get; private set; }
public static HashSet<SharpTreeNode> ActiveNodes { get; private set; }
static SharpTreeNode[] ActiveNodesArray
{
get
{
return ActiveNodes.ToArray();
public SharpTreeNodeCollection Children {
get {
if (modelChildren == null)
modelChildren = new SharpTreeNodeCollection(this);
return modelChildren;
}
}
public SharpTreeNode()
{
Children = new SharpTreeNodeCollection(this);
public SharpTreeNode Parent {
get { return modelParent; }
}
public SharpTreeNodeCollection Children { get; private set; }
public SharpTreeNode Parent { get; internal set; }
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");
// }
//}
bool isHidden;
public bool IsHidden
{
get { return isHidden; }
set {
if (isHidden != value) {
isHidden = value;
RaisePropertyChanged("IsHidden");
}
}
}
bool isSelected;
public bool IsSelected
{
get { return isSelected; }
set {
if (isSelected != value) {
isSelected = value;
RaisePropertyChanged("IsSelected");
}
}
}
public virtual ContextMenu GetContextMenu()
{
return null;
}
internal protected void OnChildrenChanged(NotifyCollectionChangedEventArgs e)
#endregion
#region OnChildrenChanged
internal void OnChildrenChanged(NotifyCollectionChangedEventArgs e)
{
if (e.OldItems != null) {
foreach (SharpTreeNode node in e.OldItems) {
Debug.Assert(node.modelParent == this);
node.modelParent = null;
}
}
if (e.NewItems != null) {
foreach (SharpTreeNode node in e.NewItems) {
Debug.Assert(node.modelParent == null);
node.modelParent = this;
}
}
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; }
}
//public virtual bool ShowLoading
//{
// get { return false; }
//}
bool isExpanded;
public bool IsExpanded
{
get { return isExpanded; }
@ -125,18 +142,13 @@ namespace ICSharpCode.TreeView @@ -125,18 +142,13 @@ namespace ICSharpCode.TreeView
if (isExpanded) {
EnsureLazyChildren();
}
else {
if (Collapsing != null) {
Collapsing(this, EventArgs.Empty);
}
}
RaisePropertyChanged("IsExpanded");
}
}
}
bool lazyLoading;
public bool LazyLoading
{
get { return lazyLoading; }
@ -155,12 +167,12 @@ namespace ICSharpCode.TreeView @@ -155,12 +167,12 @@ namespace ICSharpCode.TreeView
{
get { return Icon != null; }
}
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>
@ -171,49 +183,31 @@ namespace ICSharpCode.TreeView @@ -171,49 +183,31 @@ namespace ICSharpCode.TreeView
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()
{
foreach (var child in Children) {
foreach (var child2 in child.ExpandedDescendantsAndSelf()) {
yield return child2;
}
}
return TreeTraversal.PreOrder(this.Children, n => n.IsExpanded ? n.Children : null);
}
public IEnumerable<SharpTreeNode> ExpandedDescendantsAndSelf()
{
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.IsExpanded ? n.Children : null);
}
public IEnumerable<SharpTreeNode> Ancestors()
{
var node = this;
@ -222,7 +216,7 @@ namespace ICSharpCode.TreeView @@ -222,7 +216,7 @@ namespace ICSharpCode.TreeView
node = node.Parent;
}
}
public IEnumerable<SharpTreeNode> AncestorsAndSelf()
{
yield return this;
@ -230,18 +224,18 @@ namespace ICSharpCode.TreeView @@ -230,18 +224,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; }
@ -253,42 +247,39 @@ namespace ICSharpCode.TreeView @@ -253,42 +247,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()) {
@ -297,7 +288,7 @@ namespace ICSharpCode.TreeView @@ -297,7 +288,7 @@ namespace ICSharpCode.TreeView
}
}
}
foreach (var parent in Ancestors()) {
if (parent.IsCheckable) {
if (!parent.TryValueForIsChecked(true)) {
@ -308,11 +299,11 @@ namespace ICSharpCode.TreeView @@ -308,11 +299,11 @@ namespace ICSharpCode.TreeView
}
}
}
RaisePropertyChanged("IsChecked");
}
}
bool TryValueForIsChecked(bool? value)
{
if (Children.Where(n => n.IsCheckable).All(n => n.IsChecked == value)) {
@ -321,245 +312,245 @@ namespace ICSharpCode.TreeView @@ -321,245 +312,245 @@ namespace ICSharpCode.TreeView
}
return false;
}
#endregion
#region Cut / Copy / Paste / Delete
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 CommandManager_RequerySuggested(object sender, EventArgs e)
{
if (cuttedData != null && !Clipboard.IsCurrent(cuttedData)) {
ClearCuttedData();
/*
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 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()
{
throw new NotSupportedException(GetType().Name + " does not support deletion");
}
public virtual void DeleteCore()
{
throw new NotSupportedException(GetType().Name + " does not support deletion");
}
public virtual IDataObject Copy(SharpTreeNode[] nodes)
{
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);
}
*/
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()
{
return CanDrag(ActiveNodesArray);
}
internal void InternalDrag(DependencyObject dragSource)
{
DragDrop.DoDragDrop(dragSource, Copy(ActiveNodesArray), DragDropEffects.All);
}
internal bool InternalCanDrop(DragEventArgs e, int index)
{
var finalEffect = GetFinalEffect(e, index);
e.Effects = GetDragDropEffects(finalEffect);
return finalEffect != DropEffect.None;
}
internal void InternalDrop(DragEventArgs e, int index)
{
if (LazyLoading) {
EnsureLazyChildren();
index = Children.Count;
/*
internal bool InternalCanDrag()
{
return CanDrag(ActiveNodesArray);
}
var finalEffect = GetFinalEffect(e, index);
Drop(e.Data, index, finalEffect);
if (finalEffect == DropEffect.Move) {
foreach (SharpTreeNode node in ActiveNodesArray)
node.DeleteCore();
internal void InternalDrag(DependencyObject dragSource)
{
DragDrop.DoDragDrop(dragSource, Copy(ActiveNodesArray), DragDropEffects.All);
}
}
DropEffect GetFinalEffect(DragEventArgs e, int index)
{
var requestedEffect = GetDropEffect(e);
var result = CanDrop(e.Data, requestedEffect);
if (result == DropEffect.Move) {
if (!ActiveNodesArray.All(n => n.CanDelete())) {
return DropEffect.None;
internal bool InternalCanDrop(DragEventArgs e, int index)
{
var finalEffect = GetFinalEffect(e, index);
e.Effects = GetDragDropEffects(finalEffect);
return finalEffect != DropEffect.None;
}
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) {
foreach (SharpTreeNode node in ActiveNodesArray)
node.DeleteCore();
}
}
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;
DropEffect GetFinalEffect(DragEventArgs e, int index)
{
var requestedEffect = GetDropEffect(e);
var result = CanDrop(e.Data, requestedEffect);
if (result == DropEffect.Move) {
if (!ActiveNodesArray.All(n => n.CanDelete())) {
return DropEffect.None;
}
}
if ((e.KeyStates & all) == DragDropKeyStates.AltKey) {
return DropEffect.Link;
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;
}
if ((e.KeyStates & all) == (DragDropKeyStates.ControlKey | DragDropKeyStates.ShiftKey)) {
return DropEffect.Link;
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 DropEffect.Move;
return DragDropEffects.None;
}
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;
public virtual bool CanDrag(SharpTreeNode[] nodes)
{
return false;
}
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)
{
throw new NotSupportedException(GetType().Name + " does not support Drop()");
}
public virtual DropEffect CanDrop(IDataObject data, DropEffect requestedEffect)
{
return DropEffect.None;
}
public virtual void Drop(IDataObject data, int index, DropEffect finalEffect)
{
throw new NotSupportedException(GetType().Name + " does not support Drop()");
}
*/
#endregion
#region IsLast (for TreeView lines)
public bool IsLast
{
get
@ -568,7 +559,7 @@ namespace ICSharpCode.TreeView @@ -568,7 +559,7 @@ namespace ICSharpCode.TreeView
Parent.Children[Parent.Children.Count - 1] == this;
}
}
void RaiseIsLastChangedIfNeeded(NotifyCollectionChangedEventArgs e)
{
switch (e.Action) {
@ -589,20 +580,20 @@ namespace ICSharpCode.TreeView @@ -589,20 +580,20 @@ 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>

150
SharpTreeView/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,44 +11,141 @@ using System.Collections.Specialized; @@ -10,44 +11,141 @@ 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();*/
// workaround for bug (reproducable when using ILSpy search filter)
while (Count > 0)
RemoveAt(Count - 1);
}
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 RemoveAt(int index)
{
ThrowOnReentrancy();
var oldItem = list[index];
list.RemoveAt(index);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, oldItem, index));
}
public void Add(SharpTreeNode node)
{
ThrowOnReentrancy();
ThrowIfValueIsNullOrHasParent(node);
list.Add(node);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, node, list.Count - 1));
}
public void Clear()
{
ThrowOnReentrancy();
var oldList = list;
list = new List<SharpTreeNode>();
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, list, 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()
{
base.OnCollectionChanged(e);
Parent.OnChildrenChanged(e);
return list.GetEnumerator();
}
}
}

17
SharpTreeView/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,8 +85,7 @@ namespace ICSharpCode.TreeView @@ -87,8 +85,7 @@ 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()) {
var container = ParentTreeView.ItemContainerGenerator.ContainerFromItem(child) as SharpTreeViewItem;
@ -97,14 +94,12 @@ namespace ICSharpCode.TreeView @@ -97,14 +94,12 @@ namespace ICSharpCode.TreeView
}
}
}
} else if (e.PropertyName == "IsExpanded") {
if (Node.IsExpanded == false)
ParentTreeView.HandleCollapsing(Node);
}
}
void Node_Collapsing(object sender, EventArgs e)
{
ParentTreeView.HandleCollapsing(Node);
}
void OnIsEditingChanged()
{
var textEditorContainer = Template.FindName("textEditorContainer", this) as Border;
@ -146,6 +141,6 @@ namespace ICSharpCode.TreeView @@ -146,6 +141,6 @@ namespace ICSharpCode.TreeView
if (result < 0)
throw new InvalidOperationException();
return result;
}
}
}
}

37
SharpTreeView/SharpTreeView.cs

@ -130,7 +130,7 @@ namespace ICSharpCode.TreeView @@ -130,7 +130,7 @@ namespace ICSharpCode.TreeView
Root.IsExpanded = true;
}
flattener = new TreeFlattener(Root, ShowRoot);
ItemsSource = flattener.List;
this.ItemsSource = flattener;
flattener.Start();
}
}
@ -151,10 +151,10 @@ namespace ICSharpCode.TreeView @@ -151,10 +151,10 @@ 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));
var selectedChilds = Node.ExpandedDescendants().Where(n => n.IsSelected);
if (selectedChilds.Any()) {
var list = SelectedItems.Cast<SharpTreeNode>().Except(selectedChilds).ToList();
list.AddOnce(Node);
@ -237,41 +237,18 @@ namespace ICSharpCode.TreeView @@ -237,41 +237,18 @@ namespace ICSharpCode.TreeView
protected override void OnSelectionChanged(SelectionChangedEventArgs e)
{
foreach (SharpTreeNode node in e.RemovedItems) {
SharpTreeNode.SelectedNodes.Remove(node);
node.IsSelected = false;
}
foreach (SharpTreeNode node in e.AddedItems) {
SharpTreeNode.SelectedNodes.Add(node);
}
if (IsKeyboardFocusWithin) {
foreach (SharpTreeNode node in e.RemovedItems) {
SharpTreeNode.ActiveNodes.Remove(node);
}
foreach (SharpTreeNode node in e.AddedItems) {
SharpTreeNode.ActiveNodes.Add(node);
}
node.IsSelected = true;
}
base.OnSelectionChanged(e);
}
protected override void OnPreviewGotKeyboardFocus(KeyboardFocusChangedEventArgs e)
{
foreach (SharpTreeNode node in SelectedItems) {
SharpTreeNode.ActiveNodes.Add(node);
}
}
protected override void OnPreviewLostKeyboardFocus(KeyboardFocusChangedEventArgs e)
{
foreach (SharpTreeNode node in SelectedItems) {
SharpTreeNode.ActiveNodes.Remove(node);
}
}
#endregion
#region Drag and Drop
/*
protected override void OnDragEnter(DragEventArgs e)
{
OnDragOver(e);
@ -521,7 +498,7 @@ namespace ICSharpCode.TreeView @@ -521,7 +498,7 @@ namespace ICSharpCode.TreeView
previewNodeView = null;
}
}
*/
#endregion
#region Cut / Copy / Paste / Delete Commands

38
SharpTreeView/SharpTreeViewItem.cs

@ -33,10 +33,10 @@ namespace ICSharpCode.TreeView @@ -33,10 +33,10 @@ namespace ICSharpCode.TreeView
{
switch (e.Key) {
case Key.F2:
if (SharpTreeNode.ActiveNodes.Count == 1 && Node.IsEditable) {
Node.IsEditing = true;
e.Handled = true;
}
// if (SharpTreeNode.ActiveNodes.Count == 1 && Node.IsEditable) {
// Node.IsEditing = true;
// e.Handled = true;
// }
break;
case Key.Escape:
Node.IsEditing = false;
@ -84,9 +84,9 @@ namespace ICSharpCode.TreeView @@ -84,9 +84,9 @@ namespace ICSharpCode.TreeView
if (Math.Abs(currentPoint.X - startPoint.X) >= SystemParameters.MinimumHorizontalDragDistance ||
Math.Abs(currentPoint.Y - startPoint.Y) >= SystemParameters.MinimumVerticalDragDistance) {
if (Node.InternalCanDrag()) {
Node.InternalDrag(this);
}
// if (Node.InternalCanDrag()) {
// Node.InternalDrag(this);
// }
}
}
}
@ -110,29 +110,5 @@ namespace ICSharpCode.TreeView @@ -110,29 +110,5 @@ namespace ICSharpCode.TreeView
}
#endregion
#region Drag and Drop
protected override void OnDragEnter(DragEventArgs e)
{
ParentTreeView.HandleDragEnter(this, e);
}
protected override void OnDragOver(DragEventArgs e)
{
ParentTreeView.HandleDragOver(this, e);
}
protected override void OnDrop(DragEventArgs e)
{
ParentTreeView.HandleDrop(this, e);
}
protected override void OnDragLeave(DragEventArgs e)
{
ParentTreeView.HandleDragLeave(this, e);
}
#endregion
}
}

190
SharpTreeView/TreeFlattener.cs

@ -2,148 +2,126 @@ @@ -2,148 +2,126 @@
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.Collections;
using System.Collections.Specialized;
using System.ComponentModel;
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
{
readonly SharpTreeNode root;
readonly bool includeRoot;
readonly object syncRoot = new object();
public TreeFlattener(SharpTreeNode root, bool includeRoot)
{
this.root = root;
this.includeRoot = includeRoot;
List = new ObservableCollection<SharpTreeNode>();
}
SharpTreeNode root;
bool includeRoot;
public ObservableCollection<SharpTreeNode> List { get; private set; }
public event NotifyCollectionChangedEventHandler CollectionChanged;
public void Start()
{
if (includeRoot) {
Add(root);
}
public void Stop()
{
}
public object this[int index] {
get {
if (index < 0 || index >= this.Count)
throw new ArgumentOutOfRangeException();
return SharpTreeNode.GetNodeByVisibleIndex(root, includeRoot ? index : index - 1);
}
else {
root.Children.CollectionChanged += node_ChildrenChanged;
set {
throw new NotSupportedException();
}
foreach (var node in root.ExpandedDescendants()) {
Add(node);
}
public int Count {
get {
return includeRoot ? root.totalListLength : root.totalListLength - 1;
}
}
public void Stop()
public int IndexOf(object item)
{
while (List.Count > 0) {
RemoveAt(0);
SharpTreeNode node = item as SharpTreeNode;
if (node != null && node.AncestorsAndSelf().Last() == root) {
if (includeRoot)
return SharpTreeNode.GetVisibleIndexForNode(node);
else
return SharpTreeNode.GetVisibleIndexForNode(node) - 1;
} else {
return -1;
}
}
void Add(SharpTreeNode node)
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 IList.Insert(int index, object item)
{
Insert(List.Count, node);
throw new NotSupportedException();
}
void Insert(int index, SharpTreeNode node)
void IList.RemoveAt(int index)
{
List.Insert(index, node);
node.PropertyChanged += node_PropertyChanged;
if (node.IsExpanded) {
node.Children.CollectionChanged += node_ChildrenChanged;
}
throw new NotSupportedException();
}
void RemoveAt(int index)
int IList.Add(object item)
{
var node = List[index];
List.RemoveAt(index);
node.PropertyChanged -= node_PropertyChanged;
if (node.IsExpanded) {
node.Children.CollectionChanged -= node_ChildrenChanged;
}
throw new NotSupportedException();
}
void ClearDescendants(SharpTreeNode node)
void IList.Clear()
{
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)
public bool Contains(object item)
{
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;
}
}
return IndexOf(item) >= 0;
}
void Insert(SharpTreeNode parent, int index, SharpTreeNode node)
public void CopyTo(Array array, int arrayIndex)
{
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);
foreach (object item in this)
array.SetValue(item, arrayIndex++);
}
void RemoveAt(SharpTreeNode parent, int index, SharpTreeNode node)
void IList.Remove(object item)
{
var i = List.IndexOf(node);
foreach (var child in node.ExpandedDescendantsAndSelf()) {
RemoveAt(i);
}
throw new NotSupportedException();
}
void node_ChildrenChanged(object sender, NotifyCollectionChangedEventArgs e)
public IEnumerator GetEnumerator()
{
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;
for (int i = 0; i < this.Count; i++) {
yield return this[i];
}
}
}

54
SharpTreeView/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