Exemple #1
0
 public RowClickedEventArgs(TreeGridRow row, MouseButtonEventArgs originalArgs)
     : base(TreeGridRow.RowClickedEvent, row)
 {
     this.Row = row;
     this.OriginalArgs = originalArgs;
     this.Modifiers = Keyboard.Modifiers;
 }
Exemple #2
0
        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;
            }
        }
Exemple #3
0
        static void OnIsCurrentChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            TreeGridRow row = obj as TreeGridRow;

            if (row != null)
            {
                if (row.IsCurrent)
                    row.RaiseEvent(new RoutedEventArgs(CurrentItemChangedEvent, row));
            }
        }
Exemple #4
0
        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;
        }
        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 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;
        }