Example #1
0
 internal ItemCountChangedEventArgs(
     ITree tree, int anchorRow, int column, int change, int parentRow, int blanksAfterAnchor,
     SubItemColumnAdjustment[] subItemChanges, bool isToggle)
 {
     myTree                        = tree;
     myRow                         = anchorRow;
     myColumn                      = column;
     myChange                      = change;
     myParentRow                   = parentRow;
     myBlanksAfterAnchor           = isToggle ? blanksAfterAnchor : -1;
     myColumnAdjustments           = subItemChanges;
     myColumnAdjustmentsCollection = null;
 }
 internal ItemCountChangedEventArgs(
     ITree tree, int anchorRow, int column, int change, int parentRow, int blanksAfterAnchor,
     SubItemColumnAdjustment[] subItemChanges, bool isToggle)
 {
     myTree = tree;
     myRow = anchorRow;
     myColumn = column;
     myChange = change;
     myParentRow = parentRow;
     myBlanksAfterAnchor = isToggle ? blanksAfterAnchor : -1;
     myColumnAdjustments = subItemChanges;
     myColumnAdjustmentsCollection = null;
 }
        private void OnToggleExpansion(
            int visibleItemCount, int absIndex, int column, int change, int blanksAboveChange,
            SubItemColumnAdjustmentCollection subItemChanges, bool isExpansionToggle)
        {
            var checkLeftVertScroll = LeftScrollBar;
            var beforeVertScroll = HasVerticalScrollBar;

            var colPerm = myColumnPermutation;
            var adjustAfter = absIndex;
            var adjustChange = change;
            var adjustBlanksAboveChange = blanksAboveChange;
            var totalChange = change;
            var fullRowChangeBegin = -1;
            if (subItemChanges != null)
            {
                SubItemColumnAdjustment columnAdjustment;
                var changeCount = subItemChanges.Count;
                var selIndex = CurrentIndex;
                int displayColumnDummy;
                var nativeSelectionColumn = -1;
                for (var i = 0; i < changeCount; ++i)
                {
                    columnAdjustment = subItemChanges[i];
                    if (Math.Abs(totalChange) < Math.Abs(columnAdjustment.Change))
                    {
                        totalChange = columnAdjustment.Change;
                    }
                    if (i == (changeCount - 1))
                    {
                        if (selIndex > absIndex
                            && selIndex <= (absIndex + columnAdjustment.ItemsBelowAnchor))
                        {
                            if (nativeSelectionColumn == -1)
                            {
                                ResolveSelectionColumn(selIndex, out displayColumnDummy, out nativeSelectionColumn);
                            }
                            if (columnAdjustment.Column != nativeSelectionColumn)
                            {
                                adjustAfter = absIndex + columnAdjustment.ItemsBelowAnchor;
                                adjustBlanksAboveChange = 0;
                            }
                            else
                            {
                                adjustChange = totalChange;
                            }
                        }
                        fullRowChangeBegin = absIndex + Math.Abs(columnAdjustment.Change - change) + 1;
                    }
                }
            }

            if (totalChange == 0)
            {
                OnDisplayDataChanged(VirtualTreeDisplayDataChanges.ItemButton, absIndex, column, 1);
                return;
            }
            var hWnd = Handle;
            NativeMethods.RECT pendingUpdateRect;
            var paintPending = NativeMethods.GetUpdateRect(hWnd, out pendingUpdateRect, false);
            BeginUpdate();

            // We need the top index for both cases to determine if it has changed.
            // A top index change indicates that the whole window needs refreshing,
            // no change indicates we only need to update the portion below absIndex.
            var stateTracker = new ListBoxStateTracker(this);
            // The window's listbox doesn't allow a mechanism for inserting
            // items in a no-data listbox, so everything vital (selection, caret,
            // and top index) must be explicitly maintained. LB_DELETESTRING does
            // work (unlike LB_INSERTSTRING), but there are performance penalties
            // with multiple WM_DELETEITEM messages due to the callbacks to the parent
            // object, so we handle deletion in a similar fashion.

            // Adjust cached settings
            stateTracker.ApplyChange(adjustAfter, column, adjustChange, adjustBlanksAboveChange, visibleItemCount, isExpansionToggle);

            try
            {
                // Change the ItemCount. This clears the selection, scroll state, etc.  Set RestoringSelection
                // flag so that we don't fire events for this, since we will restore it below.
                SetStateFlag(VTCStateFlags.RestoringSelection, true);
                ItemCount = visibleItemCount;
            }
            finally
            {
                SetStateFlag(VTCStateFlags.RestoringSelection, false);
            }

            // see if there was enough room for the expansion
            if (isExpansionToggle && change > 0)
            {
                var spaceNeeded = totalChange + blanksAboveChange - (myFullyVisibleCount - 1 - (absIndex - stateTracker.restoreTop));
                if (spaceNeeded > 0)
                {
                    var newCaret = stateTracker.restoreTop + spaceNeeded;

                    if (newCaret > absIndex)
                    {
                        newCaret = absIndex;
                    }

                    stateTracker.restoreTop = newCaret;
                }

                if (absIndex < stateTracker.restoreTop)
                {
                    stateTracker.restoreTop = absIndex;
                }
            }

            // Restore the previous state.  Only do this if we're not in the middle of a list shuffle, 
            // as in that case the shuffle will take care of this when it finishes.
            if (myTree != null
                && !myTree.ListShuffle)
            {
                stateTracker.Restore(this);
            }

            EndUpdate();

            // UNDONE: If the scrollbar has appeared/disappeared when it is on the left,
            // then we want vertical gridlines to not change. Unfortunately, the listbox
            // automatically scrolls the window for us, so column 0 is correct, and the rest
            // are shifted by a scrollbar width. This means that we should be able to validate
            // large portions of column 0.

            var invalidatePending = false;
            var subsequentSubItems = false;

            // only perfom the optimization if we arent horizontally scrolled. (bug 38565)
            if (change != 0
                && myXPos == 0)
            {
                // If the expanded item is below the top, and the top
                // hasn't moved, then we can get away with only repainting
                // a portion (possibly none) of the window. Make sure we
                // only attempt to Validate regions if there is no paint
                // event pending when this occurs.
                var afterVertScroll = HasVerticalScrollBar;
                if (absIndex >= stateTracker.startTop
                    &&
                    (!checkLeftVertScroll || afterVertScroll == beforeVertScroll)
                    &&
                    stateTracker.startTop == TopIndex)
                {
                    NativeMethods.RECT itemRect;
                    NativeMethods.RECT anchorRect;
                    NativeMethods.RECT clientRect;
                    var offsetAnchor = fullRowChangeBegin != -1;
                    NativeMethods.SendMessage(hWnd, NativeMethods.LB_GETITEMRECT, absIndex, out anchorRect);
                    itemRect = anchorRect;
                    if (offsetAnchor)
                    {
                        // The full row change begin may be after the current number of items in the control, so
                        // we can't use LB_GETITEMRECT to pick it up. Use an offset from absIndex instead.
                        var offsetBy = (fullRowChangeBegin - absIndex) * itemRect.height;
                        if (offsetBy != 0)
                        {
                            itemRect.bottom += offsetBy;
                            itemRect.top += offsetBy;
                        }
                        if (HasHorizontalGridLines)
                        {
                            // Make sure the preceding gridline is redraw
                            itemRect.top -= 1;
                        }
                    }
                    NativeMethods.GetClientRect(hWnd, out clientRect);
                    var restoreXPos = stateTracker.restoreXPos;
                    if (anchorRect.bottom >= clientRect.top
                        && anchorRect.top <= clientRect.bottom)
                    {
                        // The top of the client area doesn't change.
                        // UNDONE: This can be further optimized with ScrollWindowEx, but this
                        // is a pretty good painting optimization for a first go round.
                        clientRect.bottom = itemRect.top - 1;
                        if (clientRect.width >= restoreXPos)
                        {
                            // The scrollbar acrobatics above leave the upper right portion
                            // of the window blank, adjust the rect accordingly
                            clientRect.right -= restoreXPos;
                            // TFS 166541: We used to adjust the clientRect for the scrollbar width
                            // only. However, that did not work if the last column contained right
                            // aligned content. So, we now adjust the clientRect for the entire last
                            // column width (which includes the scrollbar width change).
                            if (afterVertScroll != beforeVertScroll)
                            {
                                var numColumns = ColumnCount;
                                var lastColumn = numColumns - 1;

                                var columnRect = Rectangle.FromLTRB(clientRect.left, clientRect.top, clientRect.right, clientRect.bottom);
                                LimitRectToColumn(lastColumn, ref columnRect);
                                clientRect.right -= columnRect.Width;
                            }
                            NativeMethods.ValidateRect(hWnd, ref clientRect);
                            invalidatePending = paintPending;
                        }
                    }
                    else
                    {
                        // None of the client area changes
                        NativeMethods.ValidateRect(hWnd, IntPtr.Zero);
                        invalidatePending = paintPending;
                    }
                }
                subsequentSubItems = true;
            }
            if (subItemChanges != null)
            {
                if (subsequentSubItems || stateTracker.startTop == TopIndex)
                {
                    // Since the total item count did not change, we don't have to worry
                    // about scrollbars coming/going here
                    if (!subsequentSubItems)
                    {
                        NativeMethods.ValidateRect(hWnd, IntPtr.Zero);
                        invalidatePending = paintPending;
                    }
                    SubItemColumnAdjustment columnAdjustment;
                    var changeCount = subItemChanges.Count;
                    for (var i = 0; i < changeCount; ++i)
                    {
                        columnAdjustment = subItemChanges[i];
                        var displayColumn = columnAdjustment.Column;
                        if (colPerm != null)
                        {
                            displayColumn = colPerm.GetPermutedColumn(displayColumn);
                            if (displayColumn == -1)
                            {
                                continue;
                            }
                        }
                        var columnRect = GetItemRectangle(absIndex, displayColumn);
                        var leftColumn = displayColumn;
                        var rightColumn = displayColumn;
                        if (colPerm != null)
                        {
                            var columnExpansion = colPerm.GetColumnExpansion(displayColumn, columnAdjustment.LastColumnOnRow);
                            if (columnExpansion.Width > 1)
                            {
                                leftColumn = columnExpansion.LeftColumn;
                                rightColumn = columnExpansion.RightColumn;
                            }
                        }
                        else if (displayColumn == columnAdjustment.LastColumnOnRow)
                        {
                            var lastColumn = myMctree.ColumnCount - 1;
                            if (displayColumn < lastColumn)
                            {
                                rightColumn = lastColumn;
                            }
                        }
                        if (leftColumn != rightColumn)
                        {
                            int columnLeft;
                            int columnWidth;
                            GetColumnBounds(leftColumn, rightColumn, out columnLeft, out columnWidth);
                            columnRect.Width = columnWidth;
                            columnRect.X = columnLeft;
                        }
                        NativeMethods.RECT rect;
                        // UNDONE: Use the Change and TrailingItems values to optimize this.
                        // Easy for now: Just redraw the rest of the column
                        var changeRows = columnAdjustment.ItemsBelowAnchor + Math.Abs(columnAdjustment.Change - change) + 1;
                        if (changeRows > 0)
                        {
                            if (changeRows > 1)
                            {
                                columnRect.Inflate(0, columnRect.Height * (changeRows - 1));
                            }
                            rect = new NativeMethods.RECT(columnRect);
                            NativeMethods.InvalidateRect(hWnd, ref rect, false);
                        }
                    }
                }
            }
            if (invalidatePending)
            {
                NativeMethods.InvalidateRect(hWnd, ref pendingUpdateRect, false);
            }

            if (totalChange > 0)
            {
                ScrollBarsAfterExpand(absIndex, totalChange);
            }
            else if (totalChange < 0)
            {
                ScrollBarsAfterCollapse(absIndex, -totalChange);
            }

            if (VirtualTreeAccEvents.ShouldNotify(VirtualTreeAccEvents.eventObjectStateChange, this))
            {
                VirtualTreeAccEvents.Notify(
                    VirtualTreeAccEvents.eventObjectStateChange, absIndex, 0,
                    this);
            }
        }
 private void OnToggleExpansion(ITree tree, int absRow, int column, int change, int blanksAboveChange, SubItemColumnAdjustmentCollection subItemChanges, bool isExpansionToggle)
 {
     OnToggleExpansion(tree.VisibleItemCount, absRow, column, change, blanksAboveChange, subItemChanges, isExpansionToggle);
 }