DataGridView: Copy complete to clipboard

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;
        }
    }

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());

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);
}