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); }