mhttk
mhttk

Reputation: 1696

WPF Datagrid: copy additional rows to clipboard

I am working with the WPF Datagrid and I am trying to enhance/change the copy & paste mechanism.

When the user selects some cells and then hit CTRL + C, the underlying controls is able to catch the CopyingRowClipboardContent event.

 this.mainDataGrid.CopyingRowClipboardContent 
              += this.DatagridOnCopyingRowClipboardContent;

In this method, some cells are added to both the header and the rows, hence resulting in a "wider" grid.

    private void DatagridOnCopyingRowClipboardContent(
        object sender, 
        DataGridRowClipboardEventArgs dataGridRowClipboardEventArgs)
    {
        // this is fired every time a row is copied
        var allContent = dataGridRowClipboardEventArgs.ClipboardRowContent;

        allContent.Insert(0, new DataGridClipboardCellContent(
                                            null, 
                                            this.mainDataGrid.Columns[0], 
                                            "new cell"));
    }

At this point I am stuck because I am trying to add an additional row before the header and two after the last row (see image below).

Any ideas? Suggestions?

Please note I am not interested in an MVVM way of doing it here.

enter image description here

Upvotes: 5

Views: 7462

Answers (3)

Markus
Markus

Reputation: 438

I realize this an older post, but I post this solution for completeness. I could not find a more recent question on copying datagrid rows to clipboard. Using Clipboard.SetData belies the ClipboardRowContent intention.

For my needs, I'm re-pasting back into the e.ClipboardRowContent the row I would like. The cell.Item is all the information I need for each selected row.

Hint: I was getting duplicates without doing an e.ClipboardRowContent.Clear(); after using the e.ClipboardRowContent . I was clearing before and using DataGrid.SelectedItems to build the rows.

private void yourDataGrid_CopyingRowClipboardContent(object sender,    DataGridRowClipboardEventArgs e)
{
var dataGridClipboardCellContent = new List<DataGridClipboardCellContent>(); 

string prevCell = "";
string curCell = ""; 

foreach (DataGridClipboardCellContent cell in e.ClipboardRowContent)
{
    //Gives you access item.Item or item.Content here
    //if you are using your struct (data type) you can recast it here curItem = (yourdatatype)item.Item;        
    curItem = cell.Item.ToString(); 

    if (curCell != prevCell)
        dataGridClipboardCellContent.Add(new DataGridClipboardCellContent(item, item.Column, curCell)); 

    prevCell = curCell;  

}
e.ClipboardRowContent.Clear();

//Re-paste back into e.ClipboardRowContent, additionally if you have modified/formatted rows to your liking
e.ClipboardRowContent.AddRange(dataGridClipboardCellContent);

}

Upvotes: 0

skofgar
skofgar

Reputation: 1617

Are you trying to copy the content into e.g. Excel later? If so, here's what I did:

/// <summary>
/// Copy the data from the data grid to the clipboard
/// </summary>
private void copyDataOfMyDataGridToClipboard(object sender, EventArgs e)
{
    // Save selection
    int selectedRow = this.myDataGrid.SelectedRows[0].Index;

    // Select data which you would like to copy
    this.myDataGrid.MultiSelect = true;
    this.myDataGrid.SelectAll();

    // Prepare data to be copied (that's the interesting part!)
    DataObject myGridDataObject = this.myDataGrid.GetClipboardContent();
    string firstRow = "FirstRowCommentCell1\t"+ this.someDataInCell2 +"..\r\n";
    string lastTwoRows = "\r\nBottomLine1\t" + yourvariables + "\r\nBottomLine2";
    string concatenatedData = firstRow + myGridDataObject.GetText() + lastTwoRows;

    // Copy into clipboard
    Clipboard.SetDataObject(concatenatedData);

    // Restore settings
    this.myDataGrid.ClearSelection();
    this.myDataGrid.MultiSelect = false;

    // Restore selection
    this.myDataGrid.Rows[selectedRow].Selected = true;
}

In my case I had some static header's which could be easily concatenated with some variables. Important to write are the \t for declaring another cell, \r\n declares the next row

Upvotes: 0

Damascus
Damascus

Reputation: 6651

Here is a code snippet that might help you. This snippet is mainly used to retrieve all of your selected data, including headers (I removed the RowHeaders part since you apparently don't need it). If you have any question please let me know. I left a few part with comments written in capital letters: this is where you should add your own data The good part of this approach is that it directly works with your DataGrid's ItemsSource and NOT the DataGridCell. The main reason being: if you use DataGridCell on a formatted number for example, you will NOT get the actual value, but just the formatted one (say your source is 14.49 and your StringFormat is N0, you'll just copy 14 if you use a "regular" way)

   /// <summary>
    /// Handles DataGrid copying with headers
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void OnCopyingDataGrid(object sender, ExecutedRoutedEventArgs e)
    {
        // First step: getting the coordinates list of all cells selected
        IList<Tuple<int, int>> cellsCoordinatesList = new List<Tuple<int, int>>();
        HashSet<int> rowList = new HashSet<int>();
        HashSet<int> columnList = new HashSet<int>();
        foreach (System.Windows.Controls.DataGridCellInfo cell in this.SelectedCells)
        {
            int column = cell.Column.DisplayIndex;
            int row = this.Items.IndexOf(cell.Item);
            cellsCoordinatesList.Add(new Tuple<int, int>(row, column));
            if (!rowList.Contains(row))
            {
                rowList.Add(row);
            }
            if (!columnList.Contains(column))
            {
                columnList.Add(column);
            }
        }

        // Second step: Create the table to copy/paste
        object[,] arrayToBeCopied = new object[rowList.Count, columnList.Count + 1];
        IList<string> colHead = this.ColumnHeaders.Cast<object>().Select(h => h.ToString()).ToList();
        for (int row = 0; row < arrayToBeCopied.GetLength(0); row++)
        {
            for (int column = 0; column < arrayToBeCopied.GetLength(1); column++)
            {
                if (row == 0)
                {
                    arrayToBeCopied[row, column] = colHead[columnList.ElementAt(column - 1)];
                }
                else
                {
                    arrayToBeCopied[row, column] = // WHATEVER YOU WANT TO PUT IN THE CLIPBOARD SHOULD BE HERE. THIS SHOULD GET SOME PROPERTY IN YOUR ITEMSSOURCE

                }
            }
        }

        // Third step: Converting it into a string
        StringBuilder sb = new StringBuilder();

        // HERE, ADD YOUR FIRST ROW BEFORE STARTING TO PARSE THE COPIED DATA

        for (int row = 0; row < arrayToBeCopied.GetLength(0); row++)
        {
            for (int column = 0; column < arrayToBeCopied.GetLength(1); column++)
            {
                sb.Append(arrayToBeCopied[row, column]);
                if (column < arrayToBeCopied.GetLength(1) - 1)
                {
                    sb.Append("\t");
                }
            }
            sb.Append("\r\n");
        }

        // AND HERE, ADD YOUR LAST ROWS BEFORE SETTING THE DATA TO CLIPBOARD

        DataObject data = new DataObject();
        data.SetData(DataFormats.Text, sb.ToString());

        Clipboard.SetDataObject(data);
    }

Upvotes: 3

Related Questions