internal void OnNodeInvalidated(TreeGridNodeReference invalidatedNodeRef)
        {
            bool updateNeeded = false;

            if (invalidatedNodeRef.Equals(this.currentNode))
            {
                SetCurrentNode(null);
                updateNeeded = true;
            }

            if (this.selectedNodeReferences.ContainsKey(invalidatedNodeRef))
            {
                RemoveNodeFromSelection(invalidatedNodeRef);
                updateNeeded = true;
            }

            if (!updateNeeded)
            {
                updateNeeded = this.visibleRows.Any(r => r.NodeReference.Equals(invalidatedNodeRef));
            }

            if (updateNeeded)
            {
                InvalidateRowLayout(false);
            }
        }
        TreeGridRow GetRowForNode(TreeGridNodeReference nodeRef)
        {
            TreeGridRow row;

            if (this.rowCache.TryGetValue(nodeRef, out row))
            {
                // Found in the cache.  That means the row is already in the canvas, and already has
                // the correct data.  It may need its IsCurrent/IsSelected flags set, but other than
                // that we just need to position it vertically.
                Debug.Assert(row.NodeReference.Equals(nodeRef));
                row.IsCurrent = (nodeRef.Equals(this.currentNode));
                row.IsSelected = this.selectedNodeReferences.ContainsKey(nodeRef);
                row.UpdateExpansionState();
                MoveRowToTopOfUsedList(row);

                if (this.fullRowLayoutRequired)
                {
                    row.Width = double.NaN;
                    row.Measure(infiniteSize);
                    CollectDesiredColumnWidths(row);
                }
                else
                {
                    row.Width = this.headerRow.DesiredSize.Width;
                }
            }
            else if ((this.rowCache.Count >= ((this.canvas.ActualHeight / this.headerRow.ActualHeight) * CachedPageCount)) &&
                (this.tailOfRowCache != null) && (this.tailOfRowCache.lastLayoutPassUsed != this.rowLayoutPass))
            {
                // Not in the cache with that row data, but the cache is full and thus has rows available for recycling.  Use the oldest one.
                // it will be in the cache, but keyed under a different node.  We need to remove it using that key (row.NodeReference)
                // and then update the row and add it to the cache with the new node reference key.
                row = this.tailOfRowCache;
                this.rowCache.Remove(row.NodeReference);
                row.UpdateRowData(nodeRef, nodeRef.Equals(this.currentNode), this.selectedNodeReferences.ContainsKey(nodeRef));
                if (this.fullRowLayoutRequired)
                {
                    row.Width = double.NaN;
                    row.Measure(infiniteSize);
                    CollectDesiredColumnWidths(row);
                }
                else
                {
                    row.Width = this.headerRow.DesiredSize.Width;
                    row.Measure(infiniteSize);
                }
                this.rowCache.Add(row.NodeReference, row);
                MoveRowToTopOfUsedList(row);
            }
            else
            {
                // Not in the cache, and we haven't created a full cache of rows yet.  Create a new one now.
                row = new TreeGridRow(this, false);
                this.canvas.Children.Add(row);
                row.UpdateRowData(nodeRef, nodeRef.Equals(this.currentNode), this.selectedNodeReferences.ContainsKey(nodeRef));
                if (this.fullRowLayoutRequired)
                {
                    row.Width = double.NaN;
                    row.UpdateLayout();
                    CollectDesiredColumnWidths(row);
                }
                else
                {
                    row.Width = this.headerRow.DesiredSize.Width;
                    row.UpdateLayout();
                }

                // NOTE:  Very important that we use the row's NodeReference value as the key here, because we need to make sure the
                // lifetime of the key outlives the lifetime of the row.  (If we used nodeRef, for example, it gets mutated as we
                // progress through the paint, and then gets disposed at the end.)
                this.rowCache.Add(row.NodeReference, row);
                AddRowToUsedList(row);
            }

            return row;
        }