internal void OnRowRemoved() { if (this.panel != null) { this.panel.OnRowRemoved(); } if (this.nodeReference != null) { this.nodeReference.Dispose(); this.nodeReference = null; } }
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; }