/// <summary> /// Create a ColumnPermutation object with a given set of /// visible columns, in the order they should be displayed. /// </summary> /// <param name="totalColumns"> /// The total number of columns that can be visible. This /// should correspond to the ColumnCount on the multicolumn tree. /// </param> /// <param name="visibleColumns">The columns that are visible.</param> /// <param name="preferLeftBlanks"> /// If there is a choice, attach blank cells to the /// cell on the right instead of the cell on the left. Generally corresponds to the /// RightToLeft property on the tree control. /// </param> public ColumnPermutation(int totalColumns, int[] visibleColumns, bool preferLeftBlanks) { var columns = new int[totalColumns]; int i; // Initialize to -1 for (i = 0; i < totalColumns; ++i) { columns[i] = -1; } // Walk the visibleColumns array and fill in the values. When we're done, // if (myColumns[i] != -1) then myVisibleColumns[myColumns[i]] == i int targetColumn; var visibleColumnCount = visibleColumns.Length; for (i = 0; i < visibleColumnCount; ++i) { targetColumn = visibleColumns[i]; if (columns[targetColumn] != -1) // Let this throw naturally if the argument is out of range { throw new ArgumentException(VirtualTreeStrings.GetString(VirtualTreeStrings.DuplicateColumnException), "visibleColumns"); } columns[targetColumn] = i; } myColumns = columns; if (visibleColumns.IsReadOnly) { myVisibleColumns = visibleColumns; } else { myVisibleColumns = (int[])visibleColumns.Clone(); } myPreferLeftBlanks = preferLeftBlanks; }
/// <summary> /// Change the visible order to match the new order. This allows /// a secondary ordering permutation, such as that used by the header control, /// to work well with our permutation. This routine is optimized to expect a /// single column moving, but will work correctly with any order modification. /// </summary> /// <param name="oldOrder">The old order, should correspond to the current display order.</param> /// <param name="newOrder">The new order. Compare columns to old order to deduce current display order.</param> public void ChangeVisibleColumnOrder(int[] oldOrder, int[] newOrder) { var columns = myVisibleColumns.Length; if (oldOrder.Length != columns && newOrder.Length != columns) { throw new ArgumentException(VirtualTreeStrings.GetString(VirtualTreeStrings.InvalidColumnOrderArrayException)); } // The algorithm finds the old values in the new array and moves a column // as soon as it is found. To make an accurate move, this means that the // positions that have already moved need to be marked as already processed, // so we duplicate the old array to enable writing to it. var startOrder = oldOrder.Clone() as int[]; var startIndex = 0; var newIndex = 0; int startCurrent; int newCurrent; var totalMarked = 0; var permanentlyPassedMarked = 0; while (startIndex < columns && newIndex < columns) { if (-1 == (startCurrent = startOrder[startIndex])) { // Slot already handle, go look for one that hasn't been ++startIndex; ++permanentlyPassedMarked; } else if (startCurrent == (newCurrent = newOrder[newIndex])) { ++startIndex; ++newIndex; } else { // The values are different. The first step is to find the // new value in the old array. We care about the position of // the new value, as well as the number of already handled values // after the located, which can be calculated by seeing how // many marked values we pass on the value. var passedMarked = 0; var foundStartIndex = -1; int testValue; for (var i = startIndex + 1; i < columns; ++i) { testValue = startOrder[i]; if (testValue == newCurrent) { foundStartIndex = i; break; } else if (testValue == -1) { ++passedMarked; } } if (foundStartIndex == -1) { // We're not going to find any more break; } // Update the arrays accordingly MoveVisibleColumn(foundStartIndex + (totalMarked - permanentlyPassedMarked - passedMarked), newIndex); // Mark this node as processed ++totalMarked; startOrder[foundStartIndex] = -1; // Move to next new item ++newIndex; } } }