Beispiel #1
0
        internal void OnRowRemoved()
        {
            if (this.panel != null)
            {
                this.panel.OnRowRemoved();
            }

            if (this.nodeReference != null)
            {
                this.nodeReference.Dispose();
                this.nodeReference = null;
            }
        }
Beispiel #2
0
        internal void UpdateRowData(TreeGridNodeReference newNodeReference, bool isCurrent, bool isSelected)
        {
            if (this.nodeReference != null)
            {
                this.nodeReference.Dispose();
            }

            this.ignoreIsExpandedChanges = true;
            try
            {
                // NOTE:  Since we keep this reference we must Clone it.
                this.nodeReference = newNodeReference.Clone();
                this.IsCurrent = isCurrent;
                this.IsSelected = isSelected;
                this.ExpansionLevel = newNodeReference.ExpansionLevel;
                this.IsExpanded = newNodeReference.IsExpanded;
                this.RowData = newNodeReference.Item;
            }
            finally
            {
                this.ignoreIsExpandedChanges = false;
            }
        }
        public void ScrollNodeIntoView(TreeGridNodeReference node)
        {
            DoRowLayoutUntilValid();

            var visibleRow = this.visibleRows.FirstOrDefault(r => r.NodeReference.Equals(node));

            if (visibleRow == null)
            {
                node.ExpandParents();

                int flatIndex;

                if (node.TryGetFlatIndex(out flatIndex))
                {
                    this.TopItemIndex = Math.Max(0, flatIndex - this.visibleRows.Count / 3);
                }
            }
        }
        public void SetCurrentNode(TreeGridNodeReference node)
        {
            if (this.currentNode != null)
            {
                if (this.currentRow != null)
                {
                    this.currentRow.IsCurrent = false;
                    this.currentRow = null;
                }

                this.currentNode.Dispose();
            }

            if (node != null)
            {
                this.currentNode = node.Clone();
                this.currentRow = this.visibleRows.FirstOrDefault(r => r.NodeReference.Equals(this.currentNode));
                if (this.currentRow != null)
                {
                    this.currentRow.IsCurrent = true;
                }
            }
            else
            {
                this.currentNode = null;
            }

            RaiseCurrentItemChanged();
        }
 internal bool IsNodeSelected(TreeGridNodeReference nodeRef)
 {
     return this.selectedNodeReferences.ContainsKey(nodeRef);
 }
 void SledgehammerReset()
 {
     ClearRowCache();
     ClearSelection(false);
     this.currentNode = null;
     if (this.currentRow != null)
     {
         this.currentRow.IsCurrent = false;
     }
     this.currentRow = null;
     this.TopItemIndex = 0;
     this.HorizontalOffset = 0;
     RaiseCurrentItemChanged();
 }
        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);
            }
        }
        void OnCurrentItemChanged(object sender, RoutedEventArgs e)
        {
            var row = e.OriginalSource as TreeGridRow;

            if (this.currentRow != null && this.currentRow != row)
            {
                this.currentRow.IsCurrent = false;
            }

            if (this.currentNode != null)
            {
                this.currentNode.Dispose();
                this.currentNode = null;
            }

            this.currentRow = row;
            this.currentNode = row.NodeReference.Clone();
            RaiseCurrentItemChanged();
        }
        public TreeGridRow SelectNode(TreeGridNodeReference node, SelectionOperation op)
        {
            int nodeIndex;

            using (new SelectionChangeSuppressor(this))
            {
                if (!node.TryGetFlatIndex(out nodeIndex))
                {
                    Debug.Fail("Selected node must be exposed (can't be in a collapsed parent)!");
                    return null;
                }

                if (op == SelectionOperation.SelectOne)
                {
                    ClearSelection(false);
                    AddNodeToSelection(node);
                    this.selectionAnchorIndex = nodeIndex;
                }
                else if (op == SelectionOperation.Toggle)
                {
                    if (!this.selectedNodeReferences.ContainsKey(node))
                    {
                        AddNodeToSelection(node);
                    }
                    else
                    {
                        RemoveNodeFromSelection(node);
                    }

                    if (this.selectionAnchorIndex == -1)
                    {
                        this.selectionAnchorIndex = nodeIndex;
                    }
                }
                else if (op == SelectionOperation.Add)
                {
                    if (!this.selectedNodeReferences.ContainsKey(node))
                    {
                        AddNodeToSelection(node);
                        if (this.selectionAnchorIndex == -1)
                        {
                            this.selectionAnchorIndex = nodeIndex;
                        }
                    }
                }
                else if (op == SelectionOperation.ExtendTo)
                {
                    if (this.selectionAnchorIndex != -1)
                    {
                        int min = Math.Min(this.selectionAnchorIndex, nodeIndex);
                        int max = Math.Max(this.selectionAnchorIndex, nodeIndex);

                        ClearSelection(false);

                        using (var nodeToSelect = this.rootNode.CreateNodeReference(min))
                        {
                            while (nodeToSelect != null && (min <= max))
                            {
                                AddNodeToSelection(nodeToSelect);
                                if (!nodeToSelect.MoveToNextFlatNode())
                                {
                                    break;
                                }
                                min += 1;
                            }
                        }
                    }
                }

                TreeGridRow newCurrentRow = null;

                foreach (var row in this.visibleRows)
                {
                    row.IsSelected = this.selectedNodeReferences.ContainsKey(row.NodeReference);
                    if (row.NodeReference.Equals(this.currentNode))
                    {
                        newCurrentRow = row;
                    }
                }

                if (newCurrentRow != null)
                {
                    newCurrentRow.IsCurrent = true;
                }

                return newCurrentRow;
            }
        }
        void AddNodeToSelection(TreeGridNodeReference node)
        {
            using (new SelectionChangeSuppressor(this))
            {
                // Must clone the node (creating the active reference)...
                var selectedNode = node.Clone();

                // ...and then be SURE that the clone is used for both the key AND the value.  The value is what we
                // dispose when this node is removed from the selection, and the key must not get disposed while it
                // is used as a key (because, again, disposal alters the value semantics).
                this.selectedNodeReferences.Add(selectedNode, selectedNode);
                this.SelectedItemCount = this.selectedNodeReferences.Count;
                this.changeSuppressor.RegisterSelectionChange();
            }
        }
        void RemoveNodeFromSelection(TreeGridNodeReference node)
        {
            using (new SelectionChangeSuppressor(this))
            {
                // This is why selectedNodeReferences must be a dictionary and not just a hash set.
                // Being in the selection implies a reference count on the node (i.e., the node reference is live).
                // Nodes have value semantics so they can be hashed/placed/found in a dictionary (or hash set).
                // But if we used a hash set, there would be no way to get the actual reference that we need to
                // dispose when we remove a node from the selection.  The incoming node has value equality but not
                // reference equality with the node reference in the table.  With a dictionary, we can do this weird-
                // looking statement:
                var selectedNode = this.selectedNodeReferences[node];

                // This ensures that 'selectedNode' not only references the same node as 'node', but is the actual
                // reference we need to dispose.
                // BUT:  Do NOT dispose it before removing it from the dictionary, because Dispose alters
                // the node reference's hash code.
                this.selectedNodeReferences.Remove(node);
                this.SelectedItemCount = this.selectedNodeReferences.Count;
                selectedNode.Dispose();
                this.changeSuppressor.RegisterSelectionChange();
            }
        }
        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;
        }