65.9K
CodeProject 正在变化。 阅读更多。
Home

将数据粘贴到 WPF DataGrid 的另一种实现

starIconstarIconstarIconstarIconstarIcon

5.00/5 (3投票s)

2011 年 8 月 28 日

CPOL

2分钟阅读

viewsIcon

30504

如何粘贴数据,而不是制造混乱

引言

我受到文章 在 WPF DataGrid .NET 4 中实现复制和粘贴[^] 的启发,尝试了一种替代实现。
上述文章中的解决方案解析剪贴板并将数据复制到模型中。
这种技术有几个缺点。第一个是当列/行被过滤或排序时,它很容易混淆。第二个缺点是它需要转换器将来自剪贴板的字符串数据转换为模型所需的正确类型。
此处提供的函数试图通过将数据复制回网格而不是模型来规避这些类型的问题。实现中最棘手的部分是弄清楚单元格在屏幕上的布局,以便剪贴板中的数据粘贴到预期位置。

使用代码

我将从代码开始,然后尝试解释该函数的作用。
// 2-dim array containing clipboard data
string[][] clipboardData =
    ((string)Clipboard.GetData(DataFormats.Text)).Split('\n')
    .Select(row =>
        row.Split('\t')
        .Select(cell =>
            cell.Length > 0 && cell[cell.Length - 1] == '\r' ?
            cell.Substring(0, cell.Length - 1) : cell).ToArray())
    .Where(a => a.Any(b => b.Length > 0)).ToArray();

// the index of the first DataGridRow
int startRow = dataGrid.ItemContainerGenerator.IndexFromContainer(
    (DataGridRow)dataGrid.ItemContainerGenerator.ContainerFromItem
    (dataGrid.CurrentCell.Item));

// the destination rows 
//  (from startRow to either end or length of clipboard rows)
DataGridRow[] rows =
    Enumerable.Range(
        startRow, Math.Min(dataGrid.Items.Count, clipboardData.Length))
    .Select(rowIndex =>
        dataGrid.ItemContainerGenerator.ContainerFromIndex(rowIndex) as DataGridRow)
    .Where(a => a != null).ToArray();

// the destination columns 
//  (from selected row to either end or max. length of clipboard colums)
DataGridColumn[] columns =
    dataGrid.Columns.OrderBy(column => column.DisplayIndex)
    .SkipWhile(column => column != dataGrid.CurrentCell.Column)
    .Take(clipboardData.Max(row => row.Length)).ToArray();

for (int rowIndex = 0; rowIndex < rows.Length; rowIndex++)
{
    string[] rowContent = clipboardData[rowIndex];
    for (int colIndex = 0; colIndex < columns.Length; colIndex++)
    {
        string cellContent =
            colIndex >= rowContent.Length ? "" : rowContent[colIndex];
        columns[colIndex].OnPastingCellClipboardContent(
            rows[rowIndex].Item, cellContent);
    }
}
请注意,上面的代码块需要一个名为 dataGridDataGrid 才能编译。对上述代码最明智的做法是将其打包到 Ctrl-V 键的处理器或 ApplicationCommands.Paste 的处理器中。我在这里不展示该代码,因为可以在网上其他地方轻松找到。
第一步是解析剪贴板并创建一个我们要复制到网格中的二维字符串数组 (string[][] clipboardData)。
接下来,我们需要当前选定行的第一个索引(在屏幕上)。结果存储在 startRow 中以备后用。
然后使用函数 ContainerFromIndex 创建目标行 (DataGridRow[] rows)。使用索引函数是因为我们需要按照屏幕上的布局对数组进行排序。
现在对列进行相同的操作是必要的 (DataGridColumn[] columns)。这里重要部分是按 DisplayIndex 排序。通过这样做,隐藏的列将被删除(因为它们的索引为 -1)。调用 Take 过滤函数是因为我们需要的列数不多于剪贴板中的列数。
剩下的唯一事情是遍历所有单元格并将内容从已解析的剪贴板 (clipboardData) 复制到 DataGrid 单元格中。为我们执行此操作的函数是 OnPastingCellClipboardContent。关于该函数的好处是它使用普通的 WPF 绑定(包括类型转换器)将传入的字符串转换为模型所需的类型。

总结

上面的代码远未完成,并且绝对无法处理所有预期的场景。它更像是一个起点,展示了实现 WPF DataGrid 的粘贴功能的另一种方法。
虽然已经完成了这篇文章的写作,但我发现了 http://blogs.msdn.com/b/vinsibal/archive/2008/09/19/wpf-datagrid-clipboard-paste-sample.aspx[^],它使用相同的技术来处理列和复制,但处理行的方式不同。无论如何,我都会发布这篇文章,因为我认为它错误更少 ;) 并且有些人可能会觉得这种功能方法更具可读性。
© . All rights reserved.