public override void Dispose()
 {
     if (this.node != null && this.node.childIndex != -1)
     {
         this.node.ReleaseExternalReference();
         this.node = null;
     }
 }
            static TreeGridNode FindFirstCollapsePoint(TreeGridNode node)
            {
                TreeGridNode parentCollapsePoint = null;

                if (node.level > 1)
                {
                    Debug.Assert(node.Parent != null, "Node level is wrong in FindFirstCollapsePoint!");
                    parentCollapsePoint = FindFirstCollapsePoint(node.Parent);
                }

                if (node.IsExpanded || (parentCollapsePoint != null))
                {
                    // We're expanded, or our parent has a collapse point.  If we're expanded
                    // then it's okay if our parent doesn't have a collapsed parent (since we 
                    // don't either in that case).
                    return parentCollapsePoint;
                }

                // We're collapsed, and either have no parent, or our parent has no collapse point.
                // We're it.
                return node;
            }
            static TreeGridNode FindLastNode(TreeGridNode node)
            {
                if (node.IsExpanded && node.items != null && node.items.Count > 0)
                {
                    var lastChild = node.GetOrCreateChildNode(node.items.Count - 1);
                    return FindLastNode(lastChild);
                }

                return node;
            }
            public override bool MoveToParentNode()
            {
                if (this.node == null)
                {
                    return false;
                }

                TreeGridNode parentNode = this.node.Parent;

                // Can't move up to root node...
                if (parentNode is ChildTreeGridNode)
                {
                    parentNode.AddExternalReference();
                    this.node.ReleaseExternalReference();
                    this.node = parentNode;
                    return true;
                }

                return false;
            }
            public override bool MoveToFirstCollapsePoint()
            {
                if (this.node == null)
                {
                    return false;
                }

                TreeGridNode collapsePoint = FindFirstCollapsePoint(this.node);

                if (collapsePoint == this.node || collapsePoint == null)
                {
                    // Didn't move
                    return false;
                }

                collapsePoint.AddExternalReference();
                this.node.ReleaseExternalReference();
                this.node = collapsePoint;
                return true;
            }
        internal void OnChildCountChanged(TreeGridNode child, int oldCount)
        {
            this.ItemCount = this.rootNode.FlatCount;
            this.VerticalScrollRange = this.ItemCount - 1;

            // When nodes collapse, it's possible to "swallow" the current node.  If that happens, we need
            // to move the current node up to it's "collapse point" -- the last (downward) exposed node in its heritage.
            if (this.currentNode != null)
            {
                if (this.currentNode.MoveToFirstCollapsePoint())
                {
                    RaiseCurrentItemChanged();
                }
            }

            // Don't do anything if we haven't had our template applied yet...
            if (this.canvas != null)
            {
                if (this.TopItemIndex >= this.ItemCount)
                {
                    // A collapse-all, filter, or similar operation could cause this.  Do a bottom-up
                    // (and therefore synchronous) layout.
                    this.TopItemIndex = Math.Max(0, this.ItemCount - 1);
                    this.bottomUpLayout = true;
                    DoRowLayout();
                }
                else
                {
                    InvalidateRowLayout(false);
                }
            }
        }
            public override bool MoveToChildItemNode(object item)
            {
                if (this.node == null)
                {
                    return false;
                }

                this.node.EnsureItems();

                int index = this.node.items.IndexOf(item);

                if (index == -1)
                {
                    return false;
                }

                var nextNode = this.node.GetOrCreateChildNode(index);
                nextNode.AddExternalReference();
                this.node.ReleaseExternalReference();
                this.node = nextNode;
                return true;
            }
            public override void ExpandParents()
            {
                if (this.node == null)
                {
                    return;
                }

                for (var node = this.node.Parent; node != null && node.Parent != null; node = node.Parent)
                {
                    if (!node.IsExpanded)
                    {
                        node.Parent.ToggleChildExpansion(node);
                    }
                }
            }
            public override bool MoveToNextFlatNode()
            {
                if (this.node == null)
                {
                    return false;
                }

                TreeGridNode nextNode = this.node;

                if (nextNode.IsExpanded && nextNode.items.Count > 0)
                {
                    // We're on a node that is expanded.  Move to its first child.  
                    nextNode = nextNode.GetOrCreateChildNode(0);
                }
                else
                {
                    while (nextNode.Parent == null || nextNode.childIndex == nextNode.Parent.items.Count - 1)
                    {
                        // Reached the end of this node.  Pop up a level.  If we're already at the top, we're done.
                        if (nextNode.Parent == null)
                        {
                            return false;
                        }

                        nextNode = nextNode.Parent;
                    }

                    nextNode = nextNode.Parent.GetOrCreateChildNode(nextNode.childIndex + 1);
                }

                nextNode.AddExternalReference();
                this.node.ReleaseExternalReference();
                this.node = nextNode;
                return true;
            }
 void OnChildFinalRelease(TreeGridNode child)
 {
     if (this.childNodes != null && this.childNodes.ContainsKey(child.childIndex))
     {
         this.childNodes.Remove(child.childIndex);
         if (this.childNodes.Count == 0 && this.externalReferences == 0 && !this.IsExpanded)
         {
             if (this.Parent != null)
             {
                 this.Parent.OnChildFinalRelease(this);
             }
         }
     }
     else
     {
         Debug.Fail("Final release called; child not found in parent.");
     }
 }
 public Reference(TreeGridNode node)
 {
     this.node = node;
     Debug.Assert(node != null, "You cannot create a reference to a null node.");
     Debug.Assert(this.node.Parent != null, "You cannot create a reference to the root node.");
     this.node.AddExternalReference();
 }
        void OnChildCountChanged(TreeGridNode child, int oldCount)
        {
            int ourOldCount = this.flatCount;

            this.flatCount = this.flatCount - oldCount + child.flatCount;

            if (this.IsExpanded)
            {
                NotifyParentChildCountChanged(this, ourOldCount);
            }
        }
 protected virtual void NotifyParentChildCountChanged(TreeGridNode child, int oldCount)
 {
     if (!this.IsSuppressingChildCountNotification)
     {
         // If this.Parent is null, then there's a parenting problem -- the root node overrides this method.
         this.Parent.OnChildCountChanged(child, oldCount);
     }
 }
        void ToggleChildExpansion(TreeGridNode childNode)
        {
            int oldFlatCount = this.flatCount;

            Debug.Assert(childNode.Parent == this, "Expanding a child node that isn't a child?");

            if (childNode.IsExpanded)
            {
                this.flatCount -= childNode.flatCount;
                childNode.IsExpanded = false;
            }
            else
            {
                childNode.EnsureItems();
                this.flatCount += childNode.flatCount;
                childNode.IsExpanded = true;
            }

            if (this.IsExpanded)
            {
                NotifyParentChildCountChanged(this, oldFlatCount);
            }

            if (!childNode.IsExpanded)
            {
                // Collapsing a node may be its final reference release
                if ((childNode.childNodes == null || childNode.childNodes.Count == 0) && childNode.externalReferences == 0)
                {
                    OnChildFinalRelease(childNode);
                }
            }
        }
 internal ChildTreeGridNode(TreeGridNode parent)
 {
     this.parent = parent;
 }
            public override bool MoveToPreviousFlatNode()
            {
                if (this.node == null)
                {
                    return false;
                }

                TreeGridNode nextNode = this.node;

                if (this.node.childIndex == 0)
                {
                    // We're at the top of this list of children.  Pop up to the parent if we have one.
                    if (this.node.Parent is RootTreeGridNode)
                    {
                        return false;
                    }

                    nextNode = this.node.Parent;
                }
                else
                {
                    nextNode = this.node.Parent.GetOrCreateChildNode(this.node.childIndex - 1);
                    nextNode = FindLastNode(nextNode);
                }

                nextNode.AddExternalReference();
                this.node.ReleaseExternalReference();
                this.node = nextNode;
                return true;
            }
 protected override void NotifyParentChildCountChanged(TreeGridNode child, int oldCount)
 {
     if (!this.IsSuppressingChildCountNotification)
     {
         this.owner.OnChildCountChanged(this, oldCount);
     }
 }
        public TreeGrid()
        {
            this.columns = new ObservableCollection<TreeGridColumn>();
            this.columns.CollectionChanged += OnColumnsChanged;

            // HasChildrenFunc is an optimization opportunity for data sets whose children require computation.
            this.hasChildrenFunc = DefaultHasChildren;
            this.childrenFunc = DefaultChildren;
            this.items = null;

            this.headerRow = new TreeGridRow(this, true);
            Canvas.SetTop(this.headerRow, 0);
            this.visibleRows = new List<TreeGridRow>();
            this.rowCache = new Dictionary<TreeGridNodeReference, TreeGridRow>();
            this.selectedNodeReferences = new Dictionary<TreeGridNodeReference, TreeGridNodeReference>();
            this.PreviewMouseDown += OnPreviewMouseDown;
            this.MouseWheel += OnMouseWheel;
            this.KeyDown += OnKeyDown;
            this.Focusable = true;
            this.selectionAnchorIndex = -1;

            this.rootNode = TreeGridNode.CreateRootNode(this);

            this.AddHandler(TreeGridRow.CurrentItemChangedEvent, (RoutedEventHandler)OnCurrentItemChanged);
            this.AddHandler(TreeGridRow.RowClickedEvent, (EventHandler<RowClickedEventArgs>)OnRowClicked);

            this.CommandBindings.Add(new CommandBinding(ExpandCommand, OnExpandExecuted, OnExpandCanExecute));
            this.CommandBindings.Add(new CommandBinding(ExpandFullyCommand, OnExpandFullyExecuted, OnExpandFullyCanExecute));
            this.CommandBindings.Add(new CommandBinding(ExpandAllCommand, OnExpandAllExecuted));
            this.CommandBindings.Add(new CommandBinding(CollapseCommand, OnCollapseExecuted, OnCollapseCanExecute));
            this.CommandBindings.Add(new CommandBinding(CollapseFullyCommand, OnCollapseFullyExecuted, OnCollapseFullyCanExecute));
            this.CommandBindings.Add(new CommandBinding(CollapseAllCommand, OnCollapseAllExecuted));
            this.CommandBindings.Add(new CommandBinding(SelectAllCommand, OnSelectAllExecuted));

            this.AdditionalContextMenuItems = new ObservableCollection<MenuItem>();
            this.AdditionalContextMenuItems.CollectionChanged += OnAdditionalContextMenuItemsCollectionChanged;
        }