|
//------------------------------------------------------------------------------
// <copyright file="TreeNodeCollection.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
/*
*/
namespace System.Windows.Forms {
using System.Runtime.InteropServices;
using System;
using System.Collections;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Diagnostics;
using System.Drawing.Design;
using System.Globalization;
/// <include file='doc\TreeNodeCollection.uex' path='docs/doc[@for="TreeNodeCollection"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[
Editor("System.Windows.Forms.Design.TreeNodeCollectionEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor))
]
public class TreeNodeCollection : IList {
private TreeNode owner;
/// A caching mechanism for key accessor
/// We use an index here rather than control so that we don't have lifetime
/// issues by holding on to extra references.
private int lastAccessedIndex = -1;
//this index is used to optimize performance of AddRange
//items are added from last to first after this index
//(to work around TV_INSertItem comctl32 perf issue with consecutive adds in the end of the list)
private int fixedIndex = -1;
internal TreeNodeCollection(TreeNode owner) {
this.owner = owner;
}
/// <include file='doc\TreeNodeCollection.uex' path='docs/doc[@for="TreeNodeCollection.FixedIndex"]/*' />
/// <internalonly/>
internal int FixedIndex
{
get {
return fixedIndex;
}
set {
fixedIndex = value;
}
}
/// <include file='doc\TreeNodeCollection.uex' path='docs/doc[@for="TreeNodeCollection.this"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public virtual TreeNode this[int index] {
get {
if (index < 0 || index >= owner.childCount) {
throw new ArgumentOutOfRangeException("index");
}
return owner.children[index];
}
set {
if (index < 0 || index >= owner.childCount)
throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture)));
value.parent = owner;
value.index = index;
owner.children[index] = value;
value.Realize(false);
}
}
/// <include file='doc\TreeNodeCollection.uex' path='docs/doc[@for="TreeNodeCollection.IList.this"]/*' />
/// <internalonly/>
object IList.this[int index] {
get {
return this[index];
}
set {
if (value is TreeNode) {
this[index] = (TreeNode)value;
}
else {
throw new ArgumentException(SR.GetString(SR.TreeNodeCollectionBadTreeNode), "value");
}
}
}
/// <include file='doc\TreeNodeCollection.uex' path='docs/doc[@for="TreeNodeCollection.this"]/*' />
/// <devdoc>
/// <para>Retrieves the child control with the specified key.</para>
/// </devdoc>
public virtual TreeNode this[string key] {
get {
// We do not support null and empty string as valid keys.
if (string.IsNullOrEmpty(key)){
return null;
}
// Search for the key in our collection
int index = IndexOfKey(key);
if (IsValidIndex(index)) {
return this[index];
}
else {
return null;
}
}
}
/// <include file='doc\TreeNodeCollection.uex' path='docs/doc[@for="TreeNodeCollection.Count"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
// VSWhidbey 152051: Make this property available to Intellisense. (Removed the EditorBrowsable attribute.)
[Browsable(false)]
public int Count {
get {
return owner.childCount;
}
}
/// <include file='doc\TreeNodeCollection.uex' path='docs/doc[@for="TreeNodeCollection.ICollection.SyncRoot"]/*' />
/// <internalonly/>
object ICollection.SyncRoot {
get {
return this;
}
}
/// <include file='doc\TreeNodeCollection.uex' path='docs/doc[@for="TreeNodeCollection.ICollection.IsSynchronized"]/*' />
/// <internalonly/>
bool ICollection.IsSynchronized {
get {
return false;
}
}
/// <include file='doc\TreeNodeCollection.uex' path='docs/doc[@for="TreeNodeCollection.IList.IsFixedSize"]/*' />
/// <internalonly/>
bool IList.IsFixedSize {
get {
return false;
}
}
/// <include file='doc\TreeNodeCollection.uex' path='docs/doc[@for="TreeNodeCollection.IsReadOnly"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public bool IsReadOnly {
get {
return false;
}
}
/// <include file='doc\TreeNodeCollection.uex' path='docs/doc[@for="TreeNodeCollection.Add"]/*' />
/// <devdoc>
/// Creates a new child node under this node. Child node is positioned after siblings.
/// </devdoc>
public virtual TreeNode Add(string text) {
TreeNode tn = new TreeNode(text);
Add(tn);
return tn;
}
// <-- NEW ADD OVERLOADS IN WHIDBEY
/// <include file='doc\TreeNodeCollection.uex' path='docs/doc[@for="TreeNodeCollection.Add1"]/*' />
/// <devdoc>
/// Creates a new child node under this node. Child node is positioned after siblings.
/// </devdoc>
public virtual TreeNode Add(string key, string text) {
TreeNode tn = new TreeNode(text);
tn.Name = key;
Add(tn);
return tn;
}
/// <include file='doc\TreeNodeCollection.uex' path='docs/doc[@for="TreeNodeCollection.Add2"]/*' />
/// <devdoc>
/// Creates a new child node under this node. Child node is positioned after siblings.
/// </devdoc>
public virtual TreeNode Add(string key, string text, int imageIndex) {
TreeNode tn = new TreeNode(text);
tn.Name = key;
tn.ImageIndex = imageIndex;
Add(tn);
return tn;
}
/// <include file='doc\TreeNodeCollection.uex' path='docs/doc[@for="TreeNodeCollection.Add3"]/*' />
/// <devdoc>
/// Creates a new child node under this node. Child node is positioned after siblings.
/// </devdoc>
public virtual TreeNode Add(string key, string text, string imageKey) {
TreeNode tn = new TreeNode(text);
tn.Name = key;
tn.ImageKey = imageKey;
Add(tn);
return tn;
}
/// <include file='doc\TreeNodeCollection.uex' path='docs/doc[@for="TreeNodeCollection.Add4"]/*' />
/// <devdoc>
/// Creates a new child node under this node. Child node is positioned after siblings.
/// </devdoc>
public virtual TreeNode Add(string key, string text, int imageIndex, int selectedImageIndex) {
TreeNode tn = new TreeNode(text, imageIndex, selectedImageIndex);
tn.Name = key;
Add(tn);
return tn;
}
/// <include file='doc\TreeNodeCollection.uex' path='docs/doc[@for="TreeNodeCollection.Add5"]/*' />
/// <devdoc>
/// Creates a new child node under this node. Child node is positioned after siblings.
/// </devdoc>
public virtual TreeNode Add(string key, string text, string imageKey, string selectedImageKey) {
TreeNode tn = new TreeNode(text);
tn.Name = key;
tn.ImageKey = imageKey;
tn.SelectedImageKey = selectedImageKey;
Add(tn);
return tn;
}
// END - NEW ADD OVERLOADS IN WHIDBEY -->
/// <include file='doc\TreeNodeCollection.uex' path='docs/doc[@for="TreeNodeCollection.AddRange"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public virtual void AddRange(TreeNode[] nodes) {
if (nodes == null) {
throw new ArgumentNullException("nodes");
}
if (nodes.Length == 0)
return;
TreeView tv = owner.TreeView;
if (tv != null && nodes.Length > TreeNode.MAX_TREENODES_OPS) {
tv.BeginUpdate();
}
owner.Nodes.FixedIndex = owner.childCount;
owner.EnsureCapacity(nodes.Length);
for (int i = nodes.Length-1 ; i >= 0; i--) {
AddInternal(nodes[i],i);
}
owner.Nodes.FixedIndex = -1;
if (tv != null && nodes.Length > TreeNode.MAX_TREENODES_OPS) {
tv.EndUpdate();
}
}
/// <include file='doc\TreeNodeCollection.uex' path='docs/doc[@for="TreeNodeCollection.Find"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public TreeNode[] Find (string key, bool searchAllChildren) {
ArrayList foundNodes = FindInternal(key, searchAllChildren, this, new ArrayList());
//
TreeNode[] stronglyTypedFoundNodes = new TreeNode[foundNodes.Count];
foundNodes.CopyTo(stronglyTypedFoundNodes, 0);
return stronglyTypedFoundNodes;
}
private ArrayList FindInternal(string key, bool searchAllChildren, TreeNodeCollection treeNodeCollectionToLookIn, ArrayList foundTreeNodes) {
if ((treeNodeCollectionToLookIn == null) || (foundTreeNodes == null)) {
return null;
}
// Perform breadth first search - as it's likely people will want tree nodes belonging
// to the same parent close to each other.
for (int i = 0; i < treeNodeCollectionToLookIn.Count; i++) {
if (treeNodeCollectionToLookIn[i] == null){
continue;
}
if (WindowsFormsUtils.SafeCompareStrings(treeNodeCollectionToLookIn[i].Name, key, /* ignoreCase = */ true)) {
foundTreeNodes.Add(treeNodeCollectionToLookIn[i]);
}
}
// Optional recurive search for controls in child collections.
if (searchAllChildren){
for (int i = 0; i < treeNodeCollectionToLookIn.Count; i++) {
if (treeNodeCollectionToLookIn[i] == null){
continue;
}
if ((treeNodeCollectionToLookIn[i].Nodes != null) && treeNodeCollectionToLookIn[i].Nodes.Count > 0){
// if it has a valid child collecion, append those results to our collection
foundTreeNodes = FindInternal(key, searchAllChildren, treeNodeCollectionToLookIn[i].Nodes, foundTreeNodes);
}
}
}
return foundTreeNodes;
}
/// <include file='doc\TreeNodeCollection.uex' path='docs/doc[@for="TreeNodeCollection.Add1"]/*' />
/// <devdoc>
/// Adds a new child node to this node. Child node is positioned after siblings.
/// </devdoc>
public virtual int Add(TreeNode node) {
return AddInternal(node, 0);
}
private int AddInternal(TreeNode node, int delta) {
if (node == null) {
throw new ArgumentNullException("node");
}
if (node.handle != IntPtr.Zero)
throw new ArgumentException(SR.GetString(SR.OnlyOneControl, node.Text), "node");
// If the TreeView is sorted, index is ignored
TreeView tv = owner.TreeView;
if (tv != null && tv.Sorted) {
return owner.AddSorted(node);
}
node.parent = owner;
int fixedIndex = owner.Nodes.FixedIndex;
if (fixedIndex != -1) {
node.index = fixedIndex + delta;
}
else {
//if fixedIndex != -1 capacity was ensured by AddRange
Debug.Assert(delta == 0,"delta should be 0");
owner.EnsureCapacity(1);
node.index = owner.childCount;
}
owner.children[node.index] = node;
owner.childCount++;
node.Realize(false);
if (tv != null && node == tv.selectedNode)
tv.SelectedNode = node; // communicate this to the handle
if (tv != null && tv.TreeViewNodeSorter != null) {
tv.Sort();
}
return node.index;
}
/// <include file='doc\TreeNodeCollection.uex' path='docs/doc[@for="TreeNodeCollection.IList.Add"]/*' />
/// <internalonly/>
int IList.Add(object node) {
if (node == null) {
throw new ArgumentNullException("node");
}
else if (node is TreeNode) {
return Add((TreeNode)node);
}
else
{
return Add(node.ToString()).index;
}
}
/// <include file='doc\TreeNodeCollection.uex' path='docs/doc[@for="TreeNodeCollection.Contains"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public bool Contains(TreeNode node) {
return IndexOf(node) != -1;
}
/// <include file='doc\TreeNodeCollection.uex' path='docs/doc[@for="TreeNodeCollection.ContainsKey"]/*' />
/// <devdoc>
/// <para>Returns true if the collection contains an item with the specified key, false otherwise.</para>
/// </devdoc>
public virtual bool ContainsKey(string key) {
return IsValidIndex(IndexOfKey(key));
}
/// <include file='doc\TreeNodeCollection.uex' path='docs/doc[@for="TreeNodeCollection.IList.Contains"]/*' />
/// <internalonly/>
bool IList.Contains(object node) {
if (node is TreeNode) {
return Contains((TreeNode)node);
}
else {
return false;
}
}
/// <include file='doc\TreeNodeCollection.uex' path='docs/doc[@for="TreeNodeCollection.IndexOf"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public int IndexOf(TreeNode node) {
for(int index=0; index < Count; ++index) {
if (this[index] == node) {
return index;
}
}
return -1;
}
/// <include file='doc\TreeNodeCollection.uex' path='docs/doc[@for="TreeNodeCollection.IList.IndexOf"]/*' />
/// <internalonly/>
int IList.IndexOf(object node) {
if (node is TreeNode) {
return IndexOf((TreeNode)node);
}
else {
return -1;
}
}
/// <include file='doc\TreeNodeCollection.uex' path='docs/doc[@for="TreeNodeCollection.this"]/*' />
/// <devdoc>
/// <para>The zero-based index of the first occurrence of value within the entire CollectionBase, if found; otherwise, -1.</para>
/// </devdoc>
public virtual int IndexOfKey(String key) {
// Step 0 - Arg validation
if (string.IsNullOrEmpty(key)){
return -1; // we dont support empty or null keys.
}
// step 1 - check the last cached item
if (IsValidIndex(lastAccessedIndex))
{
if (WindowsFormsUtils.SafeCompareStrings(this[lastAccessedIndex].Name, key, /* ignoreCase = */ true)) {
return lastAccessedIndex;
}
}
// step 2 - search for the item
for (int i = 0; i < this.Count; i ++) {
if (WindowsFormsUtils.SafeCompareStrings(this[i].Name, key, /* ignoreCase = */ true)) {
lastAccessedIndex = i;
return i;
}
}
// step 3 - we didn't find it. Invalidate the last accessed index and return -1.
lastAccessedIndex = -1;
return -1;
}
/// <include file='doc\TreeNodeCollection.uex' path='docs/doc[@for="TreeNodeCollection.Insert"]/*' />
/// <devdoc>
/// Inserts a new child node on this node. Child node is positioned as specified by index.
/// </devdoc>
public virtual void Insert(int index, TreeNode node) {
if (node.handle != IntPtr.Zero)
throw new ArgumentException(SR.GetString(SR.OnlyOneControl, node.Text), "node");
// If the TreeView is sorted, index is ignored
TreeView tv = owner.TreeView;
if (tv != null && tv.Sorted) {
owner.AddSorted(node);
return;
}
if (index < 0) index = 0;
if (index > owner.childCount) index = owner.childCount;
owner.InsertNodeAt(index, node);
}
/// <include file='doc\TreeNodeCollection.uex' path='docs/doc[@for="TreeNodeCollection.IList.Insert"]/*' />
/// <internalonly/>
void IList.Insert(int index, object node) {
if (node is TreeNode) {
Insert(index, (TreeNode)node);
}
else {
throw new ArgumentException(SR.GetString(SR.TreeNodeCollectionBadTreeNode), "node");
}
}
// <-- NEW INSERT OVERLOADS IN WHIDBEY
/// <include file='doc\TreeNodeCollection.uex' path='docs/doc[@for="TreeNodeCollection.Insert1"]/*' />
/// <devdoc>
/// Inserts a new child node on this node. Child node is positioned as specified by index.
/// </devdoc>
public virtual TreeNode Insert(int index, string text) {
TreeNode tn = new TreeNode(text);
Insert(index, tn);
return tn;
}
/// <include file='doc\TreeNodeCollection.uex' path='docs/doc[@for="TreeNodeCollection.Insert2"]/*' />
/// <devdoc>
/// Inserts a new child node on this node. Child node is positioned as specified by index.
/// </devdoc>
public virtual TreeNode Insert(int index, string key, string text) {
TreeNode tn = new TreeNode(text);
tn.Name = key;
Insert(index, tn);
return tn;
}
/// <include file='doc\TreeNodeCollection.uex' path='docs/doc[@for="TreeNodeCollection.Insert3"]/*' />
/// <devdoc>
/// Inserts a new child node on this node. Child node is positioned as specified by index.
/// </devdoc>
public virtual TreeNode Insert(int index, string key, string text, int imageIndex) {
TreeNode tn = new TreeNode(text);
tn.Name = key;
tn.ImageIndex = imageIndex;
Insert(index, tn);
return tn;
}
/// <include file='doc\TreeNodeCollection.uex' path='docs/doc[@for="TreeNodeCollection.Insert4"]/*' />
/// <devdoc>
/// Inserts a new child node on this node. Child node is positioned as specified by index.
/// </devdoc>
public virtual TreeNode Insert(int index, string key, string text, string imageKey) {
TreeNode tn = new TreeNode(text);
tn.Name = key;
tn.ImageKey = imageKey;
Insert(index, tn);
return tn;
}
/// <include file='doc\TreeNodeCollection.uex' path='docs/doc[@for="TreeNodeCollection.Insert5"]/*' />
/// <devdoc>
/// Inserts a new child node on this node. Child node is positioned as specified by index.
/// </devdoc>
public virtual TreeNode Insert(int index, string key, string text, int imageIndex, int selectedImageIndex) {
TreeNode tn = new TreeNode(text, imageIndex, selectedImageIndex);
tn.Name = key;
Insert(index, tn);
return tn;
}
/// <include file='doc\TreeNodeCollection.uex' path='docs/doc[@for="TreeNodeCollection.Insert6"]/*' />
/// <devdoc>
/// Inserts a new child node on this node. Child node is positioned as specified by index.
/// </devdoc>
public virtual TreeNode Insert(int index, string key, string text, string imageKey, string selectedImageKey) {
TreeNode tn = new TreeNode(text);
tn.Name = key;
tn.ImageKey = imageKey;
tn.SelectedImageKey = selectedImageKey;
Insert(index, tn);
return tn;
}
// END - NEW INSERT OVERLOADS IN WHIDBEY -->
/// <include file='doc\TreeNodeCollection.uex' path='docs/doc[@for="TreeNodeCollection.IsValidIndex"]/*' />
/// <devdoc>
/// <para>Determines if the index is valid for the collection.</para>
/// </devdoc>
/// <internalonly/>
private bool IsValidIndex(int index) {
return ((index >= 0) && (index < this.Count));
}
/// <include file='doc\TreeNodeCollection.uex' path='docs/doc[@for="TreeNodeCollection.Clear"]/*' />
/// <devdoc>
/// Remove all nodes from the tree view.
/// </devdoc>
public virtual void Clear() {
owner.Clear();
}
/// <include file='doc\TreeNodeCollection.uex' path='docs/doc[@for="TreeNodeCollection.CopyTo"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public void CopyTo(Array dest, int index) {
if (owner.childCount > 0) {
System.Array.Copy(owner.children, 0, dest, index, owner.childCount);
}
}
/// <include file='doc\TreeNodeCollection.uex' path='docs/doc[@for="TreeNodeCollection.Remove"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public void Remove(TreeNode node) {
node.Remove();
}
/// <include file='doc\TreeNodeCollection.uex' path='docs/doc[@for="TreeNodeCollection.IList.Remove"]/*' />
/// <internalonly/>
void IList.Remove(object node) {
if (node is TreeNode ) {
Remove((TreeNode)node);
}
}
/// <include file='doc\TreeNodeCollection.uex' path='docs/doc[@for="TreeNodeCollection.RemoveAt"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public virtual void RemoveAt(int index) {
this[index].Remove();
}
/// <include file='doc\TreeNodeCollection.uex' path='docs/doc[@for="TreeNodeCollection.RemoveByKey"]/*' />
/// <devdoc>
/// <para>Removes the child control with the specified key.</para>
/// </devdoc>
public virtual void RemoveByKey(string key) {
int index = IndexOfKey(key);
if (IsValidIndex(index)) {
RemoveAt(index);
}
}
/// <include file='doc\TreeNodeCollection.uex' path='docs/doc[@for="TreeNodeCollection.GetEnumerator"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public IEnumerator GetEnumerator() {
return new WindowsFormsUtils.ArraySubsetEnumerator(owner.children, owner.childCount);
}
}
}
|