Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Базы данных под .NET > Copy-Paste для DataGridView(Virtual Mode)


Автор: allknower 20.6.2008, 15:41
На данном этапе реализовал копирование одной ячейки:

Код

        private void copyClipMenuItem_Click(object sender, EventArgs e)
        {

            DataFormats.Format myFormat = DataFormats.GetFormat("SimpleData");
            DataObject myDataObject = new DataObject(myFormat.Name, grid.CurrentCell.Value);
            Clipboard.SetDataObject(myDataObject);
        }

        private void pasteClipMenuItem_Click(object sender, EventArgs e)
        {
            DataFormats.Format myFormat = DataFormats.GetFormat("SimpleData");
            string oldName = (grid.CurrentCell.Value as SimpleData).name;
            grid.CurrentCell.Value = Clipboard.GetDataObject().GetData(myFormat.Name);
            (grid.CurrentCell.Value as SimpleData).name = oldName;
        }


Теперь встала задача реализовать копирование любой выделенной области. То есть строк, столбцов, нескольких ячеяк нескольких строк:


+------+------+------+------+------+
|      |      |      |      |      |
+------+------+------+------+------+
|      |      |  S   |  S   |      |
+------+------+------+------+------+
|      |      |  S   |  S   |      |
+------+------+------+------+------+

S - выделенные ячейки


Я создал класс Range

Код

    [Serializable]
    public class Range
    {
        private SimpleData[,] _range;

        public SimpleData this[int rowIndex, int colIndex]
        {
            get
            {
                return (this._range[rowIndex, colIndex] as SimpleData);
            }

            set 
            {
                this._range[rowIndex, colIndex] = value;
            }
        }

        public Range(int rowCount, int colCount)
        {
            _range = new SimpleData[rowCount, colCount];
        }
    }


Идея такова: оборачиваем коллекцию DataGridView.SelectedCell в этот класс и толкаем в буфер.
При вставке - выталкиваем из буфера и втавляем в грид начиная с ячейки, над которой было вызвано меню. Все что не вошло усекаем.

Проблема в том что DataGridView.SelectedCell - есть ничто иное как ArrayList в котором списком хранятся выделенные ячейки. Как мне получить количество колонок и столбцов?? Свойства DataGridView.SelectedRow.Count и DataGridView.SelectedColumn.Count при неполном выборе (как на картинке) хранят нули. Хотя реально выбрано две строки и два столбца. Хотелось бы увидеть какой то SelectedRange (на подобие Delphi'йского. У StringGrid'a кажется таковой был.)

Автор: Idsa 21.6.2008, 02:00
Цитата(allknower @  20.6.2008,  19:41 Найти цитируемый пост)
Свойства DataGridView.SelectedRow.Count и DataGridView.SelectedColumn.Count при неполном выборе (как на картинке) хранят нули. 

Ага, есть такое.
В MSDN'е объяснено такое поведение:
Цитата

The SelectionMode property must be set to FullColumnSelect or ColumnHeaderSelect for the SelectedColumns property to be populated with selected columns.

The SelectionMode property must be set to FullRowSelect or RowHeaderSelect for the SelectedRows property to be populated with selected rows.

Такое поведение вполне логично. Стоит представить, что ячейки выделены не так, как Вы описали, а, например, вот так:
+------+------+------+------+------+
|      |      |      |      |      |
+------+------+------+------+------+
|      |      |  S   |  S   |      |
+------+------+------+------+------+
|      |      |  S   |  S   |      |     S
+------+------+------+------+------+
Действительно, непонятно, чему должны быть равны SelectedRowCount и SelectedColumnCount. Так что пусть уж они будут равны нулю от греха подальше smile

Это небольшой офтоп. А теперь к делу.
Вообще, в MSDN'е в http://msdn.microsoft.com/en-us/library/ha5xt0d9.aspx есть интересная заметка по поводу использования SelectionMode.CellSelect
Цитата

Avoid cell-based selection modes. Instead, set the SelectionMode property to FullRowSelect or FullColumnSelect. 

Мило, да? smile

На фоне всего этого безобразия, думаю, придется Вам писать алгоритм на базе DataGridViewCell.ColumnIndex и DataGridViewCell.RowIndex.
Мне на ум пришел примерно следующий алгоритм.
1. Находим минимальный ColumnIndex и минимальный RowIndex из SelectedCells.
2. Вычитаем для всех выделенных ячеек из их ColumnIndex минимальный ColumnIndex, а из их RowIndex минимальный RowIndex. Если рассматривать предыдущий пример, то мы получим ячейки, которые расположились бы в DataGridView следующим образом:
+------+------+------+------+------+
|   S   |   S   |      |      |      |
+------+------+------+------+------+
|   S   |   S   |      |   S   |      |  
+------+------+-----+------+------+
|      |      |     |     |      |     |
+------+------+------+------+------+
На самом деле мы работаем с массивом ячеек: эта таблица только для демонстрации.
3. При вставке располагаем наши ячейки в ячейке с номером колонки, равным: номер колонки, в которой было вызвано контекстное меню, + "нормированный" номер текущей вставляемой ячейки. Аналогично со строками.

P. S. Не исключаю, что есть красивый способ реализовать подобный функционал... но поверхностный гуглинг не помог.

Автор: allknower 23.6.2008, 13:38
Idsa, обнаружился такой вот негатиффчик:

Пи выделении строк в ArrayList "DataGridView.SelectedCell" они попадают в совершенно немыслемом порядке.

Выкладываю то что наваял, может кому пригодится. Конечно сыровато, но работает. Пинами не ногать!!!

Класс Range
Код

    [Serializable]
    public class Range
    {
        private SimpleData[,] _range;

        public SimpleData this[int rowIndex, int colIndex]
        {
            get
            {
                return (this._range[rowIndex, colIndex] as SimpleData);
            }

            set
            {
                this._range[rowIndex, colIndex] = value;
            }
        }

        public int RowCount
        {
            get { return _range.GetLength(0); }
        }

        public int ColCount
        {
            get { return _range.GetLength(1); }
        }

public Range(System.Windows.Forms.DataGridViewSelectedCellCollection SelectedRange)
        {
            int maxRow = 0; int minRow = SelectedRange[0].RowIndex;
            int maxCol = 0; int minCol = SelectedRange[0].ColumnIndex;
            for (int indx = 0; indx < SelectedRange.Count; indx++)
            {
                if (SelectedRange[indx].RowIndex > maxRow)
                { maxRow = SelectedRange[indx].RowIndex; }
                else if (SelectedRange[indx].RowIndex < minRow)
                { minRow = SelectedRange[indx].RowIndex; }

                if (SelectedRange[indx].ColumnIndex > maxCol)
                { maxCol = SelectedRange[indx].ColumnIndex; }
                else if (SelectedRange[indx].ColumnIndex < minCol)
                { minCol = SelectedRange[indx].ColumnIndex; }
            }
            int rowCount = maxRow - minRow + 1;
            int colCount = maxCol - minCol + 1;
            _range = new SimpleData[rowCount, colCount];

            foreach (System.Windows.Forms.DataGridViewCell cell in SelectedRange)
            {
                _range[cell.RowIndex - minRow, cell.ColumnIndex - minCol] = (cell.Value as SimpleData);
            }
        }

SimpleData здесь это просто пользовательский тип данных, не более того. На логику никак не влияет.

Клик по пунктам Copy и Paste:
Код

 private void copyClipMenuItem_Click(object sender, EventArgs e)
        {
            Range r = new Range(grid.SelectedCells);
            
            DataFormats.Format myFormat = DataFormats.GetFormat("ArrayOffCell");
            DataObject myDataObject = new DataObject(myFormat.Name, r);
            Clipboard.SetDataObject(myDataObject);
        }

        private void pasteClipMenuItem_Click(object sender, EventArgs e)
        {
            int startRowIndx = grid.CurrentCell.RowIndex;
            int startColIndx = grid.CurrentCell.ColumnIndex;
            DataFormats.Format myFormat = DataFormats.GetFormat("ArrayOffCell");
            Range r = (Clipboard.GetDataObject().GetData(myFormat.Name) as Range);
            for (int rowIndx = 0; rowIndx < r.RowCount; rowIndx++)
            for (int colIndx = 0; colIndx < r.ColCount; colIndx++ )
            {
                try
                {
                    grid.Rows[startRowIndx + rowIndx].Cells[startColIndx + colIndx].Value = r[rowIndx, colIndx];
                }
                catch (Exception ex)
                { MessageBox.Show(ex.Message, "Ошибка", MessageBoxButtons.OK); }
            }
        }


Этот код выдаст ошибку если попытаться скопировать и вставить строку, выбранную за заголовок (там где стрелочка)... Это нужно обробатывать отдельно.

Автор: stashe 24.6.2008, 13:17
Код

private void copyToClipBoardToolStripMenuItem_Click(object sender, EventArgs e)
        {
            dataGridView.ClipboardCopyMode = DataGridViewClipboardCopyMode.EnableWithoutHeaderText;
            Clipboard.SetDataObject(dataGridView.GetClipboardContent());
        }       


А это не в тему?

Автор: allknower 24.6.2008, 15:46
Цитата(stashe @ 24.6.2008,  13:17)
Код

private void copyToClipBoardToolStripMenuItem_Click(object sender, EventArgs e)
        {
            dataGridView.ClipboardCopyMode = DataGridViewClipboardCopyMode.EnableWithoutHeaderText;
            Clipboard.SetDataObject(dataGridView.GetClipboardContent());
        }       


А это не в тему?

Сколько я не колдовал с ячейками - вытащить их из буфера у меня так и не получилось. Может что не правильно делал? Какой format нужно указать в 

Код

Clipboard.GetDataObject.GetData(format);


C пользовательским форматом у меня не заработало. :(

А самописный Range развязал руки, и теперь я копирую, дублирую (как в Excel) как хочу...

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)