DataGridView:复制全部内容到剪贴板

15

我在一个 .Net 应用程序(V4 C# VS2010)中使用了一个 DataGridView,希望在点击按钮时将所有数据复制到剪贴板。没问题 -

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

问题在于用户可能已经选择了DataGrid上的一些单元格、行等,我不想改变那个选择。上面的代码显然会选择所有内容。最后可以使用dataGridView1.ClearSelection();,虽然有点改善但仍无法达到所需效果。

我可以保存所选的单元格:

var mySelectedCells = dataGridView1.SelectedCells;

但是,我如何在复制后重新选择DataGrid上的这些选定单元格?有没有一种简单的方法将选定单元格集合重新放回DataGrid中?也许有更好的方法可以在不影响当前选定单元格的情况下将整个网格复制到剪贴板中?

6个回答

14

如果您只是想将单元格的内容表示为文本并复制到剪贴板中,以制表符分隔,您可以尝试以下方法:

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

输出结果看起来与GetClipboardContent()相似,但要注意DataGridViewImageColumns或任何不是隐式字符串的类型。

编辑:安东尼是正确的,使用StringBuilder避免为每个连接分配新字符串。 新代码如下:

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

谢谢。最终我做了以下操作。我不得不添加另一个if语句if (row.Cells[i].Visible),因为有几列是不可见的,而这个解决方案复制了所有单元格,无论是否可见 - (dataGridView1.GetClipboardContent();仅复制可见内容)。不确定DataGridViewImageCell将如何处理,但这不是我特定应用程序的问题。 - Nigel
没问题,尼格尔,很高兴能帮忙。当我提到DataGridViewImageCell时,我只是想说如果你使用的不是DataGridViewTextBoxCell,我不确定代码会如何运行,但我相信如果你没有指定其他类型,那么该类型就是默认值。但如果你使用的是所有的TextBox单元格,那就不应该有问题了。干杯。 - B L
1
只是想提一下,如果 DataGridView 很大,你可能想使用 StringBuilder 而不仅仅是使用字符串拼接。 - Anthony

1
您可能希望包含列标题:
    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());
    }

目前你的回答不够清晰,请编辑并添加更多细节,以帮助其他人理解它如何回答问题。你可以在帮助中心找到有关如何编写好答案的更多信息。 - Community

1
这是一份带有复制表头和仅复制选定行选项的VB代码版本,以下是它的C#版本。
    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;
        }
    }

1
你应该改变DataGridView的multiselect属性。以下是代码:
private void copyToClipboard()
{
  dataGridView1.MultiSelect = True;
  dataGridView1.SelectAll();
  DataObject dataObj = dataGridView1.GetClipboardContent();
  if (dataObj != null)
  Clipboard.SetDataObject(dataObj);
}

这在一个.NET 7.0 Winform应用程序中有效,而B L使用SetText()的答案引发了异常。 - user0123456789

0
我认为下面的方法会完全满足你的需求。只需要在按钮单击事件中使用该方法并传入 DataGridView 的名称即可。
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

0

DataGridView的列可以可见/不可见,也可以按照创建顺序以外的顺序显示。这段代码可以处理这两种情况:

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接