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