Copy to clipboard and multiple selection

Jul 23, 2009 at 1:46 PM

Hi.

I've made some tests and I saw that if there is a multiple selection (cells that aren't adjacent), only the first cell is copied.

It's OK, but there is a little case which isn't "pretty": if you select manually several cells which are adjacent (or by code with SelectCell), only the first cell is copied.
I think it could be pretty to check if the selected cells are, in fact, a range.

 

I've done this, which overwrites Ctrl+C (fast written, not optimized, but works):

            SourceGrid.RangeRegion region = new SourceGrid.RangeRegion();

            int minRow = 0, maxRow = 0, minCol = 0, maxCol = 0;
            int rows = 0, cols = 0;

            SourceGrid.RangeRegion selRegion = grid.Selection.GetSelectionRegion();
            int[] selRows = selRegion.GetRowsIndex();
            int[] selCols = selRegion.GetColumnsIndex();
            Array.Sort(selRows);
            Array.Sort(selCols);

            rows = selRows.Length;
            minRow = selRows[0];
            maxRow = selRows[rows - 1];

            cols = selCols.Length;
            minCol = selCols[0];
            maxCol = selCols[cols - 1];
            
            if (maxRow - minRow == rows - 1 && maxCol - minCol == cols - 1 && selRegion.GetCellsPositions().Count == rows * cols)
            {
                region.Add(new SourceGrid.Range(minRow, minCol, maxRow, maxCol));
            }
            else
            {
                foreach (SourceGrid.Position pCell in grid.Selection.GetSelectionRegion().GetCellsPositions())
                    region.Add(pCell);
            }

            this.grid.PerformCopy(region);

Jul 23, 2009 at 3:42 PM

And here I post another function that allows to make copy like MS Excel (also not really optimized, but works correctly!).

I mean copy multiple selections when the same columns are selected and each row. For example copy cells (1,1) (1,3) (3,1) (3,3) or whole rows 1, 3, 4 and 7 or whole cols 2, 7, 8 or cols 2 to 6 or rows 3, 4 and 8, etc.
In these cases, the unselected rows/cols/cells are ignored and so the output selection/range is smaller than original/input selection.

I think it could be useful for somebody...

        public void CopyToClipboard()
        {
            // Declarations
            int minRow = 0, maxRow = 0, minCol = 0, maxCol = 0;
            int rows = 0, cols = 0;

            // Analyze selected range (rows/cols)
            SourceGrid.RangeRegion selRegion = grid.Selection.GetSelectionRegion();
            int[] selRows = selRegion.GetRowsIndex();
            int[] selCols = selRegion.GetColumnsIndex();
            Array.Sort(selRows);
            Array.Sort(selCols);

            rows = selRows.Length;
            minRow = selRows[0];
            maxRow = selRows[rows - 1];

            cols = selCols.Length;
            minCol = selCols[0];
            maxCol = selCols[cols - 1];

            // Check that the same columns are selected on each row: if not, it's a multiple selection => not allowed
            for (int r = 0; r < rows; r++)
            {
                for (int c = 0; c < cols; c++)
                {
                    if (!grid.Selection.IsSelectedCell(new SourceGrid.Position(selRows[r], selCols[c])))
                        throw new SelectionCopyException("Unable to copy multiple selection!");
                }
            }

            // Array tables that allows to find an ouput clipboard range row/col from input cell
            Hashtable rowsRefs = new Hashtable();
            for (int i = 0; i < rows; i++)
                rowsRefs.Add(selRows[i], i);

            Hashtable colsRefs = new Hashtable();
            for (int i = 0; i < cols; i++)
                colsRefs.Add(selCols[i], i);

            // Ignore row headers
            if (minCol == 0)
                cols--;

            // Create a 2-D array with all cells to copy
            string[,] cells = new string[rows, cols];
            foreach (SourceGrid.Position pCell in grid.Selection.GetSelectionRegion().GetCellsPositions())
            {
                if (grid[pCell.Row, pCell.Column] != null && pCell.Column > 0) // Ignore row header and empty cells
                {
                    if (grid[pCell.Row, pCell.Column].Editor != null)
                        cells[(int)rowsRefs[pCell.Row], (int)colsRefs[pCell.Column]] = grid[pCell.Row, pCell.Column].Editor.ValueToString(grid[pCell.Row, pCell.Column].Value);
                    else
                        cells[(int)rowsRefs[pCell.Row], (int)colsRefs[pCell.Column]] = grid[pCell.Row, pCell.Column].Value.ToString();
                }
            }

            // New clipboard DataObject
            cbDataObject = new System.Windows.Forms.DataObject();
            cbDataObject.SetData("SourceGrid.Grid", this);
            
            // Build string from cells content
            System.Text.StringBuilder builder = new System.Text.StringBuilder();

            int arrayRow = 0;
            for (int r = 0; r < rows; r++, arrayRow++)
            {
                int arrayCol = 0;
                for (int c = 0; c < cols; c++, arrayCol++)
                {
                    builder.Append(cells[arrayRow, arrayCol]);
                    if (c < cols - 1)
                        builder.Append('\t');
                }
                if (r < rows - 1)
                    builder.Append("\x0D\x0A");
            }

            // Copy string object to clipboard
            cbDataObject.SetData(typeof(string), builder.ToString());
            System.Windows.Forms.Clipboard.SetDataObject(cbDataObject);
        }

Note: I didn't want to modify the original SourceGrid source code, so I've made a separate function.

In SourceGrid, the following functions should be modified:

- GridVirtual.PerformCopy (only the call to LoadData)
- RangeData.LoadData
- Perhaps RangeData.DataToString (or new function of all in LoadData)