DataGridView 复制和粘贴






4.75/5 (33投票s)
在 DataGridView 和 Excel 之间进行复制和粘贴。
引言
我经常需要满足用户的需求,即他们希望在 Excel 中编辑数据;毕竟,他们是电子表格的狂热者。我曾尝试过导出/导入 CSV 文件,在 VS 中创建 Excel 模板,然后读取编辑后的结果。但这些方法似乎都未能满足用户的要求。
背景
最近,一位用户找到我,向我演示了她可以将 `DataGridView` 的内容复制到 Excel,并询问为什么在编辑完数据后不能将结果粘贴回 `DataGridView`。由于我不知道原来可以从 `DataGridView` 复制数据,所以我答应会去研究一下。以下是研究结果。
使用代码
获取数据
首先,我们需要一些数据 - 我使用 MS 示例数据库来完成此操作。我已包含一些 XML 文件中的示例数据用于测试。请注意,在从 datatable 写入 XML 时,您需要包含架构才能后续读取文件。
public DataTable GetData()
{
DataTable oTable = new DataTable();
FileInfo oFI = new FileInfo("SampleData.xml");
//test for the existing sample data xml
if (!oFI.Exists)
{
//get the data from the database
string sSQL = "SELECT SalesPersonID,FirstName," +
"LastName,SalesQuota FROM Sales.vSalesPerson";
oTable = GetTableSQL(sSQL);
//you need to write out the schema
//with the data to read it into the table.
oTable.WriteXml(oFI.FullName, XmlWriteMode.WriteSchema);
}
//read in the data from the xml file
oTable.ReadXml(oFI.FullName);
return oTable;
}
另外,我已将数据检索方法拆分到一个单独的类中;这可以是一个单独的项目或 Web Service,这也是客户端/服务器的基本设计原则。将您的 UI 与业务规则和数据处理层分开。
绑定到 DataGridView
直到最近,我一直直接将 datatable 绑定到 `DataGridView`;然而,我现在在其中加入了一个 `BindingSource`,因为它还有其他与本文无关的好处。所以,将数据绑定到 `DataGridView`。您需要将您不希望编辑的列设为只读。在此演示中,我只希望最后一列可编辑。在生产环境中,我会区分这些列,以便用户知道哪些列不能编辑。
private void btnGetData_Click(object sender, EventArgs e)
{
DataTable oTable = oData.GetData();
oBS.DataSource = oTable;
dgData.DataSource = oBS;
//readonly the columns that cannot be edited
dgData.Columns[0].ReadOnly = true;
dgData.Columns[1].ReadOnly = true;
dgData.Columns[2].ReadOnly = true;
}
我添加了一个上下文菜单,以便更容易地复制和粘贴数据。我还通过 `KeyDown` 事件在代码隐藏中实现了 ctrl/shift insert/delete 功能。
请注意,粘贴方法应移至一个实用工具类;这对粘贴方法很重要,因为您不希望将此代码散布在 UI 类中。复制方法很简单,它使用 `DataGridView` 的内置属性来获取数据并将其放入剪贴板。
DataObject d = dgData.GetClipboardContent();
Clipboard.SetDataObject(d);
这样用户就可以在 Excel 中操作数据了。用户随后可以选择任意数量的数据粘贴回 `DataGridView`。
注意:用户负责正确粘贴。在粘贴时无法验证数据放置的位置,尽管我有一些关于检查粘贴的附加列以查看它们是否与单元格内容匹配的想法。
private void PasteClipboard()
{
try
{
string s = Clipboard.GetText();
string[] lines = s.Split('\n');
int iFail = 0, iRow = dgData.CurrentCell.RowIndex;
int iCol = dgData.CurrentCell.ColumnIndex;
DataGridViewCell oCell;
foreach (string line in lines)
{
if (iRow < dgData.RowCount && line.Length > 0)
{
string[] sCells = line.Split('\t');
for (int i = 0; i < sCells.GetLength(0); ++i)
{
if (iCol + i < this.dgData.ColumnCount)
{
oCell = dgData[iCol + i, iRow];
if (!oCell.ReadOnly)
{
if (oCell.Value.ToString() != sCells[i])
{
oCell.Value = Convert.ChangeType(sCells[i],
oCell.ValueType);
oCell.Style.BackColor = Color.Tomato;
}
else
iFail++;
//only traps a fail if the data has changed
//and you are pasting into a read only cell
}
}
else
{ break; }
}
iRow++;
}
else
{ break; }
if (iFail > 0)
MessageBox.Show(string.Format("{0} updates failed due" +
" to read only column setting", iFail));
}
}
catch (FormatException )
{
MessageBox.Show("The data you pasted is in the wrong format for the cell");
return;
}
}
粘贴的数据是制表符分隔的文本行。我对任何公式都不感兴趣,只对 Excel 中的文本感兴趣。因此,遍历每一行并按制表符字符进行拆分;假设用户已被警告不要在 Excel 的数据中插入制表符字符。检查数据是否已更改(与原始数据相比),方法是比较剪贴板中的文本和单元格的文本值。如果值已更改,则检查单元格是否为只读。如果一切正常,则尝试将文本数据转换为与单元格信息相同的格式,捕获格式错误并中止粘贴。将数据放入单元格并将背景颜色更改为通知用户发生了某些事情。启用“保存”按钮。注意,数据尚未保存回数据库,更改仅保留在支持 `DataGridView` 的 datatable 中。用户仍有机会通过重新加载数据来撤销更改。此演示是从源数据重新加载的,但也可以通过取消 datatable 中的更改来实现。
通过使用 `DataView` 过滤 datatable 并仅获取已更改的记录来将数据保存到数据库。将数据写入数据库不是本演示的重点。
关注点
用 datatable 写入 XML schema 对我来说是新的;我经常使用 dataset 的 `WriteXML`,但不是 table 方法。
捕获不正确的格式错误极大地提高了可用性。
此功能尚未投入生产,因此我无法评估通过粘贴信息到数据库中的数据质量。我有一种不好的感觉,支持团队将会接到许多这样的电话:“我不知道它从哪里来的,它就是出现了。”
历史
- 2009 年 5 月 31 日:初始发布。