Reputation: 175
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
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
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
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
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
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
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