/// <summary> /// Reimplementation of <see cref="ITree.GetNavigationTarget"/> to fix issues with the Down navigation direction /// </summary> protected new VirtualTreeCoordinate GetNavigationTarget(TreeNavigation direction, int sourceRow, int sourceColumn, ColumnPermutation columnPermutation) { // UNDONE: MSBUG GetNavigationTarget calls the internal method to get its blank expansion, not // the interface, so we have no way to override it. Duplicate the GetNavigationTarget fix here for // the 'Down' navigation target. BlankExpansionData blankExpansion; switch (direction) { case TreeNavigation.Down: blankExpansion = GetBlankExpansion(sourceRow, sourceColumn, columnPermutation); if (blankExpansion.Anchor.IsValid) { int lastRow = ((ITree)this).VisibleItemCount - 1; while (blankExpansion.BottomRow < lastRow) { // Just return the next non-blank column int testRow = blankExpansion.BottomRow + 1; blankExpansion = GetBlankExpansion(testRow, sourceColumn, columnPermutation); if (!blankExpansion.Anchor.IsValid) { break; } int topRow = blankExpansion.TopRow; if (sourceColumn == blankExpansion.AnchorColumn && topRow >= testRow) { return new VirtualTreeCoordinate(topRow, sourceColumn); } } } return VirtualTreeCoordinate.Invalid; case TreeNavigation.Right: // If no column permutation is specified and we're on the first row in a complex // subitem expansion, then the provided implementation does not go right. // MSBUG: VirtualTree.GetNavigationTarget is looking at the column count of the // ParentNode of a complex subitem. The column count in this case is always 1. if (columnPermutation == null) { VirtualTreeCoordinate retVal = base.GetNavigationTarget(TreeNavigation.Right, sourceRow, sourceColumn, null); int lastAllowedSourceColumn; if (!retVal.IsValid && sourceColumn < (lastAllowedSourceColumn = (ColumnCount - 1)) && (blankExpansion = GetBlankExpansion(sourceRow, sourceColumn, null)).Anchor.IsValid && blankExpansion.RightColumn < lastAllowedSourceColumn) { retVal = GetBlankExpansion(sourceRow, blankExpansion.RightColumn + 1, null).Anchor; } return retVal; } break; case TreeNavigation.LeftColumn: if (sourceColumn == 0) { // UNDONE: MSBUG Navigating LeftColumn from column 0 is crashing return VirtualTreeCoordinate.Invalid; } break; } return base.GetNavigationTarget(direction, sourceRow, sourceColumn, columnPermutation); }
/// <summary> /// Reimplementation of <see cref="ITree.GetNavigationTarget"/> to fix issues with the Down navigation direction /// </summary> protected new VirtualTreeCoordinate GetNavigationTarget(TreeNavigation direction, int sourceRow, int sourceColumn, ColumnPermutation columnPermutation) { // UNDONE: MSBUG GetNavigationTarget calls the internal method to get its blank expansion, not // the interface, so we have no way to override it. Duplicate the GetNavigationTarget fix here for // the 'Down' navigation target. BlankExpansionData blankExpansion; switch (direction) { case TreeNavigation.Down: blankExpansion = GetBlankExpansion(sourceRow, sourceColumn, columnPermutation); if (blankExpansion.Anchor.IsValid) { int lastRow = ((ITree)this).VisibleItemCount - 1; while (blankExpansion.BottomRow < lastRow) { // Just return the next non-blank column int testRow = blankExpansion.BottomRow + 1; blankExpansion = GetBlankExpansion(testRow, sourceColumn, columnPermutation); if (!blankExpansion.Anchor.IsValid) { break; } int topRow = blankExpansion.TopRow; if (sourceColumn == blankExpansion.AnchorColumn && topRow >= testRow) { return(new VirtualTreeCoordinate(topRow, sourceColumn)); } } } return(VirtualTreeCoordinate.Invalid); case TreeNavigation.Right: // If no column permutation is specified and we're on the first row in a complex // subitem expansion, then the provided implementation does not go right. // MSBUG: VirtualTree.GetNavigationTarget is looking at the column count of the // ParentNode of a complex subitem. The column count in this case is always 1. if (columnPermutation == null) { VirtualTreeCoordinate retVal = base.GetNavigationTarget(TreeNavigation.Right, sourceRow, sourceColumn, null); int lastAllowedSourceColumn; if (!retVal.IsValid && sourceColumn < (lastAllowedSourceColumn = (ColumnCount - 1)) && (blankExpansion = GetBlankExpansion(sourceRow, sourceColumn, null)).Anchor.IsValid && blankExpansion.RightColumn < lastAllowedSourceColumn) { retVal = GetBlankExpansion(sourceRow, blankExpansion.RightColumn + 1, null).Anchor; } return(retVal); } break; case TreeNavigation.LeftColumn: if (sourceColumn == 0) { // UNDONE: MSBUG Navigating LeftColumn from column 0 is crashing return(VirtualTreeCoordinate.Invalid); } break; } return(base.GetNavigationTarget(direction, sourceRow, sourceColumn, columnPermutation)); }
VirtualTreeCoordinate ITree.GetNavigationTarget(TreeNavigation direction, int sourceRow, int sourceColumn, ColumnPermutation columnPermutation) { return(GetNavigationTarget(direction, sourceRow, sourceColumn, columnPermutation)); }
VirtualTreeCoordinate ITree.GetNavigationTarget( TreeNavigation direction, int sourceRow, int sourceColumn, ColumnPermutation columnPermutation) { Debug.Assert(sourceColumn == 0); sourceColumn = 0; var targetRow = sourceRow; if (myParent.myRootNode == null) { return VirtualTreeCoordinate.Invalid; } var retVal = false; var pos = myParent.TrackSingleColumnRow(sourceRow); TREENODE tnChild; switch (direction) { case TreeNavigation.Left: case TreeNavigation.Parent: case TreeNavigation.ComplexParent: if (pos.ParentAbsolute != -1) { targetRow = pos.ParentAbsolute; retVal = true; } break; case TreeNavigation.Right: case TreeNavigation.FirstChild: case TreeNavigation.LastChild: // FirstChild and LastChild share code for testing if the node is expanded tnChild = pos.ParentNode.GetChildNode(pos.Index); if (tnChild != null && tnChild.Expanded && tnChild.ImmedCount > 0) { // Right jumps to FirstChild, test LastChild, not FirstChild, so // else handles both elements. if (direction == TreeNavigation.LastChild) { targetRow = sourceRow + tnChild.GetSingleColumnChildOffset(tnChild.ImmedCount - 1); } else { targetRow = sourceRow + 1; } retVal = true; } break; case TreeNavigation.NextSibling: // Test localColumn. Expandable and simple cells don't have siblings, // and all other nodes will give back a localColumn of 0. if (pos.Index < (pos.ParentNode.ImmedCount - 1)) { targetRow = sourceRow + 1; tnChild = pos.ParentNode.GetChildNode(pos.Index); if (tnChild != null) { targetRow += tnChild.FullCount; } retVal = true; } break; case TreeNavigation.PreviousSibling: if (pos.Index > 0) { targetRow = pos.ParentAbsolute + pos.ParentNode.GetSingleColumnChildOffset(pos.Index - 1); retVal = true; } break; case TreeNavigation.Up: if (targetRow > 0) { --targetRow; retVal = true; } break; case TreeNavigation.Down: if (targetRow < (myParent.myRootNode.FullCount - 1)) { ++targetRow; retVal = true; } break; //case TreeNavigation.RightColumn: //case TreeNavigation.LeftColumn: } return retVal ? new VirtualTreeCoordinate(targetRow, 0) : VirtualTreeCoordinate.Invalid; }
protected VirtualTreeCoordinate GetNavigationTarget( TreeNavigation direction, int sourceRow, int sourceColumn, ColumnPermutation columnPermutation) { var targetRow = sourceRow; var targetColumn = sourceColumn; if (myRootNode == null) { return VirtualTreeCoordinate.Invalid; } var startColumn = sourceColumn; var retVal = false; var expansion = GetBlankExpansion(sourceRow, sourceColumn, columnPermutation); sourceRow = expansion.TopRow; var nativeSourceColumn = sourceColumn; if (expansion.AnchorColumn != VirtualTreeConstant.NullIndex) { sourceColumn = expansion.AnchorColumn; } if (columnPermutation != null) { nativeSourceColumn = columnPermutation.GetNativeColumn(sourceColumn); } var localColumn = nativeSourceColumn; var lastSubItem = false; var pos = TrackCell(sourceRow, ref localColumn, ref lastSubItem); TREENODE tnChild; var attemptNextColumn = false; int selectColumn; TREENODE tnParent = null; var rowOffset = 0; switch (direction) { case TreeNavigation.Parent: case TreeNavigation.ComplexParent: if (localColumn == 0 && (pos.ParentAbsolute != -1 || (pos.ParentNode.SubItemRoot && !pos.ParentNode.ComplexSubItem))) { if (nativeSourceColumn > 0) { // This is more work than column zero. The parent // absolute is relative to the root node in the branch. targetRow = sourceRow - pos.ParentNode.GetChildOffset(pos.Index); } else { targetRow = pos.ParentAbsolute; } targetColumn = sourceColumn; retVal = true; } else if (direction == TreeNavigation.ComplexParent) { if (pos.ParentNode.ComplexSubItem) { targetColumn = nativeSourceColumn - pos.ParentNode.Parent.FirstSubItem.ColumnOfRootNode(pos.ParentNode); if ((columnPermutation == null) || (-1 != (targetColumn = columnPermutation.GetPermutedColumn(targetColumn)))) { targetRow = sourceRow - pos.ParentNode.GetChildOffset(pos.Index) + 1; retVal = true; } } else if (localColumn > 0) { targetColumn = nativeSourceColumn - localColumn; if ((columnPermutation == null) || (-1 != (targetColumn = columnPermutation.GetPermutedColumn(targetColumn)))) { targetRow = sourceRow; retVal = true; } } } break; case TreeNavigation.FirstChild: case TreeNavigation.LastChild: // FirstChild and LastChild share code for testing if the node is expanded tnChild = pos.ParentNode.GetChildNode(pos.Index); if (localColumn > 0 && tnChild != null) { var sn = tnChild.SubItemAtColumn(localColumn); tnChild = (sn == null) ? null : sn.RootNode; } if (tnChild != null && tnChild.Expanded && tnChild.ImmedCount > 0) { // Right jumps to FirstChild, test LastChild, not FirstChild, so // else handles both elements. if (direction == TreeNavigation.LastChild) { targetRow = sourceRow + tnChild.GetChildOffset(tnChild.ImmedCount - 1); } else { targetRow = sourceRow + tnChild.ImmedSubItemGain + 1; } targetColumn = sourceColumn; retVal = true; } else if (attemptNextColumn) { goto case TreeNavigation.RightColumn; } break; case TreeNavigation.RightColumn: { // Move right to the next column. Note that this is not a natural // thing to do from the tree perspective because the closest element // to the right is likely to be only a distant relative of the current // node, but the user is unlikely to appreciate this distinction and // just wants to move right, so we let them. if (nativeSourceColumn == 0 || localColumn > 0) { if (columnPermutation == null) { if (pos.ParentNode.GetColumnCount(pos.Index) > (localColumn + 1)) { targetRow = sourceRow; targetColumn = sourceColumn + 1; retVal = true; } } else if (expansion.RightColumn < (columnPermutation.VisibleColumnCount - 1)) { targetColumn = GetBlankExpansion(sourceRow, expansion.RightColumn + 1, columnPermutation).AnchorColumn; targetRow = sourceRow; retVal = true; } } else { // Walk back up this tree until we get to a subitem root node, tracking // offsets as we go. tnParent = pos.ParentNode; rowOffset = tnParent.GetChildOffset(pos.Index); while (!tnParent.SubItemRoot) { tnChild = tnParent; tnParent = tnChild.Parent; rowOffset += tnParent.GetChildOffset(tnChild.Index); } if (tnParent.ComplexSubItem) { --rowOffset; } tnChild = tnParent; tnParent = tnChild.Parent; // Find the SUBITEMNODE for the next column and the next column // index. The sub item node may be null if the next column is simple. SUBITEMNODE snNext = null; selectColumn = -1; if (columnPermutation == null) { if (localColumn < (tnParent.Parent.GetColumnCount(tnParent.Index) - 1)) { var sn = tnParent.FirstSubItem; while (sn.RootNode != tnChild) { sn = sn.NextSibling; } var snTestNext = sn.NextSibling; if (snTestNext != null && (snTestNext.Column - sn.Column) == 1) { snNext = snTestNext; } selectColumn = sourceColumn + 1; } } else { if (expansion.RightColumn < (columnPermutation.VisibleColumnCount - 1)) { selectColumn = GetBlankExpansion(sourceRow, expansion.RightColumn + 1, columnPermutation).AnchorColumn; // Note that column zero has no subitem node, so this covers this case // UNDONE_MC: Need local column, not global snNext = tnParent.SubItemAtColumn(columnPermutation.GetNativeColumn(selectColumn)); } } if (selectColumn != -1) { if (snNext != null) { var nextGain = snNext.RootNode.TotalCount; if (snNext.RootNode.ComplexSubItem) { --nextGain; } else if (!snNext.RootNode.Expanded) { nextGain = 0; } if (nextGain < rowOffset) { targetRow = sourceRow - rowOffset + nextGain; } else { targetRow = sourceRow; } } else { targetRow = sourceRow - rowOffset; } targetColumn = selectColumn; retVal = true; } } break; } case TreeNavigation.NextSibling: // Test localColumn. Expandable and simple cells don't have siblings, // and all other nodes will give back a localColumn of 0. if (localColumn == 0 && pos.Index < (pos.ParentNode.ImmedCount - 1)) { targetRow = sourceRow + 1; tnChild = pos.ParentNode.GetChildNode(pos.Index); if (tnChild != null) { targetRow += tnChild.TotalCount; } targetColumn = sourceColumn; retVal = true; } break; case TreeNavigation.PreviousSibling: if (localColumn == 0 && pos.Index > 0) { if (pos.ParentNode.ComplexSubItem) { // The parent absolute is -1 and can't be used to give back a real count, // so use a more expensive algorithm in this case. targetRow = sourceRow - pos.ParentNode.GetChildOffset(pos.Index) + pos.ParentNode.GetChildOffset(pos.Index - 1); } else { targetRow = pos.ParentAbsolute + pos.ParentNode.GetChildOffset(pos.Index - 1); } targetColumn = sourceColumn; retVal = true; break; } break; case TreeNavigation.Up: while (targetRow > 0) { expansion = GetBlankExpansion(targetRow - 1, startColumn, columnPermutation); targetRow = expansion.TopRow; if (startColumn == expansion.AnchorColumn) { retVal = true; break; } } break; case TreeNavigation.Down: while (expansion.BottomRow < (myRootNode.TotalCount - 1)) { // Just return the next non-blank column var testRow = expansion.BottomRow + 1; expansion = GetBlankExpansion(testRow, startColumn, columnPermutation); targetRow = expansion.TopRow; if (startColumn == expansion.AnchorColumn && targetRow >= testRow) { retVal = true; break; } } break; case TreeNavigation.LeftColumn: if (localColumn > 0) { if (columnPermutation == null) { if (sourceColumn > 0) { targetRow = sourceRow; targetColumn = sourceColumn - 1; retVal = true; } } else if (expansion.LeftColumn > 0) { targetRow = sourceRow; targetColumn = GetBlankExpansion(targetRow, expansion.LeftColumn - 1, columnPermutation).AnchorColumn; retVal = true; } } else { // tnParent may have been set already in TreeNavigation.Left, only set it if needed // Note that the nativeSourceColumn == 0 case can happen with a columnPermutation only if (tnParent == null && nativeSourceColumn != 0) { tnParent = pos.ParentNode; rowOffset = tnParent.GetChildOffset(pos.Index); while (!tnParent.SubItemRoot) { tnChild = tnParent; tnParent = tnChild.Parent; rowOffset += tnParent.GetChildOffset(tnChild.Index); } if (tnParent.ComplexSubItem) { --rowOffset; } } selectColumn = -1; SUBITEMNODE snPrev = null; if (columnPermutation == null) { if (tnParent != null && tnParent.Parent != null) { var sn = tnParent.Parent.FirstSubItem; SUBITEMNODE snTestPrev = null; while (sn.RootNode != tnParent) { snTestPrev = sn; sn = sn.NextSibling; } if (snTestPrev != null && (sn.Column - snTestPrev.Column) == 1) { snPrev = snTestPrev; } } selectColumn = sourceColumn - 1; } else if (expansion.LeftColumn > 0) { selectColumn = GetBlankExpansion(sourceRow, expansion.LeftColumn - 1, columnPermutation).AnchorColumn; if (tnParent != null) { // Note that column zero has no subitem node, so this covers this case // UNDONE_MC: Need local column, not global snPrev = tnParent.Parent.SubItemAtColumn(columnPermutation.GetNativeColumn(selectColumn)); } } if (selectColumn != -1) { targetColumn = selectColumn; retVal = true; if (snPrev == null) { // The previous column is the parent node or a simple cell. Move // to that cell. targetRow = sourceRow - rowOffset; } else { // The most closely related node to the left is the 0 node in the // previous column. However, this node is not spatially closest // to the current cell. The subtle the distinction that the closest // cell is only a distant relative of the current one is generally lost // on the user, so we just let them move left to the current value. var prevGain = snPrev.RootNode.TotalCount; if (snPrev.RootNode.ComplexSubItem) { --prevGain; } else if (!snPrev.RootNode.Expanded) { prevGain = 0; } if (prevGain < rowOffset) { targetRow = sourceRow - rowOffset + prevGain; } else { targetRow = sourceRow; } } } } break; case TreeNavigation.Left: if (sourceColumn > 0) { if (localColumn > 0 || (pos.Index == 0 && pos.ParentNode.ComplexSubItem)) { // Move left one column if (columnPermutation == null) { targetRow = sourceRow; targetColumn = sourceColumn - 1; retVal = true; } else if (expansion.LeftColumn > 0) { targetRow = sourceRow; targetColumn = GetBlankExpansion(sourceRow, expansion.LeftColumn - 1, columnPermutation).AnchorColumn; retVal = true; } break; } else if (pos.ParentNode.ComplexSubItem && pos.Index > 0) { tnParent = pos.ParentNode; rowOffset = tnParent.GetChildOffset(pos.Index) - 1; goto case TreeNavigation.LeftColumn; } else if (nativeSourceColumn == 0 && pos.ParentNode.Index == -1) { goto case TreeNavigation.LeftColumn; } } goto case TreeNavigation.Parent; case TreeNavigation.Right: if (MultiColumnSupport) { if ((sourceColumn == 0) || (pos.ParentNode.ComplexSubItem && pos.Index == 0) || (localColumn > 0)) { if (columnPermutation == null) { if (localColumn < (pos.ParentNode.GetColumnCount(pos.Index) - 1)) { targetRow = sourceRow; targetColumn = sourceColumn + 1; retVal = true; break; } } else if (expansion.RightColumn < (columnPermutation.VisibleColumnCount - 1)) { targetRow = sourceRow; targetColumn = GetBlankExpansion(sourceRow, expansion.RightColumn + 1, columnPermutation).AnchorColumn; retVal = true; break; } } else if (columnPermutation == null) { if (sourceColumn < (ColumnCount - 1)) { // If firstchild doesn't go anywhere, then attempt // to move to the next branch. attemptNextColumn = true; } } else if (expansion.RightColumn < (columnPermutation.VisibleColumnCount - 1)) { attemptNextColumn = true; } } goto case TreeNavigation.FirstChild; } return retVal ? new VirtualTreeCoordinate(targetRow, targetColumn) : VirtualTreeCoordinate.Invalid; }
VirtualTreeCoordinate ITree.GetNavigationTarget( TreeNavigation direction, int sourceRow, int sourceColumn, ColumnPermutation columnPermutation) { return GetNavigationTarget(direction, sourceRow, sourceColumn, columnPermutation); }