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

4
ILSpy/TreeNodes/AssemblyTreeNode.cs

@ -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); return nodes.All(n => n is AssemblyTreeNode);
} }*/
public override bool CanDelete() public override bool CanDelete()
{ {

83
SharpTreeView/FlatListTreeNode.cs

@ -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 @@
<SubType>Code</SubType> <SubType>Code</SubType>
</Compile> </Compile>
<Compile Include="Properties\GlobalAssemblyInfo.cs" /> <Compile Include="Properties\GlobalAssemblyInfo.cs" />
<Compile Include="FlatListTreeNode.cs" />
<Compile Include="SharpGridView.cs" /> <Compile Include="SharpGridView.cs" />
<Compile Include="SharpTreeNode.cs" /> <Compile Include="SharpTreeNode.cs" />
<Compile Include="SharpTreeNodeCollection.cs" /> <Compile Include="SharpTreeNodeCollection.cs" />
@ -88,6 +89,11 @@
<Compile Include="SharpTreeViewItem.cs" /> <Compile Include="SharpTreeViewItem.cs" />
<Compile Include="TreeFlattener.cs" /> <Compile Include="TreeFlattener.cs" />
<AppDesigner Include="Properties\" /> <AppDesigner Include="Properties\" />
<Compile Include="TreeTraversal.cs" />
</ItemGroup>
<ItemGroup>
<None Include="copyright.txt" />
<None Include="license.txt" />
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- 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 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Windows; using System.Windows;
@ -14,107 +15,123 @@ using System.Windows.Input;
namespace ICSharpCode.TreeView namespace ICSharpCode.TreeView
{ {
public class SharpTreeNode : INotifyPropertyChanged public partial class SharpTreeNode : INotifyPropertyChanged
{ {
SharpTreeNodeCollection modelChildren;
internal SharpTreeNode modelParent;
bool isVisible = true;
#region Main #region Main
static SharpTreeNode() public SharpTreeNode()
{ {
SelectedNodes = new HashSet<SharpTreeNode>();
ActiveNodes = new HashSet<SharpTreeNode>();
//StartCuttedDataWatcher();
} }
public static HashSet<SharpTreeNode> SelectedNodes { get; private set; } public SharpTreeNodeCollection Children {
public static HashSet<SharpTreeNode> ActiveNodes { get; private set; } get {
if (modelChildren == null)
static SharpTreeNode[] ActiveNodesArray modelChildren = new SharpTreeNodeCollection(this);
{ return modelChildren;
get
{
return ActiveNodes.ToArray();
} }
} }
public SharpTreeNode() public SharpTreeNode Parent {
{ get { return modelParent; }
Children = new SharpTreeNodeCollection(this);
} }
public SharpTreeNodeCollection Children { get; private set; }
public SharpTreeNode Parent { get; internal set; }
public virtual object Text public virtual object Text
{ {
get { return null; } get { return null; }
} }
public virtual object Icon public virtual object Icon
{ {
get { return null; } get { return null; }
} }
public virtual object ToolTip public virtual object ToolTip
{ {
get { return null; } get { return null; }
} }
public int Level public int Level
{ {
get { return Parent != null ? Parent.Level + 1 : 0; } get { return Parent != null ? Parent.Level + 1 : 0; }
} }
public bool IsRoot public bool IsRoot
{ {
get { return Parent == null; } get { return Parent == null; }
} }
//bool isSelected; bool isHidden;
//public bool IsSelected public bool IsHidden
//{ {
// get { return isSelected; } get { return isHidden; }
// set set {
// { if (isHidden != value) {
// isSelected = value; isHidden = value;
// RaisePropertyChanged("IsSelected"); RaisePropertyChanged("IsHidden");
// } }
//} }
}
bool isSelected;
public bool IsSelected
{
get { return isSelected; }
set {
if (isSelected != value) {
isSelected = value;
RaisePropertyChanged("IsSelected");
}
}
}
public virtual ContextMenu GetContextMenu() public virtual ContextMenu GetContextMenu()
{ {
return null; 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"); RaisePropertyChanged("ShowExpander");
RaiseIsLastChangedIfNeeded(e); RaiseIsLastChangedIfNeeded(e);
} }
#endregion #endregion
#region Expanding / LazyLoading #region Expanding / LazyLoading
public event EventHandler Collapsing;
public virtual object ExpandedIcon public virtual object ExpandedIcon
{ {
get { return Icon; } get { return Icon; }
} }
public virtual bool ShowExpander public virtual bool ShowExpander
{ {
get { return Children.Count > 0 || LazyLoading; } get { return Children.Count > 0 || LazyLoading; }
} }
//public virtual bool ShowLoading
//{
// get { return false; }
//}
bool isExpanded; bool isExpanded;
public bool IsExpanded public bool IsExpanded
{ {
get { return isExpanded; } get { return isExpanded; }
@ -125,18 +142,13 @@ namespace ICSharpCode.TreeView
if (isExpanded) { if (isExpanded) {
EnsureLazyChildren(); EnsureLazyChildren();
} }
else {
if (Collapsing != null) {
Collapsing(this, EventArgs.Empty);
}
}
RaisePropertyChanged("IsExpanded"); RaisePropertyChanged("IsExpanded");
} }
} }
} }
bool lazyLoading; bool lazyLoading;
public bool LazyLoading public bool LazyLoading
{ {
get { return lazyLoading; } get { return lazyLoading; }
@ -155,12 +167,12 @@ namespace ICSharpCode.TreeView
{ {
get { return Icon != null; } get { return Icon != null; }
} }
protected virtual void LoadChildren() protected virtual void LoadChildren()
{ {
throw new NotSupportedException(GetType().Name + " does not support lazy loading"); throw new NotSupportedException(GetType().Name + " does not support lazy loading");
} }
/// <summary> /// <summary>
/// Ensures the children were initialized (loads children if lazy loading is enabled) /// Ensures the children were initialized (loads children if lazy loading is enabled)
/// </summary> /// </summary>
@ -171,49 +183,31 @@ namespace ICSharpCode.TreeView
LoadChildren(); LoadChildren();
} }
} }
#endregion #endregion
#region Ancestors / Descendants #region Ancestors / Descendants
public IEnumerable<SharpTreeNode> Descendants() public IEnumerable<SharpTreeNode> Descendants()
{ {
foreach (var child in Children) { return TreeTraversal.PreOrder(this.Children, n => n.Children);
foreach (var child2 in child.DescendantsAndSelf()) {
yield return child2;
}
}
} }
public IEnumerable<SharpTreeNode> DescendantsAndSelf() public IEnumerable<SharpTreeNode> DescendantsAndSelf()
{ {
yield return this; return TreeTraversal.PreOrder(this, n => n.Children);
foreach (var child in Descendants()) {
yield return child;
}
} }
public IEnumerable<SharpTreeNode> ExpandedDescendants() public IEnumerable<SharpTreeNode> ExpandedDescendants()
{ {
foreach (var child in Children) { return TreeTraversal.PreOrder(this.Children, n => n.IsExpanded ? n.Children : null);
foreach (var child2 in child.ExpandedDescendantsAndSelf()) {
yield return child2;
}
}
} }
public IEnumerable<SharpTreeNode> ExpandedDescendantsAndSelf() public IEnumerable<SharpTreeNode> ExpandedDescendantsAndSelf()
{ {
yield return this; return TreeTraversal.PreOrder(this, n => n.IsExpanded ? n.Children : null);
if (IsExpanded) {
foreach (var child in Children) {
foreach (var child2 in child.ExpandedDescendantsAndSelf()) {
yield return child2;
}
}
}
} }
public IEnumerable<SharpTreeNode> Ancestors() public IEnumerable<SharpTreeNode> Ancestors()
{ {
var node = this; var node = this;
@ -222,7 +216,7 @@ namespace ICSharpCode.TreeView
node = node.Parent; node = node.Parent;
} }
} }
public IEnumerable<SharpTreeNode> AncestorsAndSelf() public IEnumerable<SharpTreeNode> AncestorsAndSelf()
{ {
yield return this; yield return this;
@ -230,18 +224,18 @@ namespace ICSharpCode.TreeView
yield return node; yield return node;
} }
} }
#endregion #endregion
#region Editing #region Editing
public virtual bool IsEditable public virtual bool IsEditable
{ {
get { return false; } get { return false; }
} }
bool isEditing; bool isEditing;
public bool IsEditing public bool IsEditing
{ {
get { return isEditing; } get { return isEditing; }
@ -253,42 +247,39 @@ namespace ICSharpCode.TreeView
} }
} }
} }
public virtual string LoadEditText() public virtual string LoadEditText()
{ {
return null; return null;
} }
public virtual bool SaveEditText(string value) public virtual bool SaveEditText(string value)
{ {
return true; return true;
} }
#endregion #endregion
#region Checkboxes #region Checkboxes
public virtual bool IsCheckable public virtual bool IsCheckable {
{
get { return false; } get { return false; }
} }
bool? isChecked; bool? isChecked;
public bool? IsChecked public bool? IsChecked {
{
get { return isChecked; } get { return isChecked; }
set set {
{
SetIsChecked(value, true); SetIsChecked(value, true);
} }
} }
void SetIsChecked(bool? value, bool update) void SetIsChecked(bool? value, bool update)
{ {
if (isChecked != value) { if (isChecked != value) {
isChecked = value; isChecked = value;
if (update) { if (update) {
if (IsChecked != null) { if (IsChecked != null) {
foreach (var child in Descendants()) { foreach (var child in Descendants()) {
@ -297,7 +288,7 @@ namespace ICSharpCode.TreeView
} }
} }
} }
foreach (var parent in Ancestors()) { foreach (var parent in Ancestors()) {
if (parent.IsCheckable) { if (parent.IsCheckable) {
if (!parent.TryValueForIsChecked(true)) { if (!parent.TryValueForIsChecked(true)) {
@ -308,11 +299,11 @@ namespace ICSharpCode.TreeView
} }
} }
} }
RaisePropertyChanged("IsChecked"); RaisePropertyChanged("IsChecked");
} }
} }
bool TryValueForIsChecked(bool? value) bool TryValueForIsChecked(bool? value)
{ {
if (Children.Where(n => n.IsCheckable).All(n => n.IsChecked == value)) { if (Children.Where(n => n.IsCheckable).All(n => n.IsChecked == value)) {
@ -321,245 +312,245 @@ namespace ICSharpCode.TreeView
} }
return false; return false;
} }
#endregion #endregion
#region Cut / Copy / Paste / Delete #region Cut / Copy / Paste / Delete
public bool IsCut { get { return false; } } public bool IsCut { get { return false; } }
/* /*
static List<SharpTreeNode> cuttedNodes = new List<SharpTreeNode>(); static List<SharpTreeNode> cuttedNodes = new List<SharpTreeNode>();
static IDataObject cuttedData; static IDataObject cuttedData;
static EventHandler requerySuggestedHandler; // for weak event static EventHandler requerySuggestedHandler; // for weak event
static void StartCuttedDataWatcher() static void StartCuttedDataWatcher()
{ {
requerySuggestedHandler = new EventHandler(CommandManager_RequerySuggested); requerySuggestedHandler = new EventHandler(CommandManager_RequerySuggested);
CommandManager.RequerySuggested += requerySuggestedHandler; CommandManager.RequerySuggested += requerySuggestedHandler;
}
static void CommandManager_RequerySuggested(object sender, EventArgs e)
{
if (cuttedData != null && !Clipboard.IsCurrent(cuttedData)) {
ClearCuttedData();
} }
}
static void CommandManager_RequerySuggested(object sender, EventArgs e)
static void ClearCuttedData() {
{ if (cuttedData != null && !Clipboard.IsCurrent(cuttedData)) {
foreach (var node in cuttedNodes) { ClearCuttedData();
node.IsCut = false; }
} }
cuttedNodes.Clear();
cuttedData = null; static void ClearCuttedData()
}
//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; foreach (var node in cuttedNodes) {
RaisePropertyChanged("IsCut"); node.IsCut = false;
}
cuttedNodes.Clear();
cuttedData = null;
} }
}
//static public IEnumerable<SharpTreeNode> PurifyNodes(IEnumerable<SharpTreeNode> nodes)
internal bool InternalCanCut() //{
{ // var list = nodes.ToList();
return InternalCanCopy() && InternalCanDelete(); // var array = list.ToArray();
} // foreach (var node1 in array) {
// foreach (var node2 in array) {
internal void InternalCut() // if (node1.Descendants().Contains(node2)) {
{ // list.Remove(node2);
ClearCuttedData(); // }
cuttedData = Copy(ActiveNodesArray); // }
Clipboard.SetDataObject(cuttedData); // }
// return list;
foreach (var node in ActiveNodes) { //}
node.IsCut = true;
cuttedNodes.Add(node); bool isCut;
public bool IsCut
{
get { return isCut; }
private set
{
isCut = value;
RaisePropertyChanged("IsCut");
}
} }
}
internal bool InternalCanCut()
internal bool InternalCanCopy() {
{ return InternalCanCopy() && InternalCanDelete();
return CanCopy(ActiveNodesArray); }
}
internal void InternalCut()
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(); 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() public virtual bool CanDelete()
{ {
return false; return false;
} }
public virtual void Delete() public virtual void Delete()
{ {
throw new NotSupportedException(GetType().Name + " does not support deletion"); throw new NotSupportedException(GetType().Name + " does not support deletion");
} }
public virtual void DeleteCore() public virtual void DeleteCore()
{ {
throw new NotSupportedException(GetType().Name + " does not support deletion"); throw new NotSupportedException(GetType().Name + " does not support deletion");
} }
public virtual IDataObject Copy(SharpTreeNode[] nodes) public virtual IDataObject Copy(SharpTreeNode[] nodes)
{ {
throw new NotSupportedException(GetType().Name + " does not support copy/paste or drag'n'drop"); throw new NotSupportedException(GetType().Name + " does not support copy/paste or drag'n'drop");
} }
/* /*
public virtual bool CanCopy(SharpTreeNode[] nodes) public virtual bool CanCopy(SharpTreeNode[] nodes)
{ {
return false; return false;
} }
public virtual bool CanPaste(IDataObject data) public virtual bool CanPaste(IDataObject data)
{ {
return false; return false;
} }
public virtual void Paste(IDataObject data) public virtual void Paste(IDataObject data)
{ {
EnsureLazyChildren(); EnsureLazyChildren();
Drop(data, Children.Count, DropEffect.Copy); Drop(data, Children.Count, DropEffect.Copy);
} }
*/ */
#endregion #endregion
#region Drag and Drop #region Drag and Drop
/*
internal bool InternalCanDrag() internal bool InternalCanDrag()
{ {
return CanDrag(ActiveNodesArray); 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;
} }
var finalEffect = GetFinalEffect(e, index); internal void InternalDrag(DependencyObject dragSource)
Drop(e.Data, index, finalEffect); {
DragDrop.DoDragDrop(dragSource, Copy(ActiveNodesArray), DragDropEffects.All);
if (finalEffect == DropEffect.Move) {
foreach (SharpTreeNode node in ActiveNodesArray)
node.DeleteCore();
} }
}
internal bool InternalCanDrop(DragEventArgs e, int index)
DropEffect GetFinalEffect(DragEventArgs e, int index) {
{ var finalEffect = GetFinalEffect(e, index);
var requestedEffect = GetDropEffect(e); e.Effects = GetDragDropEffects(finalEffect);
var result = CanDrop(e.Data, requestedEffect); return finalEffect != DropEffect.None;
if (result == DropEffect.Move) { }
if (!ActiveNodesArray.All(n => n.CanDelete())) {
return 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;
} DropEffect GetFinalEffect(DragEventArgs e, int index)
{
static DropEffect GetDropEffect(DragEventArgs e) var requestedEffect = GetDropEffect(e);
{ var result = CanDrop(e.Data, requestedEffect);
if (e.Data != null) { if (result == DropEffect.Move) {
var all = DragDropKeyStates.ControlKey | DragDropKeyStates.ShiftKey | DragDropKeyStates.AltKey; if (!ActiveNodesArray.All(n => n.CanDelete())) {
return DropEffect.None;
if ((e.KeyStates & all) == DragDropKeyStates.ControlKey) { }
return DropEffect.Copy;
} }
if ((e.KeyStates & all) == DragDropKeyStates.AltKey) { return result;
return DropEffect.Link; }
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.None;
return DropEffect.Link; }
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;
} public virtual bool CanDrag(SharpTreeNode[] nodes)
{
static DragDropEffects GetDragDropEffects(DropEffect effect) return false;
{
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 DropEffect CanDrop(IDataObject data, DropEffect requestedEffect)
{
public virtual bool CanDrag(SharpTreeNode[] nodes) return DropEffect.None;
{ }
return false;
} public virtual void Drop(IDataObject data, int index, DropEffect finalEffect)
{
public virtual DropEffect CanDrop(IDataObject data, DropEffect requestedEffect) throw new NotSupportedException(GetType().Name + " does not support Drop()");
{ }
return DropEffect.None; */
}
public virtual void Drop(IDataObject data, int index, DropEffect finalEffect)
{
throw new NotSupportedException(GetType().Name + " does not support Drop()");
}
#endregion #endregion
#region IsLast (for TreeView lines) #region IsLast (for TreeView lines)
public bool IsLast public bool IsLast
{ {
get get
@ -568,7 +559,7 @@ namespace ICSharpCode.TreeView
Parent.Children[Parent.Children.Count - 1] == this; Parent.Children[Parent.Children.Count - 1] == this;
} }
} }
void RaiseIsLastChangedIfNeeded(NotifyCollectionChangedEventArgs e) void RaiseIsLastChangedIfNeeded(NotifyCollectionChangedEventArgs e)
{ {
switch (e.Action) { switch (e.Action) {
@ -589,20 +580,20 @@ namespace ICSharpCode.TreeView
break; break;
} }
} }
#endregion #endregion
#region INotifyPropertyChanged Members #region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged; public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string name) public void RaisePropertyChanged(string name)
{ {
if (PropertyChanged != null) { if (PropertyChanged != null) {
PropertyChanged(this, new PropertyChangedEventArgs(name)); PropertyChanged(this, new PropertyChangedEventArgs(name));
} }
} }
#endregion #endregion
/// <summary> /// <summary>

150
SharpTreeView/SharpTreeNodeCollection.cs

@ -3,6 +3,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
@ -10,44 +11,141 @@ using System.Collections.Specialized;
namespace ICSharpCode.TreeView 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) public SharpTreeNodeCollection(SharpTreeNode parent)
{ {
Parent = parent; this.parent = parent;
} }
public SharpTreeNode Parent { get; private set; } public event NotifyCollectionChangedEventHandler CollectionChanged;
protected override void InsertItem(int index, SharpTreeNode node) void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{ {
node.Parent = Parent; Debug.Assert(!isRaisingEvent);
base.InsertItem(index, node); 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]; if (isRaisingEvent)
node.Parent = null; throw new InvalidOperationException();
base.RemoveItem(index);
} }
protected override void ClearItems() void ThrowIfValueIsNullOrHasParent(SharpTreeNode node)
{ {
/*foreach (var node in this) { if (node == null)
node.Parent = 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); return list.GetEnumerator();
Parent.OnChildrenChanged(e);
} }
} }
} }

17
SharpTreeView/SharpTreeNodeView.cs

@ -20,7 +20,7 @@ namespace ICSharpCode.TreeView
static SharpTreeNodeView() static SharpTreeNodeView()
{ {
DefaultStyleKeyProperty.OverrideMetadata(typeof(SharpTreeNodeView), DefaultStyleKeyProperty.OverrideMetadata(typeof(SharpTreeNodeView),
new FrameworkPropertyMetadata(typeof(SharpTreeNodeView))); new FrameworkPropertyMetadata(typeof(SharpTreeNodeView)));
} }
public static readonly DependencyProperty TextBackgroundProperty = public static readonly DependencyProperty TextBackgroundProperty =
@ -71,14 +71,12 @@ namespace ICSharpCode.TreeView
void UpdateDataContext(SharpTreeNode oldNode, SharpTreeNode newNode) void UpdateDataContext(SharpTreeNode oldNode, SharpTreeNode newNode)
{ {
if (newNode != null) { if (newNode != null) {
newNode.Collapsing += Node_Collapsing;
newNode.PropertyChanged += Node_PropertyChanged; newNode.PropertyChanged += Node_PropertyChanged;
if (Template != null) { if (Template != null) {
UpdateTemplate(); UpdateTemplate();
} }
} }
if (oldNode != null) { if (oldNode != null) {
oldNode.Collapsing -= Node_Collapsing;
oldNode.PropertyChanged -= Node_PropertyChanged; oldNode.PropertyChanged -= Node_PropertyChanged;
} }
} }
@ -87,8 +85,7 @@ namespace ICSharpCode.TreeView
{ {
if (e.PropertyName == "IsEditing") { if (e.PropertyName == "IsEditing") {
OnIsEditingChanged(); OnIsEditingChanged();
} } else if (e.PropertyName == "IsLast") {
else if (e.PropertyName == "IsLast") {
if (ParentTreeView.ShowLines) { if (ParentTreeView.ShowLines) {
foreach (var child in Node.ExpandedDescendantsAndSelf()) { foreach (var child in Node.ExpandedDescendantsAndSelf()) {
var container = ParentTreeView.ItemContainerGenerator.ContainerFromItem(child) as SharpTreeViewItem; var container = ParentTreeView.ItemContainerGenerator.ContainerFromItem(child) as SharpTreeViewItem;
@ -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() void OnIsEditingChanged()
{ {
var textEditorContainer = Template.FindName("textEditorContainer", this) as Border; var textEditorContainer = Template.FindName("textEditorContainer", this) as Border;
@ -146,6 +141,6 @@ namespace ICSharpCode.TreeView
if (result < 0) if (result < 0)
throw new InvalidOperationException(); throw new InvalidOperationException();
return result; return result;
} }
} }
} }

37
SharpTreeView/SharpTreeView.cs

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

38
SharpTreeView/SharpTreeViewItem.cs

@ -33,10 +33,10 @@ namespace ICSharpCode.TreeView
{ {
switch (e.Key) { switch (e.Key) {
case Key.F2: case Key.F2:
if (SharpTreeNode.ActiveNodes.Count == 1 && Node.IsEditable) { // if (SharpTreeNode.ActiveNodes.Count == 1 && Node.IsEditable) {
Node.IsEditing = true; // Node.IsEditing = true;
e.Handled = true; // e.Handled = true;
} // }
break; break;
case Key.Escape: case Key.Escape:
Node.IsEditing = false; Node.IsEditing = false;
@ -84,9 +84,9 @@ namespace ICSharpCode.TreeView
if (Math.Abs(currentPoint.X - startPoint.X) >= SystemParameters.MinimumHorizontalDragDistance || 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()) { // if (Node.InternalCanDrag()) {
Node.InternalDrag(this); // Node.InternalDrag(this);
} // }
} }
} }
} }
@ -110,29 +110,5 @@ namespace ICSharpCode.TreeView
} }
#endregion #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 @@
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) // This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System; using System;
using System.Collections.Generic; using System.Collections;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Collections.Specialized;
namespace ICSharpCode.TreeView 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) public TreeFlattener(SharpTreeNode root, bool includeRoot)
{ {
this.root = root; this.root = root;
this.includeRoot = includeRoot; this.includeRoot = includeRoot;
List = new ObservableCollection<SharpTreeNode>();
} }
SharpTreeNode root; public event NotifyCollectionChangedEventHandler CollectionChanged;
bool includeRoot;
public ObservableCollection<SharpTreeNode> List { get; private set; }
public void Start() 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 { set {
root.Children.CollectionChanged += node_ChildrenChanged; 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) { SharpTreeNode node = item as SharpTreeNode;
RemoveAt(0); 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); throw new NotSupportedException();
node.PropertyChanged += node_PropertyChanged;
if (node.IsExpanded) {
node.Children.CollectionChanged += node_ChildrenChanged;
}
} }
void RemoveAt(int index) int IList.Add(object item)
{ {
var node = List[index]; throw new NotSupportedException();
List.RemoveAt(index);
node.PropertyChanged -= node_PropertyChanged;
if (node.IsExpanded) {
node.Children.CollectionChanged -= node_ChildrenChanged;
}
} }
void ClearDescendants(SharpTreeNode node) void IList.Clear()
{ {
var index = List.IndexOf(node); throw new NotSupportedException();
while (index + 1 < List.Count && List[index + 1].Level > node.Level) {
RemoveAt(index + 1);
}
} }
void node_PropertyChanged(object sender, PropertyChangedEventArgs e) public bool Contains(object item)
{ {
if (e.PropertyName == "IsExpanded") { return IndexOf(item) >= 0;
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;
}
}
} }
void Insert(SharpTreeNode parent, int index, SharpTreeNode node) public void CopyTo(Array array, int arrayIndex)
{ {
int finalIndex = 0; foreach (object item in this)
if (index > 0) { array.SetValue(item, arrayIndex++);
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);
} }
void RemoveAt(SharpTreeNode parent, int index, SharpTreeNode node) void IList.Remove(object item)
{ {
var i = List.IndexOf(node); throw new NotSupportedException();
foreach (var child in node.ExpandedDescendantsAndSelf()) {
RemoveAt(i);
}
} }
void node_ChildrenChanged(object sender, NotifyCollectionChangedEventArgs e) public IEnumerator GetEnumerator()
{ {
var collection = sender as SharpTreeNodeCollection; for (int i = 0; i < this.Count; i++) {
var parent = collection.Parent; yield return this[i];
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;
} }
} }
} }

54
SharpTreeView/TreeTraversal.cs

@ -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