在 .NET 4 中为 WPF DataGrid 实现复制和粘贴





5.00/5 (4投票s)
本文展示了如何使用 .NET 4 提供的 DataZGrid 在单元格之间复制和粘贴。
引言
更新 - 请参阅下面的限制 3。
在使用 .NET 4 添加的标准 DataGrid
时,我发现我需要实现复制和粘贴。在互联网上搜索后,找到的解决方案要么适用于 Silverlight,要么适用于某种预发布版本,你可以将其添加到 Visual Studio 2008 (.NET 3) 中。
令我感到恐惧的是,我必须实际弄清楚一些事情并编写一些代码。我是一名 WPF 新手,在过去的几个月里,我一直在复制互联网上的示例代码并修改以供我使用,但我认为我终于写了一些可能对其他人有用的东西,所以它来了。
感谢以下两个页面提供的帮助,并且一些代码取自第二个页面(在注释中注明)
- http://weblogs.asp.net/manishdalal/archive/2008/11/12/cross-browser-copy-and-paste-in-datagrid-with-excel-support-part-1.aspx
- http://blogs.msdn.com/b/vinsibal/archive/2008/09/25/pasting-content-to-new-rows-on-the-wpf-datagrid.aspx
该解决方案允许你在单元格之间进行一些有限的复制和粘贴。 在示例中,我有一个人员表,其中包含名字、姓氏和腿(一个数字 - 通常为 2)字段。在我正在开发的应用程序中,从一列复制到另一列毫无意义。因此,在此示例中,你无法将腿列复制到名字列(例如)。这是故意的,通过删除一些检查可以很容易地删除该限制。
需要解决的限制/问题!
- 它只处理字符串或整数。 通过更多的代码应该很容易扩展允许的类型,但也许可以使用不同的实现完全避免这种情况。
- 剪贴板仅从一种特定格式转换。 我需要看看从 Excel 复制是否有效 - 否则我可能需要将一些代码放回我删除的
DatagridHelper
中! - 重要提示 如果行被重新排序,它会破坏这段代码! 因此,我建议你不要使用这段代码。
Reto Ravisio 看到了这篇文章并提供了一个更好的解决方案。 我已经采用了它,稍微修改了一下,并提供了一个使用他的代码的完整解决方案文件
希望这将有助于像我这样的初学者使用该代码。
使用代码
我将介绍一些你必须复制到你的项目中的代码,但你必须查看整个解决方案才能获得所有需要的代码。 这是一个测试器,向你展示主要原则,以便你可以决定它是否适合你。
DataGrid
的声明如下
<DataGrid Name="dataGrid1" ItemsSource="{Binding PeopleList}"
SelectionUnit="Cell"
AutoGenerateColumns="True"
KeyDown="dataGrid1_KeyDown"
CopyingRowClipboardContent="dataGrid1_CopyingRowClipboardContent"
ColumnReordered="dataGrid1_ColumnReordered"/>
请注意,SelectionUnit
设置为 Cell
。 这允许选择单个单元格,而不是整行。 这些项取自 ObserverableCollection
,并且自动生成列。
KeyDown
事件被设置为检测粘贴操作
void dataGrid1_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.V &&
(Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control)
{
if (ValidatePaste() == false)
return;
viewModel.Paste(dataGrid1.CurrentColumn.DisplayIndex,
GetTargetRow(),
dataGrid1.SelectedCells.Count,
Clipboard.GetData(DataFormats.Text) as string);
}
}
如你所见,使用了视图模型。
视图模型的 Paste
函数使用 ClipboardHelper
类(取自介绍中提到的 MSDN 博客)将复制的单元格的字符串版本转换为表示每行单元格的字符串数组列表。 然后它循环遍历目标行,设置 ObservableCollection
。
反射仅使用表示属性名称的字符串来设置集合中的属性。 这样可以避免使用 switch
语句检查显示索引值并将其与属性匹配。 它会使代码编写起来非常乏味,并且不易重用。
SetProperty
函数实现了该功能
private void SetProperty(Person person, string propertyName, string cellItem)
{
// Use reflection to set the property to the value
Type type = person.GetType();
PropertyInfo prop = type.GetProperty(propertyName);
if (prop == null)
{
Debug.WriteLine("Property not found: " + propertyName);
return;
}
if (prop.PropertyType == typeof(string)) // only caters for string or integer types
prop.SetValue(person, cellItem, null);
else
prop.SetValue(person, int.Parse(cellItem), null);
person.OnPropertyChanged(propertyName);
}
你可以在上面的代码中看到仅具有字符串或整数的限制来自何处。
People
类是一个实现 INotifyPropertyChanged
的简单容器类
class Person : INotifyPropertyChanged
{
public Person(string first, string second)
{
FirstName = first;
SecondName = second;
Legs = 2;
}
public string FirstName { set; get; }
public string SecondName { set; get; }
public int Legs { set; get; }
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
public virtual void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
填充属性名称和显示索引映射的代码(在 VM Paste
函数中使用)依赖于代码隐藏中的这段代码
private void dataGrid1_ColumnReordered(object sender, DataGridColumnEventArgs e)
{
viewModel.ClearColumnMapping();
foreach (DataGridColumn col in dataGrid1.Columns)
{
viewModel.AddColumnMapping(col.DisplayIndex, col.SortMemberPath);
}
}
我假设 SortMemberPath
将始终与 People
类中的属性名称相同。 在我的例子中是这样的。
历史
- 2011-09-01:一个新版本,它在列被重新排序时(而不是加载时)获取列信息。 更新了一个基于Reto's article的可下载完整解决方案。