Nigel
Nigel

Reputation: 175

DataGridView: Copy complete to clipboard

I have a DataGridView in a .Net application (V4 C# VS2010) & want to copy all the data to the clipboard on the click of a button. No problem -

private void copyToClipboard()
{
    dataGridView1.SelectAll();
    DataObject dataObj = dataGridView1.GetClipboardContent();
    if (dataObj != null)
        Clipboard.SetDataObject(dataObj);
}

Problem is that the user might already have some cells, rows etc selected on the DataGrid & I don't really want to change that selection. The above obviously selects everything. I could dataGridView1.ClearSelection(); at the end which is marginally better but still doesn't achieve what's required.

I can save the selected cells:

var mySelectedCells = dataGridView1.SelectedCells;

but how do I get those selected cells reselected on the DataGrid after the copy? Is there an easy way to get the selected cells collection back into the DataGrid? Perhaps there is a better way to get the whole grid copied to the clipboard in the first place without affecting presently selected cells?

Upvotes: 16

Views: 28947

Answers (6)

Lew
Lew

Reputation: 11

You might want to include the column headers:

    private void copyAllToClipboard()
    {
        var newline = System.Environment.NewLine;
        var tab = "\t";
        var clipboard_string = new StringBuilder();
        int i;

        for (i = 0; i < this.Columns.Count - 1; i++)
        {
            clipboard_string.Append(this.Columns[i].Name);
            clipboard_string.Append(tab);
        }
        clipboard_string.Append(this.Columns[i].Name);
        clipboard_string.Append(newline);

        foreach (DataGridViewRow row in this.Rows)
        {
            for ( i = 0; i < row.Cells.Count - 1; i++)
            {
                    clipboard_string.Append(row.Cells[i].Value);
                    clipboard_string.Append(tab);
            }
            clipboard_string.Append(row.Cells[i].Value);
            clipboard_string.Append(newline);
        }

        Clipboard.SetText(clipboard_string.ToString());
    }

Upvotes: 1

MB_18
MB_18

Reputation: 2251

You should change multiselect property of DataGridView. Here is the code:

private void copyToClipboard()
{
  dataGridView1.MultiSelect = True;
  dataGridView1.SelectAll();
  DataObject dataObj = dataGridView1.GetClipboardContent();
  if (dataObj != null)
  Clipboard.SetDataObject(dataObj);
}

Upvotes: 1

Vadim
Vadim

Reputation: 414

DataGridView columns can be visible/invisible and also can be displayed in different order then the order they were created. This code takes care of both:

public static void CopyGridViewToClipboard(DataGridView gvCopy)
    {
        if (gvCopy == null) return;
        StringBuilder s = new StringBuilder();

        int offset = gvCopy.ColumnHeadersVisible ? 1 : 0;
        int visibleColumnsCount = 0;

        //count visible columns and build mapping between each column and it's display position
        Dictionary<int, int> indexMapping = new Dictionary<int, int>();
        int currIndex = 0;
        int lastFoundMinDisplayIndex = -1;
        for (int j = 0; j < gvCopy.ColumnCount; j++)
        {
            //find min DisplayIndex >= currIndex where column is visible
            int minDisplayIndex = 100000;
            int minDisplayIndexColumn = 100000;
            for (int k = 0; k < gvCopy.ColumnCount; k++)
            {
                if ((gvCopy.Columns[k].Visible) && (gvCopy.Columns[k].DisplayIndex >= currIndex) && (gvCopy.Columns[k].DisplayIndex > lastFoundMinDisplayIndex))
                {
                    if (gvCopy.Columns[k].DisplayIndex < minDisplayIndex)
                    {
                        minDisplayIndex = gvCopy.Columns[k].DisplayIndex;
                        minDisplayIndexColumn = k;
                    }
                }
            }

            if (minDisplayIndex == 100000) break;

            indexMapping.Add(minDisplayIndexColumn, currIndex);

            lastFoundMinDisplayIndex = minDisplayIndex;
            currIndex++;
        }
        visibleColumnsCount = currIndex;

        //put data in temp array -- required to position columns in display order
        string[,] data = new string[gvCopy.RowCount + offset, visibleColumnsCount];

        if (gvCopy.ColumnHeadersVisible)
        {
            for (int j = 0; j < gvCopy.ColumnCount; j++)
            {
                if (gvCopy.Columns[j].Visible)
                {
                    data[0, indexMapping[j]] = gvCopy.Columns[j].HeaderText;
                }
            }
        }

        for (int i = 0; i < gvCopy.RowCount; i++)
        {
            for (int j = 0; j < gvCopy.ColumnCount; j++)
            {
                if (gvCopy.Columns[j].Visible)
                {
                    data[i + offset, indexMapping[j]] = gvCopy[j, i].FormattedValue.ToString();
                }
            }
        }

        //copy data
        for (int i = 0; i < gvCopy.RowCount + offset; i++)
        {
            for (int j = 0; j < visibleColumnsCount; j++)
            {
                s.Append(data[i, j]);

                s.Append("\t");
            }
            s.Append("\r\n");
        }
        Clipboard.SetDataObject(s.ToString());
    }

Upvotes: 0

AustinAllan
AustinAllan

Reputation: 21

Here is a version of the VB code in C# with options to copy headers and to only copy selected rows.

    private void CopyDataGridViewToClipboard(DataGridView dgv, bool includeHeaders = true, bool allRows = false) 
    {
        // copies the contents of selected/all rows in a data grid view control to clipboard with optional headers
        try
        {
            string s = "";
            DataGridViewColumn oCurrentCol = dgv.Columns.GetFirstColumn(DataGridViewElementStates.Visible);
            if (includeHeaders)
            {                   
                do
                {
                    s = s + oCurrentCol.HeaderText + "\t";
                    oCurrentCol = dgv.Columns.GetNextColumn(oCurrentCol, DataGridViewElementStates.Visible, DataGridViewElementStates.None);
                }
                while (oCurrentCol != null);
                s = s.Substring(0, s.Length - 1);
                s = s + Environment.NewLine;    //Get rows
            }
            foreach (DataGridViewRow row in dgv.Rows)
            {
                oCurrentCol = dgv.Columns.GetFirstColumn(DataGridViewElementStates.Visible);

                if (row.Selected || allRows)
                {
                    do
                    {
                        if (row.Cells[oCurrentCol.Index].Value != null) s = s + row.Cells[oCurrentCol.Index].Value.ToString();
                        s = s + "\t";
                        oCurrentCol = dgv.Columns.GetNextColumn(oCurrentCol, DataGridViewElementStates.Visible, DataGridViewElementStates.None);
                    }
                    while (oCurrentCol != null);
                    s = s.Substring(0, s.Length - 1);
                    s = s + Environment.NewLine;
                }                                      
            }
            Clipboard.SetText(s);
        }
        catch (Exception ex)
        {
            toolStripStatusLabel2.Text = @"Error: " + ex.Message;
        }
    }

Upvotes: 1

samithagun
samithagun

Reputation: 693

I think below method will exactly do what you want. Just call this method with the DataGridView name at the button click event.

Private Sub CopyDataGridViewToClipboard(ByRef dgv As DataGridView)
    Try
        Dim s As String = ""
        Dim oCurrentCol As DataGridViewColumn    'Get header
        oCurrentCol = dgv.Columns.GetFirstColumn(DataGridViewElementStates.Visible)
        Do
            s &= oCurrentCol.HeaderText & Chr(Keys.Tab)
            oCurrentCol = dgv.Columns.GetNextColumn(oCurrentCol, _
               DataGridViewElementStates.Visible, DataGridViewElementStates.None)
        Loop Until oCurrentCol Is Nothing
        s = s.Substring(0, s.Length - 1)
        s &= Environment.NewLine    'Get rows
        For Each row As DataGridViewRow In dgv.Rows
            oCurrentCol = dgv.Columns.GetFirstColumn(DataGridViewElementStates.Visible)
            Do
                If row.Cells(oCurrentCol.Index).Value IsNot Nothing Then
                    s &= row.Cells(oCurrentCol.Index).Value.ToString
                End If
                s &= Chr(Keys.Tab)
                oCurrentCol = dgv.Columns.GetNextColumn(oCurrentCol, _
                      DataGridViewElementStates.Visible, DataGridViewElementStates.None)
            Loop Until oCurrentCol Is Nothing
            s = s.Substring(0, s.Length - 1)
            s &= Environment.NewLine
        Next    'Put to clipboard
        Dim o As New DataObject
        o.SetText(s)
        Clipboard.SetDataObject(o, True)

    Catch ex As Exception
        ShowError(ex, Me)
    End Try
End Sub

Upvotes: 0

B L
B L

Reputation: 1592

I suppose if you just wanted to represent the contents of the cells as text and copy them to the clipboard, tab-delimited, you could do something like:

    var newline = System.Environment.NewLine;
    var tab = "\t";
    var clipboard_string = "";

    foreach (DataGridViewRow row in dataGridView1.Rows)
    {
         for (int i=0; i < row.Cells.Count; i++)
         {
              if(i == (row.Cells.Count - 1))
                   clipboard_string += row.Cells[i].Value + newline;
              else
                   clipboard_string += row.Cells[i].Value + tab;
         }
    }

    Clipboard.SetText(clipboard_string);

The output seems pretty similar to that of the GetClipboardContent(), but be careful for any DataGridViewImageColumns or any type that isn't implicitly a string.

Edit: Anthony is correct, use StringBuilder to avoid allocating a new string for every concatenation. The new code:

    var newline = System.Environment.NewLine;
    var tab = "\t";
    var clipboard_string = new StringBuilder();

    foreach (DataGridViewRow row in dataGridView1.Rows)
    {
        for (int i = 0; i < row.Cells.Count; i++)
        {
            if (i == (row.Cells.Count - 1))
                clipboard_string.Append(row.Cells[i].Value + newline);
            else
                clipboard_string.Append(row.Cells[i].Value + tab);
        }
    }

    Clipboard.SetText(clipboard_string.ToString());

Upvotes: 15

Related Questions