public RowClickedEventArgs(TreeGridRow row, MouseButtonEventArgs originalArgs) : base(TreeGridRow.RowClickedEvent, row) { this.Row = row; this.OriginalArgs = originalArgs; this.Modifiers = Keyboard.Modifiers; }
static void OnIsExpandedChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e) { TreeGridRow row = obj as TreeGridRow; if (row != null && row.nodeReference != null && !row.ignoreIsExpandedChanges) { //row.ParentNode.ToggleChildExpansion(row.ChildIndex, row.treeGrid.ChildrenFunc); row.nodeReference.IsExpanded = !row.nodeReference.IsExpanded; } }
static void OnIsCurrentChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e) { TreeGridRow row = obj as TreeGridRow; if (row != null) { if (row.IsCurrent) row.RaiseEvent(new RoutedEventArgs(CurrentItemChangedEvent, row)); } }
static void OnRowDataChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e) { TreeGridRow row = obj as TreeGridRow; if (row != null) { if (!row.IsHeaderRow) { row.IsExpandable = row.treeGrid.HasChildrenFunc(row.RowData); } } }
internal void OnRowRemoved() { this.ownerRow.OwnerGrid.Columns.CollectionChanged -= OnColumnsChanged; if (!this.ownerRow.IsHeaderRow) { foreach (var column in this.cells.Keys) { column.PropertyChanged -= OnColumnPropertyChanged; } } this.cells.Clear(); this.ownerRow = null; }
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(); }
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(); }
void CollectDesiredColumnWidths(TreeGridRow row) { if (row.IsLoaded) { foreach (var column in this.columns) { if (double.IsNaN(column.Width)) { double desiredWidth; if (row.TryGetDesiredCellWidth(column, out desiredWidth)) { column.IsWidthLocked = true; } else { column.IsWidthLocked = false; } column.ActualWidth = Math.Max(column.MinWidth, row == this.headerRow ? desiredWidth : Math.Max(column.ActualWidth, desiredWidth)); } else { column.ActualWidth = Math.Max(column.Width, column.MinWidth); column.IsWidthLocked = true; } } } }
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; }
internal void OnRowHeightChanged(TreeGridRow row) { if (row.lastLayoutPassUsed == this.rowLayoutPass) { InvalidateRowLayout(false); } }
void AddRowToUsedList(TreeGridRow row) { row.lastLayoutPassUsed = this.rowLayoutPass; row.prevLink = null; row.nextLink = this.headOfRowCache; if (this.headOfRowCache != null) { this.headOfRowCache.prevLink = row; } else { Debug.Assert(this.tailOfRowCache == null, "If we don't have a head, then we shouldn't have a tail!"); this.tailOfRowCache = row; } this.headOfRowCache = row; }
void MoveRowToTopOfUsedList(TreeGridRow row) { if (row == this.headOfRowCache) { // Already at the front. Since we're not calling AddRowToUsedList, we must do this. row.lastLayoutPassUsed = this.rowLayoutPass; return; } RemoveRowFromUsedList(row); // NOTE: This marks the row's last-used paint pass with the current pass value. AddRowToUsedList(row); }
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; }
void ClearRowCache() { foreach (var kvp in this.rowCache) { this.canvas.Children.Remove(kvp.Value); kvp.Value.OnRowRemoved(); } this.rowCache.Clear(); this.visibleRows.Clear(); this.headOfRowCache = null; this.tailOfRowCache = null; }
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(); }
void RemoveRowFromUsedList(TreeGridRow row) { if (row.prevLink != null) { row.prevLink.nextLink = row.nextLink; } else { Debug.Assert(row == this.headOfRowCache, "Row doesn't have a prev link, so it should be the head!"); this.headOfRowCache = row.nextLink; } if (row.nextLink != null) { row.nextLink.prevLink = row.prevLink; } else { Debug.Assert(row == this.tailOfRowCache, "Row doesn't have a next link, so it should be the tail!"); this.tailOfRowCache = row.prevLink; } row.nextLink = null; row.prevLink = null; }